| /* |
| * 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; |
| } |
| } |