Home » openjdk-7 » java » awt » [javadoc | source]

    1   /*
    2    * Copyright (c) 2010, 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   package java.awt;
   27   
   28   import java.util.Timer;
   29   import java.util.TimerTask;
   30   import java.util.concurrent.atomic.AtomicBoolean;
   31   
   32   import java.security.PrivilegedAction;
   33   import java.security.AccessController;
   34   
   35   import sun.awt.PeerEvent;
   36   
   37   import sun.util.logging.PlatformLogger;
   38   
   39   /**
   40    * This utility class is used to suspend execution on a thread
   41    * while still allowing {@code EventDispatchThread} to dispatch events.
   42    * The API methods of the class are thread-safe.
   43    *
   44    * @author Anton Tarasov, Artem Ananiev
   45    *
   46    * @since 1.7
   47    */
   48   class WaitDispatchSupport implements SecondaryLoop {
   49   
   50       private final static PlatformLogger log =
   51           PlatformLogger.getLogger("java.awt.event.WaitDispatchSupport");
   52   
   53       private EventDispatchThread dispatchThread;
   54       private EventFilter filter;
   55   
   56       private volatile Conditional extCondition;
   57       private volatile Conditional condition;
   58   
   59       private long interval;
   60       // Use a shared daemon timer to serve all the WaitDispatchSupports
   61       private static Timer timer;
   62       // When this WDS expires, we cancel the timer task leaving the
   63       // shared timer up and running
   64       private TimerTask timerTask;
   65   
   66       private AtomicBoolean keepBlockingEDT = new AtomicBoolean(false);
   67       private AtomicBoolean keepBlockingCT = new AtomicBoolean(false);
   68   
   69       private static synchronized void initializeTimer() {
   70           if (timer == null) {
   71               timer = new Timer("AWT-WaitDispatchSupport-Timer", true);
   72           }
   73       }
   74   
   75       /**
   76        * Creates a {@code WaitDispatchSupport} instance to
   77        * serve the given event dispatch thread.
   78        *
   79        * @param dispatchThread An event dispatch thread that
   80        *        should not stop dispatching events while waiting
   81        *
   82        * @since 1.7
   83        */
   84       public WaitDispatchSupport(EventDispatchThread dispatchThread) {
   85           this(dispatchThread, null);
   86       }
   87   
   88       /**
   89        * Creates a {@code WaitDispatchSupport} instance to
   90        * serve the given event dispatch thread.
   91        *
   92        * @param dispatchThread An event dispatch thread that
   93        *        should not stop dispatching events while waiting
   94        * @param extCondition A conditional object used to determine
   95        *        if the loop should be terminated
   96        *
   97        * @since 1.7
   98        */
   99       public WaitDispatchSupport(EventDispatchThread dispatchThread,
  100                                  Conditional extCond)
  101       {
  102           if (dispatchThread == null) {
  103               throw new IllegalArgumentException("The dispatchThread can not be null");
  104           }
  105   
  106           this.dispatchThread = dispatchThread;
  107           this.extCondition = extCond;
  108           this.condition = new Conditional() {
  109               @Override
  110               public boolean evaluate() {
  111                   if (log.isLoggable(PlatformLogger.FINEST)) {
  112                       log.finest("evaluate(): blockingEDT=" + keepBlockingEDT.get() +
  113                                  ", blockingCT=" + keepBlockingCT.get());
  114                   }
  115                   boolean extEvaluate =
  116                       (extCondition != null) ? extCondition.evaluate() : true;
  117                   if (!keepBlockingEDT.get() || !extEvaluate) {
  118                       if (timerTask != null) {
  119                           timerTask.cancel();
  120                           timerTask = null;
  121                       }
  122                       return false;
  123                   }
  124                   return true;
  125               }
  126           };
  127       }
  128   
  129       /**
  130        * Creates a {@code WaitDispatchSupport} instance to
  131        * serve the given event dispatch thread.
  132        * <p>
  133        * The {@link EventFilter} is set on the {@code dispatchThread}
  134        * while waiting. The filter is removed on completion of the
  135        * waiting process.
  136        * <p>
  137        *
  138        *
  139        * @param dispatchThread An event dispatch thread that
  140        *        should not stop dispatching events while waiting
  141        * @param filter {@code EventFilter} to be set
  142        * @param interval A time interval to wait for. Note that
  143        *        when the waiting process takes place on EDT
  144        *        there is no guarantee to stop it in the given time
  145        *
  146        * @since 1.7
  147        */
  148       public WaitDispatchSupport(EventDispatchThread dispatchThread,
  149                                  Conditional extCondition,
  150                                  EventFilter filter, long interval)
  151       {
  152           this(dispatchThread, extCondition);
  153           this.filter = filter;
  154           if (interval < 0) {
  155               throw new IllegalArgumentException("The interval value must be >= 0");
  156           }
  157           this.interval = interval;
  158           if (interval != 0) {
  159               initializeTimer();
  160           }
  161       }
  162   
  163       /**
  164        * @inheritDoc
  165        */
  166       @Override
  167       public boolean enter() {
  168           log.fine("enter(): blockingEDT=" + keepBlockingEDT.get() +
  169                    ", blockingCT=" + keepBlockingCT.get());
  170   
  171           if (!keepBlockingEDT.compareAndSet(false, true)) {
  172               log.fine("The secondary loop is already running, aborting");
  173               return false;
  174           }
  175   
  176           final Runnable run = new Runnable() {
  177               public void run() {
  178                   log.fine("Starting a new event pump");
  179                   if (filter == null) {
  180                       dispatchThread.pumpEvents(condition);
  181                   } else {
  182                       dispatchThread.pumpEventsForFilter(condition, filter);
  183                   }
  184               }
  185           };
  186   
  187           // We have two mechanisms for blocking: if we're on the
  188           // dispatch thread, start a new event pump; if we're
  189           // on any other thread, call wait() on the treelock
  190   
  191           Thread currentThread = Thread.currentThread();
  192           if (currentThread == dispatchThread) {
  193               log.finest("On dispatch thread: " + dispatchThread);
  194               if (interval != 0) {
  195                   log.finest("scheduling the timer for " + interval + " ms");
  196                   timer.schedule(timerTask = new TimerTask() {
  197                       @Override
  198                       public void run() {
  199                           if (keepBlockingEDT.compareAndSet(true, false)) {
  200                               wakeupEDT();
  201                           }
  202                       }
  203                   }, interval);
  204               }
  205               // Dispose SequencedEvent we are dispatching on the the current
  206               // AppContext, to prevent us from hang - see 4531693 for details
  207               SequencedEvent currentSE = KeyboardFocusManager.
  208                   getCurrentKeyboardFocusManager().getCurrentSequencedEvent();
  209               if (currentSE != null) {
  210                   log.fine("Dispose current SequencedEvent: " + currentSE);
  211                   currentSE.dispose();
  212               }
  213               // In case the exit() method is called before starting
  214               // new event pump it will post the waking event to EDT.
  215               // The event will be handled after the the new event pump
  216               // starts. Thus, the enter() method will not hang.
  217               //
  218               // Event pump should be privileged. See 6300270.
  219               AccessController.doPrivileged(new PrivilegedAction() {
  220                   public Object run() {
  221                       run.run();
  222                       return null;
  223                   }
  224               });
  225           } else {
  226               log.finest("On non-dispatch thread: " + currentThread);
  227               synchronized (getTreeLock()) {
  228                   if (filter != null) {
  229                       dispatchThread.addEventFilter(filter);
  230                   }
  231                   try {
  232                       EventQueue eq = dispatchThread.getEventQueue();
  233                       eq.postEvent(new PeerEvent(this, run, PeerEvent.PRIORITY_EVENT));
  234                       keepBlockingCT.set(true);
  235                       if (interval > 0) {
  236                           long currTime = System.currentTimeMillis();
  237                           while (keepBlockingCT.get() &&
  238                                  ((extCondition != null) ? extCondition.evaluate() : true) &&
  239                                  (currTime + interval > System.currentTimeMillis()))
  240                           {
  241                               getTreeLock().wait(interval);
  242                           }
  243                       } else {
  244                           while (keepBlockingCT.get() &&
  245                                  ((extCondition != null) ? extCondition.evaluate() : true))
  246                           {
  247                               getTreeLock().wait();
  248                           }
  249                       }
  250                       log.fine("waitDone " + keepBlockingEDT.get() + " " + keepBlockingCT.get());
  251                   } catch (InterruptedException e) {
  252                       log.fine("Exception caught while waiting: " + e);
  253                   } finally {
  254                       if (filter != null) {
  255                           dispatchThread.removeEventFilter(filter);
  256                       }
  257                   }
  258                   // If the waiting process has been stopped because of the
  259                   // time interval passed or an exception occurred, the state
  260                   // should be changed
  261                   keepBlockingEDT.set(false);
  262                   keepBlockingCT.set(false);
  263               }
  264           }
  265   
  266           return true;
  267       }
  268   
  269       /**
  270        * @inheritDoc
  271        */
  272       public boolean exit() {
  273           log.fine("exit(): blockingEDT=" + keepBlockingEDT.get() +
  274                    ", blockingCT=" + keepBlockingCT.get());
  275           if (keepBlockingEDT.compareAndSet(true, false)) {
  276               wakeupEDT();
  277               return true;
  278           }
  279           return false;
  280       }
  281   
  282       private final static Object getTreeLock() {
  283           return Component.LOCK;
  284       }
  285   
  286       private final Runnable wakingRunnable = new Runnable() {
  287           public void run() {
  288               log.fine("Wake up EDT");
  289               synchronized (getTreeLock()) {
  290                   keepBlockingCT.set(false);
  291                   getTreeLock().notifyAll();
  292               }
  293               log.fine("Wake up EDT done");
  294           }
  295       };
  296   
  297       private void wakeupEDT() {
  298           log.finest("wakeupEDT(): EDT == " + dispatchThread);
  299           EventQueue eq = dispatchThread.getEventQueue();
  300           eq.postEvent(new PeerEvent(this, wakingRunnable, PeerEvent.PRIORITY_EVENT));
  301       }
  302   }

Home » openjdk-7 » java » awt » [javadoc | source]