136 * event queue(s) is not enough: we should lock the whole stack. |
136 * event queue(s) is not enough: we should lock the whole stack. |
137 */ |
137 */ |
138 private final Lock pushPopLock; |
138 private final Lock pushPopLock; |
139 private final Condition pushPopCond; |
139 private final Condition pushPopCond; |
140 |
140 |
|
141 /* |
|
142 * Dummy runnable to wake up EDT from getNextEvent() after |
|
143 push/pop is performed |
|
144 */ |
|
145 private final static Runnable dummyRunnable = new Runnable() { |
|
146 public void run() { |
|
147 } |
|
148 }; |
|
149 |
141 private EventDispatchThread dispatchThread; |
150 private EventDispatchThread dispatchThread; |
142 |
151 |
143 private final ThreadGroup threadGroup = |
152 private final ThreadGroup threadGroup = |
144 Thread.currentThread().getThreadGroup(); |
153 Thread.currentThread().getThreadGroup(); |
145 private final ClassLoader classLoader = |
154 private final ClassLoader classLoader = |
217 * <code>coalesceEvents</code> method will be called. |
226 * <code>coalesceEvents</code> method will be called. |
218 * |
227 * |
219 * @param theEvent an instance of <code>java.awt.AWTEvent</code>, |
228 * @param theEvent an instance of <code>java.awt.AWTEvent</code>, |
220 * or a subclass of it |
229 * or a subclass of it |
221 */ |
230 */ |
222 final void postEventPrivate(AWTEvent theEvent) { |
231 private final void postEventPrivate(AWTEvent theEvent) { |
223 theEvent.isPosted = true; |
232 theEvent.isPosted = true; |
224 pushPopLock.lock(); |
233 pushPopLock.lock(); |
225 try { |
234 try { |
226 if (dispatchThread == null && nextQueue == null) { |
235 if (nextQueue != null) { |
|
236 // Forward the event to the top of EventQueue stack |
|
237 nextQueue.postEventPrivate(theEvent); |
|
238 return; |
|
239 } |
|
240 if (dispatchThread == null) { |
227 if (theEvent.getSource() == AWTAutoShutdown.getInstance()) { |
241 if (theEvent.getSource() == AWTAutoShutdown.getInstance()) { |
228 return; |
242 return; |
229 } else { |
243 } else { |
230 initDispatchThread(); |
244 initDispatchThread(); |
231 } |
245 } |
232 } |
246 } |
233 if (nextQueue != null) { |
|
234 // Forward event to top of EventQueue stack. |
|
235 nextQueue.postEventPrivate(theEvent); |
|
236 return; |
|
237 } |
|
238 postEvent(theEvent, getPriority(theEvent)); |
247 postEvent(theEvent, getPriority(theEvent)); |
239 } finally { |
248 } finally { |
240 pushPopLock.unlock(); |
249 pushPopLock.unlock(); |
241 } |
250 } |
242 } |
251 } |
243 |
252 |
244 private static int getPriority(AWTEvent theEvent) { |
253 private static int getPriority(AWTEvent theEvent) { |
245 if (theEvent instanceof PeerEvent && |
254 if (theEvent instanceof PeerEvent) { |
246 (((PeerEvent)theEvent).getFlags() & |
255 PeerEvent peerEvent = (PeerEvent)theEvent; |
247 PeerEvent.ULTIMATE_PRIORITY_EVENT) != 0) |
256 if ((peerEvent.getFlags() & PeerEvent.ULTIMATE_PRIORITY_EVENT) != 0) { |
248 { |
257 return ULTIMATE_PRIORITY; |
249 return ULTIMATE_PRIORITY; |
258 } |
250 } |
259 if ((peerEvent.getFlags() & PeerEvent.PRIORITY_EVENT) != 0) { |
251 |
260 return HIGH_PRIORITY; |
252 if (theEvent instanceof PeerEvent && |
261 } |
253 (((PeerEvent)theEvent).getFlags() & |
262 if ((peerEvent.getFlags() & PeerEvent.LOW_PRIORITY_EVENT) != 0) { |
254 PeerEvent.PRIORITY_EVENT) != 0) |
263 return LOW_PRIORITY; |
255 { |
264 } |
256 return HIGH_PRIORITY; |
265 } |
257 } |
|
258 |
|
259 if (theEvent instanceof PeerEvent && |
|
260 (((PeerEvent)theEvent).getFlags() & |
|
261 PeerEvent.LOW_PRIORITY_EVENT) != 0) |
|
262 { |
|
263 return LOW_PRIORITY; |
|
264 } |
|
265 |
|
266 int id = theEvent.getID(); |
266 int id = theEvent.getID(); |
267 if (id == PaintEvent.PAINT || id == PaintEvent.UPDATE) { |
267 if ((id >= PaintEvent.PAINT_FIRST) && (id <= PaintEvent.PAINT_LAST)) { |
268 return LOW_PRIORITY; |
268 return LOW_PRIORITY; |
269 } |
269 } |
270 return NORM_PRIORITY; |
270 return NORM_PRIORITY; |
271 } |
271 } |
272 |
272 |
499 * event queues are nested with push()/pop(). |
499 * event queues are nested with push()/pop(). |
500 */ |
500 */ |
501 SunToolkit.flushPendingEvents(); |
501 SunToolkit.flushPendingEvents(); |
502 pushPopLock.lock(); |
502 pushPopLock.lock(); |
503 try { |
503 try { |
504 for (int i = NUM_PRIORITIES - 1; i >= 0; i--) { |
504 AWTEvent event = getNextEventPrivate(); |
505 if (queues[i].head != null) { |
505 if (event != null) { |
506 EventQueueItem entry = queues[i].head; |
506 return event; |
507 queues[i].head = entry.next; |
|
508 if (entry.next == null) { |
|
509 queues[i].tail = null; |
|
510 } |
|
511 uncacheEQItem(entry); |
|
512 return entry.event; |
|
513 } |
|
514 } |
507 } |
515 AWTAutoShutdown.getInstance().notifyThreadFree(dispatchThread); |
508 AWTAutoShutdown.getInstance().notifyThreadFree(dispatchThread); |
516 pushPopCond.await(); |
509 pushPopCond.await(); |
517 } finally { |
510 } finally { |
518 pushPopLock.unlock(); |
511 pushPopLock.unlock(); |
519 } |
512 } |
520 } while(true); |
513 } while(true); |
|
514 } |
|
515 |
|
516 /* |
|
517 * Must be called under the lock. Doesn't call flushPendingEvents() |
|
518 */ |
|
519 AWTEvent getNextEventPrivate() throws InterruptedException { |
|
520 for (int i = NUM_PRIORITIES - 1; i >= 0; i--) { |
|
521 if (queues[i].head != null) { |
|
522 EventQueueItem entry = queues[i].head; |
|
523 queues[i].head = entry.next; |
|
524 if (entry.next == null) { |
|
525 queues[i].tail = null; |
|
526 } |
|
527 uncacheEQItem(entry); |
|
528 return entry.event; |
|
529 } |
|
530 } |
|
531 return null; |
521 } |
532 } |
522 |
533 |
523 AWTEvent getNextEvent(int id) throws InterruptedException { |
534 AWTEvent getNextEvent(int id) throws InterruptedException { |
524 do { |
535 do { |
525 /* |
536 /* |
759 eventLog.fine("EventQueue.push(" + newEventQueue + ")"); |
772 eventLog.fine("EventQueue.push(" + newEventQueue + ")"); |
760 } |
773 } |
761 |
774 |
762 pushPopLock.lock(); |
775 pushPopLock.lock(); |
763 try { |
776 try { |
764 EventQueue toPush = this; |
777 EventQueue topQueue = this; |
765 while (toPush.nextQueue != null) { |
778 while (topQueue.nextQueue != null) { |
766 toPush = toPush.nextQueue; |
779 topQueue = topQueue.nextQueue; |
|
780 } |
|
781 |
|
782 if ((topQueue.dispatchThread != null) && |
|
783 (topQueue.dispatchThread.getEventQueue() == this)) |
|
784 { |
|
785 newEventQueue.dispatchThread = topQueue.dispatchThread; |
|
786 topQueue.dispatchThread.setEventQueue(newEventQueue); |
767 } |
787 } |
768 |
788 |
769 // Transfer all events forward to new EventQueue. |
789 // Transfer all events forward to new EventQueue. |
770 while (toPush.peekEvent() != null) { |
790 while (topQueue.peekEvent() != null) { |
771 try { |
791 try { |
772 newEventQueue.postEventPrivate(toPush.getNextEvent()); |
792 // Use getNextEventPrivate() as it doesn't call flushPendingEvents() |
|
793 newEventQueue.postEventPrivate(topQueue.getNextEventPrivate()); |
773 } catch (InterruptedException ie) { |
794 } catch (InterruptedException ie) { |
774 if (eventLog.isLoggable(PlatformLogger.FINE)) { |
795 if (eventLog.isLoggable(PlatformLogger.FINE)) { |
775 eventLog.fine("Interrupted push", ie); |
796 eventLog.fine("Interrupted push", ie); |
776 } |
797 } |
777 } |
798 } |
778 } |
799 } |
779 |
800 |
780 newEventQueue.previousQueue = toPush; |
801 // Wake up EDT waiting in getNextEvent(), so it can |
781 |
802 // pick up a new EventQueue. Post the waking event before |
782 /* |
803 // topQueue.nextQueue is assigned, otherwise the event would |
783 * Stop the event dispatch thread associated with the currently |
804 // go newEventQueue |
784 * active event queue, so that after the new queue is pushed |
805 topQueue.postEventPrivate(new InvocationEvent(topQueue, dummyRunnable)); |
785 * on the top this event dispatch thread won't prevent AWT from |
806 |
786 * being automatically shut down. |
807 newEventQueue.previousQueue = topQueue; |
787 * Use stopDispatchingLater() to avoid deadlock: stopDispatching() |
808 topQueue.nextQueue = newEventQueue; |
788 * waits for the dispatch thread to exit, which in turn waits |
|
789 * for the lock in EQ.detachDispatchThread(), which is hold by |
|
790 * this method. |
|
791 */ |
|
792 if (toPush.dispatchThread != null) { |
|
793 toPush.dispatchThread.stopDispatchingLater(); |
|
794 } |
|
795 |
|
796 toPush.nextQueue = newEventQueue; |
|
797 |
809 |
798 AppContext appContext = AppContext.getAppContext(); |
810 AppContext appContext = AppContext.getAppContext(); |
799 if (appContext.get(AppContext.EVENT_QUEUE_KEY) == toPush) { |
811 if (appContext.get(AppContext.EVENT_QUEUE_KEY) == topQueue) { |
800 appContext.put(AppContext.EVENT_QUEUE_KEY, newEventQueue); |
812 appContext.put(AppContext.EVENT_QUEUE_KEY, newEventQueue); |
801 } |
813 } |
|
814 |
|
815 pushPopCond.signalAll(); |
802 } finally { |
816 } finally { |
803 pushPopLock.unlock(); |
817 pushPopLock.unlock(); |
804 } |
818 } |
805 } |
819 } |
806 |
820 |
820 protected void pop() throws EmptyStackException { |
834 protected void pop() throws EmptyStackException { |
821 if (eventLog.isLoggable(PlatformLogger.FINE)) { |
835 if (eventLog.isLoggable(PlatformLogger.FINE)) { |
822 eventLog.fine("EventQueue.pop(" + this + ")"); |
836 eventLog.fine("EventQueue.pop(" + this + ")"); |
823 } |
837 } |
824 |
838 |
825 EventDispatchThread dt = null; |
839 pushPopLock.lock(); |
826 pushPopLock.lock(); |
840 try { |
827 try { |
841 EventQueue topQueue = this; |
828 EventQueue toPop = this; |
842 while (topQueue.nextQueue != null) { |
829 while (toPop.nextQueue != null) { |
843 topQueue = topQueue.nextQueue; |
830 toPop = toPop.nextQueue; |
844 } |
831 } |
845 EventQueue prevQueue = topQueue.previousQueue; |
832 EventQueue prev = toPop.previousQueue; |
846 if (prevQueue == null) { |
833 if (prev == null) { |
|
834 throw new EmptyStackException(); |
847 throw new EmptyStackException(); |
835 } |
848 } |
836 toPop.previousQueue = null; |
849 |
|
850 topQueue.previousQueue = null; |
|
851 prevQueue.nextQueue = null; |
837 |
852 |
838 // Transfer all events back to previous EventQueue. |
853 // Transfer all events back to previous EventQueue. |
839 prev.nextQueue = null; |
854 while (topQueue.peekEvent() != null) { |
840 while (toPop.peekEvent() != null) { |
|
841 try { |
855 try { |
842 prev.postEventPrivate(toPop.getNextEvent()); |
856 prevQueue.postEventPrivate(topQueue.getNextEventPrivate()); |
843 } catch (InterruptedException ie) { |
857 } catch (InterruptedException ie) { |
844 if (eventLog.isLoggable(PlatformLogger.FINE)) { |
858 if (eventLog.isLoggable(PlatformLogger.FINE)) { |
845 eventLog.fine("Interrupted pop", ie); |
859 eventLog.fine("Interrupted pop", ie); |
846 } |
860 } |
847 } |
861 } |
848 } |
862 } |
|
863 |
|
864 if ((topQueue.dispatchThread != null) && |
|
865 (topQueue.dispatchThread.getEventQueue() == this)) |
|
866 { |
|
867 prevQueue.dispatchThread = topQueue.dispatchThread; |
|
868 topQueue.dispatchThread.setEventQueue(prevQueue); |
|
869 } |
|
870 |
849 AppContext appContext = AppContext.getAppContext(); |
871 AppContext appContext = AppContext.getAppContext(); |
850 if (appContext.get(AppContext.EVENT_QUEUE_KEY) == this) { |
872 if (appContext.get(AppContext.EVENT_QUEUE_KEY) == this) { |
851 appContext.put(AppContext.EVENT_QUEUE_KEY, prev); |
873 appContext.put(AppContext.EVENT_QUEUE_KEY, prevQueue); |
852 } |
874 } |
853 |
875 |
854 dt = toPop.dispatchThread; |
876 // Wake up EDT waiting in getNextEvent(), so it can |
855 } finally { |
877 // pick up a new EventQueue |
856 pushPopLock.unlock(); |
878 topQueue.postEventPrivate(new InvocationEvent(topQueue, dummyRunnable)); |
857 } |
879 |
858 |
880 pushPopCond.signalAll(); |
859 if (dt != null) { |
881 } finally { |
860 dt.stopDispatching(); // Must be done outside synchronized |
882 pushPopLock.unlock(); |
861 // block to avoid possible deadlock |
|
862 } |
883 } |
863 } |
884 } |
864 |
885 |
865 /** |
886 /** |
866 * Returns true if the calling thread is |
887 * Returns true if the calling thread is |
905 final void initDispatchThread() { |
926 final void initDispatchThread() { |
906 pushPopLock.lock(); |
927 pushPopLock.lock(); |
907 try { |
928 try { |
908 AppContext appContext = AppContext.getAppContext(); |
929 AppContext appContext = AppContext.getAppContext(); |
909 if (dispatchThread == null && !threadGroup.isDestroyed() && !appContext.isDisposed()) { |
930 if (dispatchThread == null && !threadGroup.isDestroyed() && !appContext.isDisposed()) { |
910 dispatchThread = (EventDispatchThread) |
931 dispatchThread = AccessController.doPrivileged( |
911 AccessController.doPrivileged(new PrivilegedAction() { |
932 new PrivilegedAction<EventDispatchThread>() { |
912 public Object run() { |
933 public EventDispatchThread run() { |
913 EventDispatchThread t = |
934 EventDispatchThread t = |
914 new EventDispatchThread(threadGroup, |
935 new EventDispatchThread(threadGroup, |
915 name, |
936 name, |
916 EventQueue.this); |
937 EventQueue.this); |
917 t.setContextClassLoader(classLoader); |
938 t.setContextClassLoader(classLoader); |
918 t.setPriority(Thread.NORM_PRIORITY + 1); |
939 t.setPriority(Thread.NORM_PRIORITY + 1); |
919 t.setDaemon(false); |
940 t.setDaemon(false); |
920 return t; |
941 return t; |
921 } |
942 } |
922 }); |
943 } |
|
944 ); |
923 AWTAutoShutdown.getInstance().notifyThreadBusy(dispatchThread); |
945 AWTAutoShutdown.getInstance().notifyThreadBusy(dispatchThread); |
924 dispatchThread.start(); |
946 dispatchThread.start(); |
925 } |
947 } |
926 } finally { |
948 } finally { |
927 pushPopLock.unlock(); |
949 pushPopLock.unlock(); |
928 } |
950 } |
929 } |
951 } |
930 |
952 |
931 final void detachDispatchThread(EventDispatchThread edt, boolean restart) { |
953 final boolean detachDispatchThread(EventDispatchThread edt) { |
932 /* |
954 /* |
933 * This synchronized block is to secure that the event dispatch |
955 * This synchronized block is to secure that the event dispatch |
934 * thread won't die in the middle of posting a new event to the |
956 * thread won't die in the middle of posting a new event to the |
935 * associated event queue. It is important because we notify |
957 * associated event queue. It is important because we notify |
936 * that the event dispatch thread is busy after posting a new event |
958 * that the event dispatch thread is busy after posting a new event |
937 * to its queue, so the EventQueue.dispatchThread reference must |
959 * to its queue, so the EventQueue.dispatchThread reference must |
938 * be valid at that point. |
960 * be valid at that point. |
939 */ |
961 */ |
940 pushPopLock.lock(); |
962 pushPopLock.lock(); |
941 try { |
963 try { |
942 EventDispatchThread oldDispatchThread = dispatchThread; |
964 if (edt == dispatchThread) { |
943 if (dispatchThread == edt) { |
|
944 dispatchThread = null; |
|
945 } |
|
946 if (restart) { |
|
947 /* |
965 /* |
948 * Event dispatch thread dies in case of an uncaught exception. |
966 * Don't detach the thread if any events are pending. Not |
949 * A new event dispatch thread for this queue will be started |
967 * sure if it's a possible scenario, though. |
950 * only if a new event is posted to it. In case if no more |
|
951 * events are posted after this thread died all events that |
|
952 * currently are in the queue will never be dispatched. |
|
953 * |
968 * |
954 * Fix for 4648733. Check both the associated java event |
969 * Fix for 4648733. Check both the associated java event |
955 * queue and the PostEventQueue. |
970 * queue and the PostEventQueue. |
956 */ |
971 */ |
957 if ((peekEvent() != null) || !SunToolkit.isPostEventQueueEmpty()) { |
972 if ((peekEvent() != null) || !SunToolkit.isPostEventQueueEmpty()) { |
958 initDispatchThread(); |
973 return false; |
959 } |
974 } |
960 AWTAutoShutdown.getInstance().notifyThreadFree(oldDispatchThread); |
975 dispatchThread = null; |
961 } |
976 } |
|
977 AWTAutoShutdown.getInstance().notifyThreadFree(edt); |
|
978 return true; |
962 } finally { |
979 } finally { |
963 pushPopLock.unlock(); |
980 pushPopLock.unlock(); |
964 } |
981 } |
965 } |
982 } |
966 |
983 |