blob: 30d668088824fe8749fa1e1517b063a98bb5fedb [file] [log] [blame]
/*
* 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;
}
}