Project: SWT
Bug 51248 GUI thread does not process the GUI update requests timely, causing the display to be unresponsive
Reported: 2004-02-05 12:24:14 | Status: resolved fixed

The design of the main event dispatch cycle (Display.readAndDispatch()) provides for GUI update events (executed asynchronously or synchronously on a non-gui thread) to run within the same "time horizon" as GUI events. The mechanism for this synchronization involves posting a "wake message" to the windows event queue and subsequently dispatching it to an invisible special purpose window with a handle hwndEvent. However the mechanism is not functioning properly because of 2 implementation defects: 1) The isWakeMessage() function incorrectly returns false for wake messages generated in Windows 2000/XP/NT (it works correctly in Windows CE). 2) The readAndDispatch() function does not properly dispatch the wake messages, causing them to be unnecessarily lost, and therefore not invoking the hwndEvent's windows function. This function handles the wake messages by invoking runAsyncEvents(). However due to the mentioned defects this never actually happens. All of the above leads to the unhelpful behavior of the async messages executing only when the windows queue is entirely empty. Since this is at the mercy of the OS, it leads to unpredictability of GUI updates executed from non-GUI threads in a real-time async application, such as our bond trading application, where it can lead to stale price data being displayed in the market windows. Suggested Fix (patch to org.eclipse.swt.widgets.Display): -----BEGIN PATCH----- Index: Display.java =================================================================== RCS file: /appl/cvs/MAIN/Source/org/eclipse/swt/widgets/Attic/Display.java,v retrieving revision 1.1.2.3 diff -u -r1.1.2.3 Display.java --- Display.java 5 Feb 2004 17:07:40 -0000 1.1.2.3 +++ Display.java 5 Feb 2004 17:09:24 -0000 @@ -11,6 +11,9 @@ package org.eclipse.swt.widgets; +//CBID debug support import +import org.apache.log4j.Category; +//end CBID debug support import import org.eclipse.swt.internal.*; import org.eclipse.swt.internal.win32.*; import org.eclipse.swt.*; @@ -93,6 +96,24 @@ */ public class Display extends Device { + + + + // start display debugging support block - CBID + static Category s_log = Category.getInstance(Display.class); + static boolean s_logOn = false; + int wakeReceived = 0; + int totalReceived = 0; + + public static void setLog(boolean log) + { + s_logOn = log; + } + public static boolean isLogOn() + { + return s_logOn; + } + //end display debugging support block - CBID /** * the handle to the OS message queue @@ -1390,7 +1411,18 @@ } boolean isWakeMessage (MSG msg) { - return msg.hwnd == hwndMessage && msg.message == OS.WM_NULL; +//@@@@ CBID FIX #1 +// The wake message is generated with hwnd of 0 in Windows NT/2000/XP +// see documentation on PostThreadMessage() + +//------ ORIGINAL LINE ---- commented out +// return msg.hwnd == hwndMessage && msg.message == OS.WM_NULL; +//------ END ORIGINAL --- +// ---- REPLACEMENT - wake message is type 0, hwnd is +// ---- either the hwndMessage (Windows CE) or 0 (Windows 2000/NT/XP) + return (msg.hwnd == hwndMessage || msg.hwnd == 0) + && msg.message == OS.WM_NULL; +//---- END REPLACEMENT } boolean isValidThread () { @@ -1440,6 +1472,12 @@ if (wParam != 0) dispose (); break; case OS.WM_NULL: + //start CBID display support block + if(s_logOn) + { + s_log.error("Dispatched wake message in Window Function, will run asynch from proper place; still left to process "+synchronizer.messageCount); + } + //end CBID display support block runAsyncMessages (); break; case OS.WM_QUERYENDSESSION: @@ -1461,6 +1499,12 @@ if (code >= 0) { OS.MoveMemory (hookMsg, lParam, MSG.sizeof); if (hookMsg.message == OS.WM_NULL) runAsyncMessages (); + //start CBID display debug support block + if(s_logOn && hookMsg.message == OS.WM_NULL) + { + s_log.debug("Calling wm_null in filter and running asyn"); + } + //end CBID display debug support block } return OS.CallNextHookEx (hHook, code, wParam, lParam); } @@ -1537,15 +1581,75 @@ drawMenuBars (); runPopups (); if (OS.PeekMessage (msg, 0, 0, 0, OS.PM_REMOVE)) { + //CBID debug block + totalReceived ++; + //end CBID debug block + if (!isWakeMessage (msg)) { if (!filterMessage (msg)) { OS.TranslateMessage (msg); OS.DispatchMessage (msg); } + //CBID debug block + if(s_logOn) + { + s_log.debug("***Display - processing Windows message:"+ + + msg.message + " window:" + msg.hwnd); + } + //end CBID debug block runDeferredEvents (); + //CBID debug block + if(s_logOn) + { + s_log.debug("***finished windows message from Peek:"+ + + msg.message + " window:" + msg.hwnd); + } + //end CBID debug block return true; } + //CBID fix #2 + //ORIGINAL BLOCK (empty) + //REPLACEMENT START + //The design indicates that the wake messages are + //intended to be dispatched to the windowproc of the + //hwndMessage (see window proc!) and invoke + //runAsyncEvents(). Because of the if statement + // (if(!wakeMessage (msg) the wake message is not + //dispatched to this window proc. The else block + //corrects this by dispatching the wake message + //to the window proc of the hwndMessage. + //(It is curious to note that because of the problem + //in isWakeMessage() implementation in Windows 2000 and XP + //the wake messages were not recognized as such and were dispatched + //normally, but lacking proper hwnd, they were ignored by the native + //windows dispatch code). + else + { + + wakeReceived++; + if(s_logOn) + { + s_log.debug("***Display - processing wake message:"+ + + msg.message + " window:" + msg.hwnd + " received total " + wakeReceived); + } + //necessary to make the message go to windowproc of hwndMessage + msg.hwnd=hwndMessage; + OS.DispatchMessage (msg); + if(s_logOn) + { + s_log.debug("***finished wake message:"+ + + msg.message + " window:" + msg.hwnd); + } + return true; + } + //END CBID replacement } + //CBID display debug support block + if(s_logOn) + { + s_log.debug("*** display - Running async events because the peekmessage returned false; still left " + synchronizer.messageCount ); + } + //end CBID display debug support block return runAsyncMessages (); } -----END PATCH-----

Files changed in fix:
bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/widgets/Synchronizer.java bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Display.java