1 /*
   2  * Copyright (c) 1997, 2014, 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 java.util.stream.Collectors;
  37 
  38 import sun.awt.AppContext;
  39 
  40 
  41 
  42 /**
  43  * Internal class to manage all Timers using one thread.
  44  * TimerQueue manages a queue of Timers. The Timers are chained
  45  * together in a linked list sorted by the order in which they will expire.
  46  *
  47  * @author Dave Moore
  48  * @author Igor Kushnirskiy
  49  */
  50 class TimerQueue implements Runnable
  51 {
  52     private static final Object sharedInstanceKey =
  53         new StringBuffer("TimerQueue.sharedInstanceKey");
  54     private static final Object expiredTimersKey =
  55         new StringBuffer("TimerQueue.expiredTimersKey");
  56 
  57     private final DelayQueue<DelayedTimer> queue;
  58     private volatile boolean running;
  59     private final Lock runningLock;
  60 
  61     /* Lock object used in place of class object for synchronization.
  62      * (4187686)
  63      */
  64     private static final Object classLock = new Object();
  65 
  66     /** Base of nanosecond timings, to avoid wrapping */
  67     private static final long NANO_ORIGIN = System.nanoTime();
  68 
  69     /**
  70      * Constructor for TimerQueue.
  71      */
  72     public TimerQueue() {
  73         super();
  74         queue = new DelayQueue<DelayedTimer>();
  75         // Now start the TimerQueue thread.
  76         runningLock = new ReentrantLock();
  77         startIfNeeded();
  78     }
  79 
  80 
  81     public static TimerQueue sharedInstance() {
  82         synchronized (classLock) {
  83             TimerQueue sharedInst = (TimerQueue)
  84                                     SwingUtilities.appContextGet(
  85                                                         sharedInstanceKey);
  86             if (sharedInst == null) {
  87                 sharedInst = new TimerQueue();
  88                 SwingUtilities.appContextPut(sharedInstanceKey, sharedInst);
  89             }
  90             return sharedInst;
  91         }
  92     }
  93 
  94 
  95     void startIfNeeded() {
  96         if (! running) {
  97             runningLock.lock();
  98             try {
  99                 final ThreadGroup threadGroup =
 100                     AppContext.getAppContext().getThreadGroup();
 101                 java.security.AccessController.doPrivileged(
 102                     new java.security.PrivilegedAction<Object>() {
 103                     public Object run() {
 104                         Thread timerThread = new Thread(threadGroup, TimerQueue.this,
 105                                                         "TimerQueue");
 106                         timerThread.setDaemon(true);
 107                         timerThread.setPriority(Thread.NORM_PRIORITY);
 108                         timerThread.start();
 109                         return null;
 110                     }
 111                 });
 112                 running = true;
 113             } finally {
 114                 runningLock.unlock();
 115             }
 116         }
 117     }
 118 
 119     void addTimer(Timer timer, long delayMillis) {
 120         timer.getLock().lock();
 121         try {
 122             // If the Timer is already in the queue, then ignore the add.
 123             if (! containsTimer(timer)) {
 124                 addTimer(new DelayedTimer(timer,
 125                                       TimeUnit.MILLISECONDS.toNanos(delayMillis)
 126                                       + now()));
 127             }
 128         } finally {
 129             timer.getLock().unlock();
 130         }
 131     }
 132 
 133     private void addTimer(DelayedTimer delayedTimer) {
 134         assert delayedTimer != null && ! containsTimer(delayedTimer.getTimer());
 135 
 136         Timer timer = delayedTimer.getTimer();
 137         timer.getLock().lock();
 138         try {
 139             timer.delayedTimer = delayedTimer;
 140             queue.add(delayedTimer);
 141         } finally {
 142             timer.getLock().unlock();
 143         }
 144     }
 145 
 146     void removeTimer(Timer timer) {
 147         timer.getLock().lock();
 148         try {
 149             if (timer.delayedTimer != null) {
 150                 queue.remove(timer.delayedTimer);
 151                 timer.delayedTimer = null;
 152             }
 153         } finally {
 154             timer.getLock().unlock();
 155         }
 156     }
 157 
 158     boolean containsTimer(Timer timer) {
 159         timer.getLock().lock();
 160         try {
 161             return timer.delayedTimer != null;
 162         } finally {
 163             timer.getLock().unlock();
 164         }
 165     }
 166 
 167 
 168     public void run() {
 169         runningLock.lock();
 170         try {
 171             while (running) {
 172                 try {
 173                     Timer timer = queue.take().getTimer();
 174                     timer.getLock().lock();
 175                     try {
 176                         DelayedTimer delayedTimer = timer.delayedTimer;
 177                         if (delayedTimer != null) {
 178                             /*
 179                              * Timer is not removed after we get it from
 180                              * the queue and before the lock on the timer is
 181                              * acquired
 182                              */
 183                             timer.post(); // have timer post an event
 184                             timer.delayedTimer = null;
 185                             if (timer.isRepeats()) {
 186                                 delayedTimer.setTime(now()
 187                                     + TimeUnit.MILLISECONDS.toNanos(
 188                                           timer.getDelay()));
 189                                 addTimer(delayedTimer);
 190                             }
 191                         }
 192 
 193                         // Allow run other threads on systems without kernel threads
 194                         timer.getLock().newCondition().awaitNanos(1);
 195                     } catch (SecurityException ignore) {
 196                     } finally {
 197                         timer.getLock().unlock();
 198                     }
 199                 } catch (InterruptedException ie) {
 200                     // Shouldn't ignore InterruptedExceptions here, so AppContext
 201                     // is disposed gracefully, see 6799345 for details
 202                     if (AppContext.getAppContext().isDisposed()) {
 203                         break;
 204                     }
 205                 }
 206             }
 207         }
 208         catch (ThreadDeath td) {
 209             // Mark all the timers we contain as not being queued.
 210             for (DelayedTimer delayedTimer : queue) {
 211                 delayedTimer.getTimer().cancelEvent();
 212             }
 213             throw td;
 214         } finally {
 215             running = false;
 216             runningLock.unlock();
 217         }
 218     }
 219 
 220 
 221     public String toString() {
 222         return queue.stream()
 223                 .map(delayedTimer -> delayedTimer.getTimer().toString())
 224                 .collect(Collectors.joining(", ", "TimerQueue (", ")"));
 225     }
 226 
 227     /**
 228      * Returns nanosecond time offset by origin
 229      */
 230     private 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 }