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