| /* |
| * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| |
| |
| package javax.swing; |
| |
| |
| |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| import java.util.*; |
| import java.util.concurrent.*; |
| import java.util.concurrent.locks.*; |
| import java.util.concurrent.atomic.AtomicLong; |
| import sun.awt.AppContext; |
| |
| /** |
| * Internal class to manage all Timers using one thread. |
| * TimerQueue manages a queue of Timers. The Timers are chained |
| * together in a linked list sorted by the order in which they will expire. |
| * |
| * @author Dave Moore |
| * @author Igor Kushnirskiy |
| */ |
| class TimerQueue implements Runnable |
| { |
| private static final Object sharedInstanceKey = |
| new StringBuffer("TimerQueue.sharedInstanceKey"); |
| private static final Object expiredTimersKey = |
| new StringBuffer("TimerQueue.expiredTimersKey"); |
| |
| private final DelayQueue<DelayedTimer> queue; |
| private volatile boolean running; |
| private final Lock runningLock; |
| |
| /* Lock object used in place of class object for synchronization. |
| * (4187686) |
| */ |
| private static final Object classLock = new Object(); |
| |
| /** Base of nanosecond timings, to avoid wrapping */ |
| private static final long NANO_ORIGIN = System.nanoTime(); |
| |
| /** |
| * Constructor for TimerQueue. |
| */ |
| public TimerQueue() { |
| super(); |
| queue = new DelayQueue<DelayedTimer>(); |
| // Now start the TimerQueue thread. |
| runningLock = new ReentrantLock(); |
| startIfNeeded(); |
| } |
| |
| |
| public static TimerQueue sharedInstance() { |
| synchronized (classLock) { |
| TimerQueue sharedInst = (TimerQueue) |
| SwingUtilities.appContextGet( |
| sharedInstanceKey); |
| if (sharedInst == null) { |
| sharedInst = new TimerQueue(); |
| SwingUtilities.appContextPut(sharedInstanceKey, sharedInst); |
| } |
| return sharedInst; |
| } |
| } |
| |
| |
| void startIfNeeded() { |
| if (! running) { |
| runningLock.lock(); |
| if (running) { |
| return; |
| } |
| try { |
| final ThreadGroup threadGroup = AppContext.getAppContext().getThreadGroup(); |
| AccessController.doPrivileged((PrivilegedAction<Object>) () -> { |
| String name = "TimerQueue"; |
| Thread timerThread = |
| new Thread(threadGroup, this, name, 0, false); |
| timerThread.setDaemon(true); |
| timerThread.setPriority(Thread.NORM_PRIORITY); |
| timerThread.start(); |
| return null; |
| }); |
| running = true; |
| } finally { |
| runningLock.unlock(); |
| } |
| } |
| } |
| |
| void addTimer(Timer timer, long delayMillis) { |
| timer.getLock().lock(); |
| try { |
| // If the Timer is already in the queue, then ignore the add. |
| if (! containsTimer(timer)) { |
| addTimer(new DelayedTimer(timer, |
| TimeUnit.MILLISECONDS.toNanos(delayMillis) |
| + now())); |
| } |
| } finally { |
| timer.getLock().unlock(); |
| } |
| } |
| |
| private void addTimer(DelayedTimer delayedTimer) { |
| assert delayedTimer != null && ! containsTimer(delayedTimer.getTimer()); |
| |
| Timer timer = delayedTimer.getTimer(); |
| timer.getLock().lock(); |
| try { |
| timer.delayedTimer = delayedTimer; |
| queue.add(delayedTimer); |
| } finally { |
| timer.getLock().unlock(); |
| } |
| } |
| |
| void removeTimer(Timer timer) { |
| timer.getLock().lock(); |
| try { |
| if (timer.delayedTimer != null) { |
| queue.remove(timer.delayedTimer); |
| timer.delayedTimer = null; |
| } |
| } finally { |
| timer.getLock().unlock(); |
| } |
| } |
| |
| boolean containsTimer(Timer timer) { |
| timer.getLock().lock(); |
| try { |
| return timer.delayedTimer != null; |
| } finally { |
| timer.getLock().unlock(); |
| } |
| } |
| |
| |
| public void run() { |
| runningLock.lock(); |
| try { |
| while (running) { |
| try { |
| DelayedTimer runningTimer = queue.take(); |
| Timer timer = runningTimer.getTimer(); |
| timer.getLock().lock(); |
| try { |
| DelayedTimer delayedTimer = timer.delayedTimer; |
| if (delayedTimer == runningTimer) { |
| /* |
| * Timer is not removed (delayedTimer != null) |
| * or not removed and added (runningTimer == delayedTimer) |
| * after we get it from the queue and before the |
| * lock on the timer is acquired |
| */ |
| timer.post(); // have timer post an event |
| timer.delayedTimer = null; |
| if (timer.isRepeats()) { |
| delayedTimer.setTime(now() |
| + TimeUnit.MILLISECONDS.toNanos( |
| timer.getDelay())); |
| addTimer(delayedTimer); |
| } |
| } |
| |
| // Allow run other threads on systems without kernel threads |
| timer.getLock().newCondition().awaitNanos(1); |
| } catch (SecurityException ignore) { |
| } finally { |
| timer.getLock().unlock(); |
| } |
| } catch (InterruptedException ie) { |
| // Shouldn't ignore InterruptedExceptions here, so AppContext |
| // is disposed gracefully, see 6799345 for details |
| if (AppContext.getAppContext().isDisposed()) { |
| break; |
| } |
| } |
| } |
| } |
| catch (ThreadDeath td) { |
| // Mark all the timers we contain as not being queued. |
| for (DelayedTimer delayedTimer : queue) { |
| delayedTimer.getTimer().cancelEvent(); |
| } |
| throw td; |
| } finally { |
| running = false; |
| runningLock.unlock(); |
| } |
| } |
| |
| |
| public String toString() { |
| StringBuilder buf = new StringBuilder(); |
| buf.append("TimerQueue ("); |
| boolean isFirst = true; |
| for (DelayedTimer delayedTimer : queue) { |
| if (! isFirst) { |
| buf.append(", "); |
| } |
| buf.append(delayedTimer.getTimer().toString()); |
| isFirst = false; |
| } |
| buf.append(")"); |
| return buf.toString(); |
| } |
| |
| /** |
| * Returns nanosecond time offset by origin |
| */ |
| private static long now() { |
| return System.nanoTime() - NANO_ORIGIN; |
| } |
| |
| static class DelayedTimer implements Delayed { |
| // most of it copied from |
| // java.util.concurrent.ScheduledThreadPoolExecutor |
| |
| /** |
| * Sequence number to break scheduling ties, and in turn to |
| * guarantee FIFO order among tied entries. |
| */ |
| private static final AtomicLong sequencer = new AtomicLong(0); |
| |
| /** Sequence number to break ties FIFO */ |
| private final long sequenceNumber; |
| |
| |
| /** The time the task is enabled to execute in nanoTime units */ |
| private volatile long time; |
| |
| private final Timer timer; |
| |
| DelayedTimer(Timer timer, long nanos) { |
| this.timer = timer; |
| time = nanos; |
| sequenceNumber = sequencer.getAndIncrement(); |
| } |
| |
| |
| public final long getDelay(TimeUnit unit) { |
| return unit.convert(time - now(), TimeUnit.NANOSECONDS); |
| } |
| |
| final void setTime(long nanos) { |
| time = nanos; |
| } |
| |
| final Timer getTimer() { |
| return timer; |
| } |
| |
| public int compareTo(Delayed other) { |
| if (other == this) { // compare zero ONLY if same object |
| return 0; |
| } |
| if (other instanceof DelayedTimer) { |
| DelayedTimer x = (DelayedTimer)other; |
| long diff = time - x.time; |
| if (diff < 0) { |
| return -1; |
| } else if (diff > 0) { |
| return 1; |
| } else if (sequenceNumber < x.sequenceNumber) { |
| return -1; |
| } else { |
| return 1; |
| } |
| } |
| long d = (getDelay(TimeUnit.NANOSECONDS) - |
| other.getDelay(TimeUnit.NANOSECONDS)); |
| return (d == 0) ? 0 : ((d < 0) ? -1 : 1); |
| } |
| } |
| } |