1 /*
   2  * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 
  27 
  28 package javax.swing;
  29 
  30 
  31 
  32 import java.util.*;
  33 import java.util.concurrent.*;
  34 import java.util.concurrent.locks.*;
  35 import java.util.concurrent.atomic.AtomicLong;
  36 import sun.awt.AppContext;
  37 
  38 
  39 
  40 /**
  41  * Internal class to manage all Timers using one thread.
  42  * TimerQueue manages a queue of Timers. The Timers are chained
  43  * together in a linked list sorted by the order in which they will expire.
  44  *
  45  * @author Dave Moore
  46  * @author Igor Kushnirskiy
  47  */
  48 class TimerQueue implements Runnable
  49 {
  50     private static final Object sharedInstanceKey =
  51         new StringBuffer("TimerQueue.sharedInstanceKey");
  52     private static final Object expiredTimersKey =
  53         new StringBuffer("TimerQueue.expiredTimersKey");
  54 
  55     private final DelayQueue<DelayedTimer> queue;
  56     private volatile boolean running;
  57     private final Lock runningLock;
  58 
  59     /* Lock object used in place of class object for synchronization.
  60      * (4187686)
  61      */
  62     private static final Object classLock = new Object();
  63 
  64     /** Base of nanosecond timings, to avoid wrapping */
  65     private static final long NANO_ORIGIN = System.nanoTime();
  66 
  67     /**
  68      * Constructor for TimerQueue.
  69      */
  70     public TimerQueue() {
  71         super();
  72         queue = new DelayQueue<DelayedTimer>();
  73         // Now start the TimerQueue thread.
  74         runningLock = new ReentrantLock();
  75         startIfNeeded();
  76     }
  77 
  78 
  79     public static TimerQueue sharedInstance() {
  80         synchronized (classLock) {
  81             TimerQueue sharedInst = (TimerQueue)
  82                                     SwingUtilities.appContextGet(
  83                                                         sharedInstanceKey);
  84             if (sharedInst == null) {
  85                 sharedInst = new TimerQueue();
  86                 SwingUtilities.appContextPut(sharedInstanceKey, sharedInst);
  87             }
  88             return sharedInst;
  89         }
  90     }
  91 
  92 
  93     void startIfNeeded() {
  94         if (! running) {
  95             runningLock.lock();
  96             try {
  97                 final ThreadGroup threadGroup =
  98                     AppContext.getAppContext().getThreadGroup();
  99                 java.security.AccessController.doPrivileged(
 100                     new java.security.PrivilegedAction<Object>() {
 101                     public Object run() {
 102                         Thread timerThread = new Thread(threadGroup, TimerQueue.this,
 103                                                         "TimerQueue");
 104                         timerThread.setDaemon(true);
 105                         timerThread.setPriority(Thread.NORM_PRIORITY);
 106                         timerThread.start();
 107                         return null;
 108                     }
 109                 });
 110                 running = true;
 111             } finally {
 112                 runningLock.unlock();
 113             }
 114         }
 115     }
 116 
 117     void addTimer(Timer timer, long delayMillis) {
 118         timer.getLock().lock();
 119         try {
 120             // If the Timer is already in the queue, then ignore the add.
 121             if (! containsTimer(timer)) {
 122                 addTimer(new DelayedTimer(timer,
 123                                       TimeUnit.MILLISECONDS.toNanos(delayMillis)
 124                                       + now()));
 125             }
 126         } finally {
 127             timer.getLock().unlock();
 128         }
 129     }
 130 
 131     private void addTimer(DelayedTimer delayedTimer) {
 132         assert delayedTimer != null && ! containsTimer(delayedTimer.getTimer());
 133 
 134         Timer timer = delayedTimer.getTimer();
 135         timer.getLock().lock();
 136         try {
 137             timer.delayedTimer = delayedTimer;
 138             queue.add(delayedTimer);
 139         } finally {
 140             timer.getLock().unlock();
 141         }
 142     }
 143 
 144     void removeTimer(Timer timer) {
 145         timer.getLock().lock();
 146         try {
 147             if (timer.delayedTimer != null) {
 148                 queue.remove(timer.delayedTimer);
 149                 timer.delayedTimer = null;
 150             }
 151         } finally {
 152             timer.getLock().unlock();
 153         }
 154     }
 155 
 156     boolean containsTimer(Timer timer) {
 157         timer.getLock().lock();
 158         try {
 159             return timer.delayedTimer != null;
 160         } finally {
 161             timer.getLock().unlock();
 162         }
 163     }
 164 
 165 
 166     public void run() {
 167         runningLock.lock();
 168         try {
 169             while (running) {
 170                 try {
 171                     Timer timer = queue.take().getTimer();
 172                     timer.getLock().lock();
 173                     try {
 174                         DelayedTimer delayedTimer = timer.delayedTimer;
 175                         if (delayedTimer != null) {
 176                             /*
 177                              * Timer is not removed after we get it from
 178                              * the queue and before the lock on the timer is
 179                              * acquired
 180                              */
 181                             timer.post(); // have timer post an event
 182                             timer.delayedTimer = null;
 183                             if (timer.isRepeats()) {
 184                                 delayedTimer.setTime(now()
 185                                     + TimeUnit.MILLISECONDS.toNanos(
 186                                           timer.getDelay()));
 187                                 addTimer(delayedTimer);
 188                             }
 189                         }
 190 
 191                         // Allow run other threads on systems without kernel threads
 192                         timer.getLock().newCondition().awaitNanos(1);
 193                     } catch (SecurityException ignore) {
 194                     } finally {
 195                         timer.getLock().unlock();
 196                     }
 197                 } catch (InterruptedException ie) {
 198                     // Shouldn't ignore InterruptedExceptions here, so AppContext
 199                     // is disposed gracefully, see 6799345 for details
 200                     if (AppContext.getAppContext().isDisposed()) {
 201                         break;
 202                     }
 203                 }
 204             }
 205         }
 206         catch (ThreadDeath td) {
 207             // Mark all the timers we contain as not being queued.
 208             for (DelayedTimer delayedTimer : queue) {
 209                 delayedTimer.getTimer().cancelEvent();
 210             }
 211             throw td;
 212         } finally {
 213             running = false;
 214             runningLock.unlock();
 215         }
 216     }
 217 
 218 
 219     public String toString() {
 220         StringBuilder buf = new StringBuilder();
 221         buf.append("TimerQueue (");
 222         boolean isFirst = true;
 223         for (DelayedTimer delayedTimer : queue) {
 224             if (! isFirst) {
 225                 buf.append(", ");
 226             }
 227             buf.append(delayedTimer.getTimer().toString());
 228             isFirst = false;
 229         }
 230         buf.append(")");
 231         return buf.toString();
 232     }
 233 
 234     /**
 235      * Returns nanosecond time offset by origin
 236      */
 237     private static long now() {
 238         return System.nanoTime() - NANO_ORIGIN;
 239     }
 240 
 241     static class DelayedTimer implements Delayed {
 242         // most of it copied from
 243         // java.util.concurrent.ScheduledThreadPoolExecutor
 244 
 245         /**
 246          * Sequence number to break scheduling ties, and in turn to
 247          * guarantee FIFO order among tied entries.
 248          */
 249         private static final AtomicLong sequencer = new AtomicLong(0);
 250 
 251         /** Sequence number to break ties FIFO */
 252         private final long sequenceNumber;
 253 
 254 
 255         /** The time the task is enabled to execute in nanoTime units */
 256         private volatile long time;
 257 
 258         private final Timer timer;
 259 
 260         DelayedTimer(Timer timer, long nanos) {
 261             this.timer = timer;
 262             time = nanos;
 263             sequenceNumber = sequencer.getAndIncrement();
 264         }
 265 
 266 
 267         final public long getDelay(TimeUnit unit) {
 268             return  unit.convert(time - now(), TimeUnit.NANOSECONDS);
 269         }
 270 
 271         final void setTime(long nanos) {
 272             time = nanos;
 273         }
 274 
 275         final Timer getTimer() {
 276             return timer;
 277         }
 278 
 279         public int compareTo(Delayed other) {
 280             if (other == this) { // compare zero ONLY if same object
 281                 return 0;
 282             }
 283             if (other instanceof DelayedTimer) {
 284                 DelayedTimer x = (DelayedTimer)other;
 285                 long diff = time - x.time;
 286                 if (diff < 0) {
 287                     return -1;
 288                 } else if (diff > 0) {
 289                     return 1;
 290                 } else if (sequenceNumber < x.sequenceNumber) {
 291                     return -1;
 292                 }  else {
 293                     return 1;
 294                 }
 295             }
 296             long d = (getDelay(TimeUnit.NANOSECONDS) -
 297                       other.getDelay(TimeUnit.NANOSECONDS));
 298             return (d == 0) ? 0 : ((d < 0) ? -1 : 1);
 299         }
 300     }
 301 }