1 /*
   2  * Copyright (c) 1997, 2009, 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.concurrent.*;
  33 import java.util.concurrent.locks.*;
  34 import java.util.concurrent.atomic.AtomicLong;
  35 import sun.awt.AppContext;
  36 
  37 
  38 
  39 /**
  40  * Internal class to manage all Timers using one thread.
  41  * TimerQueue manages a queue of Timers. The Timers are chained
  42  * together in a linked list sorted by the order in which they will expire.
  43  *
  44  * @author Dave Moore
  45  * @author Igor Kushnirskiy
  46  */
  47 class TimerQueue implements Runnable
  48 {
  49     private static final Object sharedInstanceKey = new Object(); // TimerQueue.sharedInstanceKey
  50 
  51     private final DelayQueue<DelayedTimer> queue;
  52     private volatile boolean running;
  53     private final Lock runningLock;
  54 
  55     /* Lock object used in place of class object for synchronization.
  56      * (4187686)
  57      */
  58     private static final Object classLock = new Object();
  59 
  60     /** Base of nanosecond timings, to avoid wrapping */
  61     private static final long NANO_ORIGIN = System.nanoTime();
  62 
  63     /**
  64      * Constructor for TimerQueue.
  65      */
  66     public TimerQueue() {
  67         super();
  68         queue = new DelayQueue<DelayedTimer>();
  69         // Now start the TimerQueue thread.
  70         runningLock = new ReentrantLock();
  71         startIfNeeded();
  72     }
  73 
  74 
  75     public static TimerQueue sharedInstance() {
  76         synchronized (classLock) {
  77             TimerQueue sharedInst = (TimerQueue)
  78                                     SwingUtilities.appContextGet(
  79                                                         sharedInstanceKey);
  80             if (sharedInst == null) {
  81                 sharedInst = new TimerQueue();
  82                 SwingUtilities.appContextPut(sharedInstanceKey, sharedInst);
  83             }
  84             return sharedInst;
  85         }
  86     }
  87 
  88 
  89     void startIfNeeded() {
  90         if (! running) {
  91             runningLock.lock();
  92             try {
  93                 final ThreadGroup threadGroup =
  94                     AppContext.getAppContext().getThreadGroup();
  95                 java.security.AccessController.doPrivileged(
  96                     new java.security.PrivilegedAction() {
  97                     public Object run() {
  98                         Thread timerThread = new Thread(threadGroup, TimerQueue.this,
  99                                                         "TimerQueue");
 100                         timerThread.setDaemon(true);
 101                         timerThread.setPriority(Thread.NORM_PRIORITY);
 102                         timerThread.start();
 103                         return null;
 104                     }
 105                 });
 106                 running = true;
 107             } finally {
 108                 runningLock.unlock();
 109             }
 110         }
 111     }
 112 
 113     void addTimer(Timer timer, long delayMillis) {
 114         timer.getLock().lock();
 115         try {
 116             // If the Timer is already in the queue, then ignore the add.
 117             if (! containsTimer(timer)) {
 118                 addTimer(new DelayedTimer(timer,
 119                                       TimeUnit.MILLISECONDS.toNanos(delayMillis)
 120                                       + now()));
 121             }
 122         } finally {
 123             timer.getLock().unlock();
 124         }
 125     }
 126 
 127     private void addTimer(DelayedTimer delayedTimer) {
 128         assert delayedTimer != null && ! containsTimer(delayedTimer.getTimer());
 129 
 130         Timer timer = delayedTimer.getTimer();
 131         timer.getLock().lock();
 132         try {
 133             timer.delayedTimer = delayedTimer;
 134             queue.add(delayedTimer);
 135         } finally {
 136             timer.getLock().unlock();
 137         }
 138     }
 139 
 140     void removeTimer(Timer timer) {
 141         timer.getLock().lock();
 142         try {
 143             if (timer.delayedTimer != null) {
 144                 queue.remove(timer.delayedTimer);
 145                 timer.delayedTimer = null;
 146             }
 147         } finally {
 148             timer.getLock().unlock();
 149         }
 150     }
 151 
 152     boolean containsTimer(Timer timer) {
 153         timer.getLock().lock();
 154         try {
 155             return timer.delayedTimer != null;
 156         } finally {
 157             timer.getLock().unlock();
 158         }
 159     }
 160 
 161 
 162     public void run() {
 163         runningLock.lock();
 164         try {
 165             while (running) {
 166                 try {
 167                     Timer timer = queue.take().getTimer();
 168                     timer.getLock().lock();
 169                     try {
 170                         DelayedTimer delayedTimer = timer.delayedTimer;
 171                         if (delayedTimer != null) {
 172                             /*
 173                              * Timer is not removed after we get it from
 174                              * the queue and before the lock on the timer is
 175                              * acquired
 176                              */
 177                             timer.post(); // have timer post an event
 178                             timer.delayedTimer = null;
 179                             if (timer.isRepeats()) {
 180                                 delayedTimer.setTime(now()
 181                                     + TimeUnit.MILLISECONDS.toNanos(
 182                                           timer.getDelay()));
 183                                 addTimer(delayedTimer);
 184                             }
 185                         }
 186                     } catch (SecurityException ignore) {
 187                     } finally {
 188                         timer.getLock().unlock();
 189                     }
 190                 } catch (InterruptedException ie) {
 191                     // Shouldn't ignore InterruptedExceptions here, so AppContext
 192                     // is disposed gracefully, see 6799345 for details
 193                     if (AppContext.getAppContext().isDisposed()) {
 194                         break;
 195                     }
 196                 }
 197             }
 198         }
 199         catch (ThreadDeath td) {
 200             // Mark all the timers we contain as not being queued.
 201             for (DelayedTimer delayedTimer : queue) {
 202                 delayedTimer.getTimer().cancelEvent();
 203             }
 204             throw td;
 205         } finally {
 206             running = false;
 207             runningLock.unlock();
 208         }
 209     }
 210 
 211 
 212     public String toString() {
 213         StringBuilder buf = new StringBuilder();
 214         buf.append("TimerQueue (");
 215         boolean isFirst = true;
 216         for (DelayedTimer delayedTimer : queue) {
 217             if (! isFirst) {
 218                 buf.append(", ");
 219             }
 220             buf.append(delayedTimer.getTimer().toString());
 221             isFirst = false;
 222         }
 223         buf.append(")");
 224         return buf.toString();
 225     }
 226 
 227     /**
 228      * Returns nanosecond time offset by origin
 229      */
 230     private final static long now() {
 231         return System.nanoTime() - NANO_ORIGIN;
 232     }
 233 
 234     static class DelayedTimer implements Delayed {
 235         // most of it copied from
 236         // java.util.concurrent.ScheduledThreadPoolExecutor
 237 
 238         /**
 239          * Sequence number to break scheduling ties, and in turn to
 240          * guarantee FIFO order among tied entries.
 241          */
 242         private static final AtomicLong sequencer = new AtomicLong(0);
 243 
 244         /** Sequence number to break ties FIFO */
 245         private final long sequenceNumber;
 246 
 247 
 248         /** The time the task is enabled to execute in nanoTime units */
 249         private volatile long time;
 250 
 251         private final Timer timer;
 252 
 253         DelayedTimer(Timer timer, long nanos) {
 254             this.timer = timer;
 255             time = nanos;
 256             sequenceNumber = sequencer.getAndIncrement();
 257         }
 258 
 259 
 260         final public long getDelay(TimeUnit unit) {
 261             return  unit.convert(time - now(), TimeUnit.NANOSECONDS);
 262         }
 263 
 264         final void setTime(long nanos) {
 265             time = nanos;
 266         }
 267 
 268         final Timer getTimer() {
 269             return timer;
 270         }
 271 
 272         public int compareTo(Delayed other) {
 273             if (other == this) { // compare zero ONLY if same object
 274                 return 0;
 275             }
 276             if (other instanceof DelayedTimer) {
 277                 DelayedTimer x = (DelayedTimer)other;
 278                 long diff = time - x.time;
 279                 if (diff < 0) {
 280                     return -1;
 281                 } else if (diff > 0) {
 282                     return 1;
 283                 } else if (sequenceNumber < x.sequenceNumber) {
 284                     return -1;
 285                 }  else {
 286                     return 1;
 287                 }
 288             }
 289             long d = (getDelay(TimeUnit.NANOSECONDS) -
 290                       other.getDelay(TimeUnit.NANOSECONDS));
 291             return (d == 0) ? 0 : ((d < 0) ? -1 : 1);
 292         }
 293     }
 294 }