< prev index next >

src/java.desktop/share/classes/java/awt/SequencedEvent.java

Print this page
rev 52472 : 8204142: AWT hang occurs when sequenced events arrive out of sequence in multiple AppContexts
Summary: Improvements on the synchronization of SequencedEvent events from different AppContexts
Reviewed-by: serb


  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 {


 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(ID, () -> !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);


 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 }


  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.Iterator;
  31 import java.util.LinkedList;
  32 import sun.awt.AWTAccessor;
  33 import sun.awt.AppContext;
  34 import sun.awt.SunToolkit;
  35 
  36 /**
  37  * A mechanism for ensuring that a series of AWTEvents are executed in a
  38  * precise order, even across multiple AppContexts. The nested events will be
  39  * dispatched in the order in which their wrapping SequencedEvents were
  40  * constructed. The only exception to this rule is if the peer of the target of
  41  * the nested event was destroyed (with a call to Component.removeNotify)
  42  * before the wrapping SequencedEvent was able to be dispatched. In this case,
  43  * the nested event is never dispatched.
  44  *
  45  * @author David Mendenhall
  46  */
  47 class SequencedEvent extends AWTEvent implements ActiveEvent {
  48     /*
  49      * serialVersionUID
  50      */
  51     private static final long serialVersionUID = 547742659238625067L;
  52 
  53     private static final int ID =
  54         java.awt.event.FocusEvent.FOCUS_LAST + 1;
  55     private static final LinkedList<SequencedEvent> list = new LinkedList<>();
  56 
  57     private final AWTEvent nested;
  58     private AppContext appContext;
  59     private boolean disposed;
  60     private final LinkedList<AWTEvent> pendingEvents = new LinkedList<>();
  61 
  62     private static boolean fxAppThreadIsDispatchThread;
  63     private Thread fxCheckSequenceThread;
  64     static {
  65         AWTAccessor.setSequencedEventAccessor(new AWTAccessor.SequencedEventAccessor() {
  66             public AWTEvent getNested(AWTEvent sequencedEvent) {
  67                 return ((SequencedEvent)sequencedEvent).nested;
  68             }
  69             public boolean isSequencedEvent(AWTEvent event) {
  70                 return event instanceof SequencedEvent;
  71             }
  72 
  73             public AWTEvent create(AWTEvent event) {
  74                 return new SequencedEvent(event);
  75             }
  76         });
  77         AccessController.doPrivileged(new PrivilegedAction<Object>() {
  78             public Object run() {
  79                 fxAppThreadIsDispatchThread =
  80                         "true".equals(System.getProperty("javafx.embed.singleThread"));
  81                 return null;
  82             }
  83         });
  84     }
  85 
  86     private static final class SequencedEventsFilter implements EventFilter {
  87         private final SequencedEvent currentSequencedEvent;
  88         private SequencedEventsFilter(SequencedEvent currentSequencedEvent) {
  89             this.currentSequencedEvent = currentSequencedEvent;
  90         }
  91         @Override
  92         public FilterAction acceptEvent(AWTEvent ev) {
  93             if (ev.getID() == ID) {
  94                 // Move forward dispatching only if the event is previous
  95                 // in SequencedEvent.list. Otherwise, hold it for reposting later.
  96                 synchronized (SequencedEvent.class) {
  97                     Iterator<SequencedEvent> it = list.iterator();
  98                     while (it.hasNext()) {
  99                         SequencedEvent iev = it.next();
 100                         if (iev.equals(currentSequencedEvent)) {
 101                             break;
 102                         } else if (iev.equals(ev)) {
 103                             return FilterAction.ACCEPT;
 104                         }
 105                     }
 106                 }
 107             } else if (ev.getID() == SentEvent.ID) {
 108                 return FilterAction.ACCEPT;
 109             }
 110             currentSequencedEvent.pendingEvents.add(ev);
 111             return FilterAction.REJECT;
 112         }
 113     }
 114 
 115     /**
 116      * Constructs a new SequencedEvent which will dispatch the specified
 117      * nested event.
 118      *
 119      * @param nested the AWTEvent which this SequencedEvent's dispatch()
 120      *        method will dispatch
 121      */
 122     public SequencedEvent(AWTEvent nested) {
 123         super(nested.getSource(), ID);
 124         this.nested = nested;
 125         // All AWTEvents that are wrapped in SequencedEvents are (at
 126         // least currently) implicitly generated by the system
 127         SunToolkit.setSystemGenerated(nested);
 128 
 129         if (fxAppThreadIsDispatchThread) {
 130             fxCheckSequenceThread = new Thread() {
 131                 @Override
 132                 public void run() {
 133                     while(!isFirstOrDisposed()) {
 134                         try {


 149      * Dispatches the nested event after all previous nested events have been
 150      * dispatched or disposed. If this method is invoked before all previous nested events
 151      * have been dispatched, then this method blocks until such a point is
 152      * reached.
 153      * While waiting disposes nested events to disposed AppContext
 154      *
 155      * NOTE: Locking protocol.  Since dispose() can get EventQueue lock,
 156      * dispatch() shall never call dispose() while holding the lock on the list,
 157      * as EventQueue lock is held during dispatching.  The locks should be acquired
 158      * in the same order.
 159      */
 160     public final void dispatch() {
 161         try {
 162             appContext = AppContext.getAppContext();
 163 
 164             if (getFirst() != this) {
 165                 if (EventQueue.isDispatchThread()) {
 166                     if (Thread.currentThread() instanceof EventDispatchThread) {
 167                         EventDispatchThread edt = (EventDispatchThread)
 168                                 Thread.currentThread();
 169                         edt.pumpEventsForFilter(() -> !SequencedEvent.this.isFirstOrDisposed(),
 170                                 new SequencedEventsFilter(this));
 171                     } else {
 172                         if (fxAppThreadIsDispatchThread) {
 173                             fxCheckSequenceThread.start();
 174                             try {
 175                                 // check if event is dispatched or disposed
 176                                 // but since this user app thread is same as
 177                                 // dispatch thread in fx when run with
 178                                 // javafx.embed.singleThread=true
 179                                 // we do not wait infinitely to avoid deadlock
 180                                 // as dispatch will ultimately be done by this thread
 181                                 fxCheckSequenceThread.join(500);
 182                             } catch (InterruptedException e) {
 183                             }
 184                         }
 185                     }
 186                 } else {
 187                     while(!isFirstOrDisposed()) {
 188                         synchronized (SequencedEvent.class) {
 189                             try {
 190                                 SequencedEvent.class.wait(1000);


 254      * has been dispatched and handled, or when the peer of the target of the
 255      * nested event has been disposed with a call to Component.removeNotify.
 256      *
 257      * NOTE: Locking protocol.  Since SunToolkit.postEvent can get EventQueue lock,
 258      * it shall never be called while holding the lock on the list,
 259      * as EventQueue lock is held during dispatching and dispatch() will get
 260      * lock on the list. The locks should be acquired in the same order.
 261      */
 262     final void dispose() {
 263       synchronized (SequencedEvent.class) {
 264             if (disposed) {
 265                 return;
 266             }
 267             if (KeyboardFocusManager.getCurrentKeyboardFocusManager().
 268                     getCurrentSequencedEvent() == this) {
 269                 KeyboardFocusManager.getCurrentKeyboardFocusManager().
 270                     setCurrentSequencedEvent(null);
 271             }
 272             disposed = true;
 273         }




 274 
 275         SequencedEvent next = null;
 276 
 277         synchronized (SequencedEvent.class) {
 278           SequencedEvent.class.notifyAll();
 279 
 280           if (list.getFirst() == this) {
 281               list.removeFirst();
 282 
 283               if (!list.isEmpty()) {
 284                     next = list.getFirst();
 285               }
 286           } else {
 287               list.remove(this);
 288           }
 289       }
 290         // Wake up waiting threads
 291         if (next != null && next.appContext != null) {
 292             SunToolkit.postEvent(next.appContext, new SentEvent());
 293         }
 294 
 295         for(AWTEvent e : pendingEvents) {
 296             SunToolkit.postEvent(appContext, e);
 297         }
 298     }
 299 }
< prev index next >