summaryrefslogtreecommitdiff
path: root/platform/core-api/src/com/intellij/concurrency/JobScheduler.java
blob: 30d668088824fe8749fa1e1517b063a98bb5fedb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/*
 * Copyright 2000-2014 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * @author max
 */
package com.intellij.concurrency;

import com.intellij.Patches;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.util.ConcurrencyUtil;
import com.intellij.util.ReflectionUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;


import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;

public abstract class JobScheduler {
  private static final ScheduledThreadPoolExecutor ourScheduledExecutorService;
  private static final int TASK_LIMIT = 50;
  private static final Logger LOG = Logger.getInstance("#com.intellij.concurrency.JobScheduler");
  private static final ThreadLocal<Long> START = new ThreadLocal<Long>();
  private static final boolean DO_TIMING = true;

  static {
    ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1, ConcurrencyUtil.newNamedThreadFactory("Periodic tasks thread", true, Thread.NORM_PRIORITY)) {
      @Override
      protected void beforeExecute(Thread t, Runnable r) {
        if (DO_TIMING) {
          START.set(System.currentTimeMillis());
        }
      }

      @Override
      protected void afterExecute(Runnable r, Throwable t) {
        if (DO_TIMING) {
          long elapsed = System.currentTimeMillis() - START.get();
          Object unwrapped;
          if (elapsed > TASK_LIMIT && (unwrapped = info(r)) != null) {
            @NonNls String msg = TASK_LIMIT + " ms execution limit failed for: " + unwrapped + "; elapsed time was " + elapsed +"ms";
            LOG.info(msg);
          }
        }
      }
    };
    executor.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
    executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
    enableRemoveOnCancelPolicy(executor);
    ourScheduledExecutorService = executor;
  }

  private static Object info(Runnable r) {
    if (!(r instanceof FutureTask)) return r;
    Object sync = ReflectionUtil.getField(FutureTask.class, r, null, "sync"); // FutureTask.sync in <=JDK7
    Object o = sync == null ? r : sync;
    Object callable = ReflectionUtil.getField(o.getClass(), o, Callable.class, "callable"); // FutureTask.callable or Sync.callable
    if (callable == null) return null;
    Object task = ReflectionUtil.getField(callable.getClass(), callable, null, "task"); // java.util.concurrent.Executors.RunnableAdapter.task
    return task == null ? callable : task;
  }

  private static void enableRemoveOnCancelPolicy(ScheduledThreadPoolExecutor executor) {
    if (Patches.USE_REFLECTION_TO_ACCESS_JDK7) {
      try {
        Method setRemoveOnCancelPolicy = ReflectionUtil.getDeclaredMethod(ScheduledThreadPoolExecutor.class, "setRemoveOnCancelPolicy", boolean.class);
        setRemoveOnCancelPolicy.invoke(executor, true);
      }
      catch (Exception ignored) {
      }
    }
  }

  public static JobScheduler getInstance() {
    return ServiceManager.getService(JobScheduler.class);
  }

  @NotNull
  public static ScheduledExecutorService getScheduler() {
    return ourScheduledExecutorService;
  }
}