--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/javax/swing/TimerQueue.java Sun Aug 17 15:54:13 2014 +0100
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 1997, 2012, 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.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();
+ try {
+ final ThreadGroup threadGroup =
+ AppContext.getAppContext().getThreadGroup();
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<Object>() {
+ public Object run() {
+ Thread timerThread = new Thread(threadGroup, TimerQueue.this,
+ "TimerQueue");
+ 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 {
+ Timer timer = queue.take().getTimer();
+ timer.getLock().lock();
+ try {
+ DelayedTimer delayedTimer = timer.delayedTimer;
+ if (delayedTimer != null) {
+ /*
+ * Timer is not removed 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();
+ }
+
+
+ final public 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);
+ }
+ }
+}