       1 /*
       2  * Copyright (c) 2000, 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 package java.awt;
      27
      28 import java.security.AccessController;
      29 import java.security.PrivilegedAction;
      30 import java.util.LinkedList;
      31 import sun.awt.AWTAccessor;
      32 import sun.awt.AppContext;
      33 import sun.awt.SunToolkit;
      34
      35 /**
      36  * A mechanism for ensuring that a series of AWTEvents are executed in a
      37  * precise order, even across multiple AppContexts. The nested events will be
      38  * dispatched in the order in which their wrapping SequencedEvents were
      39  * constructed. The only exception to this rule is if the peer of the target of
      40  * the nested event was destroyed (with a call to Component.removeNotify)
      41  * before the wrapping SequencedEvent was able to be dispatched. In this case,
      42  * the nested event is never dispatched.
      43  *
      44  * @author David Mendenhall
      45  */
      46 class SequencedEvent extends AWTEvent implements ActiveEvent {
      47     /*
      48      * serialVersionUID
      49      */
      50     private static final long serialVersionUID = 547742659238625067L;
      51
      52     private static final int ID =
      53         java.awt.event.FocusEvent.FOCUS_LAST + 1;
      54     private static final LinkedList<SequencedEvent> list = new LinkedList<>();
      55
      56     private final AWTEvent nested;
      57     private AppContext appContext;
      58     private boolean disposed;
      59
      60     private static boolean fxAppThreadIsDispatchThread;
      61     private Thread fxCheckSequenceThread;
      62     static {
      63         AWTAccessor.setSequencedEventAccessor(new AWTAccessor.SequencedEventAccessor() {
      64             public AWTEvent getNested(AWTEvent sequencedEvent) {
      65                 return ((SequencedEvent)sequencedEvent).nested;
      66             }
      67             public boolean isSequencedEvent(AWTEvent event) {
      68                 return event instanceof SequencedEvent;
      69             }
      70
      71             public AWTEvent create(AWTEvent event) {
      72                 return new SequencedEvent(event);
      73             }
      74         });
      75         AccessController.doPrivileged(new PrivilegedAction<Object>() {
      76             public Object run() {
      77                 fxAppThreadIsDispatchThread =
      78                         "true".equals(System.getProperty("javafx.embed.singleThread"));
      79                 return null;
      80             }
      81         });
      82     }
      83
      84     /**
      85      * Constructs a new SequencedEvent which will dispatch the specified
      86      * nested event.
      87      *
      88      * @param nested the AWTEvent which this SequencedEvent's dispatch()
      89      *        method will dispatch
      90      */
      91     public SequencedEvent(AWTEvent nested) {
      92         super(nested.getSource(), ID);
      93         this.nested = nested;
      94         // All AWTEvents that are wrapped in SequencedEvents are (at
      95         // least currently) implicitly generated by the system
      96         SunToolkit.setSystemGenerated(nested);
      97
      98         if (fxAppThreadIsDispatchThread) {
      99             fxCheckSequenceThread = new Thread() {
     100                 @Override
     101                 public void run() {
     102                     while(!isFirstOrDisposed()) {
     103                         try {
     104                             Thread.sleep(100);
     105                         } catch (InterruptedException e) {
     106                             break;
     107                         }
     108                     }
     109                 }
     110             };
     111         }
     112         synchronized (SequencedEvent.class) {
     113             list.add(this);
     114         }
     115     }
     116
     117     /**
     118      * Dispatches the nested event after all previous nested events have been
     119      * dispatched or disposed. If this method is invoked before all previous nested events
     120      * have been dispatched, then this method blocks until such a point is
     121      * reached.
     122      * While waiting disposes nested events to disposed AppContext
     123      *
     124      * NOTE: Locking protocol.  Since dispose() can get EventQueue lock,
     125      * dispatch() shall never call dispose() while holding the lock on the list,
     126      * as EventQueue lock is held during dispatching.  The locks should be acquired
     127      * in the same order.
     128      */
     129     public final void dispatch() {
     130         try {
     131             appContext = AppContext.getAppContext();
     132
     133             if (getFirst() != this) {
     134                 if (EventQueue.isDispatchThread()) {
     135                     if (Thread.currentThread() instanceof EventDispatchThread) {
     136                         EventDispatchThread edt = (EventDispatchThread)
     137                                 Thread.currentThread();
     138                         edt.pumpEvents(() -> !SequencedEvent.this.isFirstOrDisposed());

     139                     } else {
     140                         if (fxAppThreadIsDispatchThread) {
     141                             fxCheckSequenceThread.start();
     142                             try {
     143                                 // check if event is dispatched or disposed
     144                                 // but since this user app thread is same as
     145                                 // dispatch thread in fx when run with
     146                                 // javafx.embed.singleThread=true
     147                                 // we do not wait infinitely to avoid deadlock
     148                                 // as dispatch will ultimately be done by this thread
     149                                 fxCheckSequenceThread.join(500);
     150                             } catch (InterruptedException e) {
     151                             }
     152                         }
     153                     }
     154                 } else {
     155                     while(!isFirstOrDisposed()) {
     156                         synchronized (SequencedEvent.class) {
     157                             try {
     158                                 SequencedEvent.class.wait(1000);
     159                             } catch (InterruptedException e) {
     160                                 break;
     161                             }
     162                         }
     163                     }
     164                 }
     165             }
     166
     167             if (!disposed) {
     168                 KeyboardFocusManager.getCurrentKeyboardFocusManager().
     169                     setCurrentSequencedEvent(this);
     170                 Toolkit.getEventQueue().dispatchEvent(nested);
     171             }
     172         } finally {
     173             dispose();
     174         }
     175     }
     176
     177     /**
     178      * true only if event exists and nested source appContext is disposed.
     179      */
     180     private static final boolean isOwnerAppContextDisposed(SequencedEvent se) {
     181         if (se != null) {
     182             Object target = se.nested.getSource();
     183             if (target instanceof Component) {
     184                 return ((Component)target).appContext.isDisposed();
     185             }
     186         }
     187         return false;
     188     }
     189
     190     /**
     191      * Sequenced events are dispatched in order, so we cannot dispatch
     192      * until we are the first sequenced event in the queue (i.e. it's our
     193      * turn).  But while we wait for our turn to dispatch, the event
     194      * could have been disposed for a number of reasons.
     195      */
     196     public final boolean isFirstOrDisposed() {
     197         if (disposed) {
     198             return true;
     199         }
     200         // getFirstWithContext can dispose this
     201         return this == getFirstWithContext() || disposed;
     202     }
     203
     204     private static final synchronized SequencedEvent getFirst() {
     205         return list.getFirst();
     206     }
     207
     208     /* Disposes all events from disposed AppContext
     209      * return first valid event
     210      */
     211     private static final SequencedEvent getFirstWithContext() {
     212         SequencedEvent first = getFirst();
     213         while(isOwnerAppContextDisposed(first)) {
     214             first.dispose();
     215             first = getFirst();
     216         }
     217         return first;
     218     }
     219
     220     /**
     221      * Disposes of this instance. This method is invoked once the nested event
     222      * has been dispatched and handled, or when the peer of the target of the
     223      * nested event has been disposed with a call to Component.removeNotify.
     224      *
     225      * NOTE: Locking protocol.  Since SunToolkit.postEvent can get EventQueue lock,
     226      * it shall never be called while holding the lock on the list,
     227      * as EventQueue lock is held during dispatching and dispatch() will get
     228      * lock on the list. The locks should be acquired in the same order.
     229      */
     230     final void dispose() {
     231       synchronized (SequencedEvent.class) {
     232             if (disposed) {
     233                 return;
     234             }
     235             if (KeyboardFocusManager.getCurrentKeyboardFocusManager().
     236                     getCurrentSequencedEvent() == this) {
     237                 KeyboardFocusManager.getCurrentKeyboardFocusManager().
     238                     setCurrentSequencedEvent(null);
     239             }
     240             disposed = true;
     241         }
     242         // Wake myself up
     243         if (appContext != null) {
     244             SunToolkit.postEvent(appContext, new SentEvent());
     245         }
     246
     247         SequencedEvent next = null;
     248
     249         synchronized (SequencedEvent.class) {
     250           SequencedEvent.class.notifyAll();
     251
     252           if (list.getFirst() == this) {
     253               list.removeFirst();
     254
     255               if (!list.isEmpty()) {
     256                     next = list.getFirst();
     257               }
     258           } else {
     259               list.remove(this);
     260           }
     261       }
     262         // Wake up waiting threads
     263         if (next != null && next.appContext != null) {
     264             SunToolkit.postEvent(next.appContext, new SentEvent());
     265         }
     266     }
     267 }
    --- EOF ---