public Alarm(@NotNull ThreadToUse threadToUse, Disposable parentDisposable) { myThreadToUse = threadToUse; myExecutorService = threadToUse == ThreadToUse.OWN_THREAD ? ConcurrencyUtil.newSingleThreadExecutor("Alarm pool(own)", Thread.NORM_PRIORITY - 2) : ourSharedExecutorService; if (parentDisposable != null) { Disposer.register(parentDisposable, this); } }
public class Alarm implements Disposable { private static final Logger LOG = Logger.getInstance("#com.intellij.util.Alarm"); private volatile boolean myDisposed; private final List<Request> myRequests = new ArrayList<Request>(); private final List<Request> myPendingRequests = new ArrayList<Request>(); private final ExecutorService myExecutorService; private static final ThreadPoolExecutor ourSharedExecutorService = ConcurrencyUtil.newSingleThreadExecutor("Alarm pool(shared)", Thread.NORM_PRIORITY - 2); private final Object LOCK = new Object(); private final ThreadToUse myThreadToUse; private JComponent myActivationComponent; @Override public void dispose() { myDisposed = true; cancelAllRequests(); if (myThreadToUse == ThreadToUse.POOLED_THREAD) { myExecutorService.shutdown(); } else if (myThreadToUse == ThreadToUse.OWN_THREAD) { myExecutorService.shutdown(); ((ThreadPoolExecutor) myExecutorService).getQueue().clear(); } } public enum ThreadToUse { SWING_THREAD, SHARED_THREAD, POOLED_THREAD, OWN_THREAD } /** Creates alarm that works in Swing thread */ public Alarm() { this(ThreadToUse.SWING_THREAD); } public Alarm(Disposable parentDisposable) { this(ThreadToUse.SWING_THREAD, parentDisposable); } public Alarm(@NotNull ThreadToUse threadToUse) { this(threadToUse, null); LOG.assertTrue( threadToUse != ThreadToUse.POOLED_THREAD && threadToUse != ThreadToUse.OWN_THREAD, "You must provide parent Disposable for ThreadToUse.POOLED_THREAD and ThreadToUse.OWN_THREAD Alarm"); } public Alarm(@NotNull ThreadToUse threadToUse, Disposable parentDisposable) { myThreadToUse = threadToUse; if (threadToUse == ThreadToUse.POOLED_THREAD) { myExecutorService = new MyExecutor(); } else if (threadToUse == ThreadToUse.OWN_THREAD) { myExecutorService = ConcurrencyUtil.newSingleThreadExecutor("Alarm pool(own)", Thread.NORM_PRIORITY - 2); } else { myExecutorService = ourSharedExecutorService; } if (parentDisposable != null) { Disposer.register(parentDisposable, this); } } public void addRequest( @NotNull final Runnable request, final int delay, boolean runWithActiveFrameOnly) { if (runWithActiveFrameOnly && !ApplicationManager.getApplication().isActive()) { final MessageBus bus = ApplicationManager.getApplication().getMessageBus(); final MessageBusConnection connection = bus.connect(this); connection.subscribe( ApplicationActivationListener.TOPIC, new ApplicationActivationListener() { @Override public void applicationActivated(IdeFrame ideFrame) { connection.disconnect(); addRequest(request, delay); } @Override public void applicationDeactivated(IdeFrame ideFrame) {} }); } else { addRequest(request, delay); } } public void addRequest(@NotNull Runnable request, long delayMillis) { _addRequest( request, delayMillis, myThreadToUse == ThreadToUse.SWING_THREAD ? ModalityState.current() : null); } public void addRequest(@NotNull Runnable request, int delayMillis) { _addRequest( request, delayMillis, myThreadToUse == ThreadToUse.SWING_THREAD ? ModalityState.current() : null); } public void addComponentRequest(@NotNull Runnable request, int delay) { assert myActivationComponent != null; _addRequest(request, delay, ModalityState.stateForComponent(myActivationComponent)); } public void addComponentRequest(@NotNull Runnable request, long delayMillis) { assert myActivationComponent != null; _addRequest(request, delayMillis, ModalityState.stateForComponent(myActivationComponent)); } public void addRequest( @NotNull Runnable request, int delayMillis, @Nullable final ModalityState modalityState) { LOG.assertTrue(myThreadToUse == ThreadToUse.SWING_THREAD); _addRequest(request, delayMillis, modalityState); } public void addRequest( @NotNull Runnable request, long delayMillis, @Nullable final ModalityState modalityState) { LOG.assertTrue(myThreadToUse == ThreadToUse.SWING_THREAD); _addRequest(request, delayMillis, modalityState); } private void _addRequest( @NotNull Runnable request, long delayMillis, ModalityState modalityState) { synchronized (LOCK) { LOG.assertTrue(!myDisposed, "Already disposed"); final Request requestToSchedule = new Request(request, modalityState, delayMillis); if (myActivationComponent == null || myActivationComponent.isShowing()) { _add(requestToSchedule); } else { if (!myPendingRequests.contains(requestToSchedule)) { myPendingRequests.add(requestToSchedule); } } } } private void _add(@NotNull Request requestToSchedule) { final ScheduledFuture<?> future = JobScheduler.getScheduler() .schedule(requestToSchedule, requestToSchedule.myDelay, TimeUnit.MILLISECONDS); requestToSchedule.setFuture(future); myRequests.add(requestToSchedule); } private void flushPending() { for (Request each : myPendingRequests) { _add(each); } myPendingRequests.clear(); } public boolean cancelRequest(@NotNull Runnable request) { synchronized (LOCK) { cancelRequest(request, myRequests); cancelRequest(request, myPendingRequests); return true; } } private void cancelRequest(@NotNull Runnable request, @NotNull List<Request> list) { for (int i = list.size() - 1; i >= 0; i--) { Request r = list.get(i); if (r.getTask() == request) { r.cancel(); list.remove(i); } } } public int cancelAllRequests() { synchronized (LOCK) { int count = cancelAllRequests(myRequests); cancelAllRequests(myPendingRequests); return count; } } private int cancelAllRequests(@NotNull List<Request> list) { int count = 0; for (Request request : list) { count++; request.cancel(); } list.clear(); return count; } public int getActiveRequestCount() { synchronized (LOCK) { return myRequests.size(); } } protected boolean isEdt() { return isEventDispatchThread(); } public static boolean isEventDispatchThread() { final Application app = ApplicationManager.getApplication(); return app != null && app.isDispatchThread() || EventQueue.isDispatchThread(); } private class Request implements Runnable { private Runnable myTask; private final ModalityState myModalityState; private Future<?> myFuture; private final long myDelay; private Request( @NotNull Runnable task, @Nullable ModalityState modalityState, long delayMillis) { myTask = task; myModalityState = modalityState; myDelay = delayMillis; } @Override public void run() { try { if (myDisposed) { return; } synchronized (LOCK) { if (myTask == null) return; } final Runnable scheduledTask = new Runnable() { @Override public void run() { final Runnable task; synchronized (LOCK) { task = myTask; if (task == null) return; myTask = null; myRequests.remove(Request.this); } if (myThreadToUse == ThreadToUse.SWING_THREAD && !isEdt()) { try { SwingUtilities.invokeAndWait(task); } catch (Exception e) { LOG.error(e); } } else { try { task.run(); } catch (Exception e) { LOG.error(e); } } } }; if (myModalityState == null) { myFuture = myExecutorService.submit(scheduledTask); } else { final Application app = ApplicationManager.getApplication(); if (app != null) { app.invokeLater(scheduledTask, myModalityState); } else { SwingUtilities.invokeLater(scheduledTask); } } } catch (Throwable e) { LOG.error(e); } } private Runnable getTask() { return myTask; } public void setFuture(@NotNull ScheduledFuture<?> future) { myFuture = future; } public ModalityState getModalityState() { return myModalityState; } private void cancel() { synchronized (LOCK) { if (myFuture != null) { myFuture.cancel(false); } myTask = null; } } @Override public String toString() { Runnable task = myTask; return super.toString() + (task != null ? task.toString() : null); } } public Alarm setActivationComponent(@NotNull final JComponent component) { myActivationComponent = component; new UiNotifyConnector( component, new Activatable() { @Override public void showNotify() { flushPending(); } @Override public void hideNotify() {} }); return this; } public boolean isDisposed() { return myDisposed; } private class MyExecutor extends AbstractExecutorService { private final AtomicBoolean isShuttingDown = new AtomicBoolean(); private final QueueProcessor<Runnable> myProcessor = QueueProcessor.createRunnableQueueProcessor(); @Override public void shutdown() { myProcessor.clear(); isShuttingDown.set(myDisposed); } @Override public List<Runnable> shutdownNow() { throw new UnsupportedOperationException(); } @Override public boolean isShutdown() { return isShuttingDown.get(); } @Override public boolean isTerminated() { return isShutdown() && myProcessor.isEmpty(); } @Override public boolean awaitTermination(long timeout, @NotNull TimeUnit unit) throws InterruptedException { throw new UnsupportedOperationException(); } @Override public void execute(@NotNull Runnable command) { myProcessor.add(command); } } }