Community
Participate
Working Groups
I am proposing to change SWT code to unsure reliable interoperability between AWT/Swing and SWT. The changes address following problems: 1. Merging SWT and AWT dispatch loops into one. Having two dispatch threads makes Swsing based code integration almost impossible because of all kinds of thread synchronization problems, especially when Swing based code has to access Eclipse public APIs. 2. Original AWT integration plugin is unstable, mostly because of problem #1. See SwingPlugin.java, new AWT integration plugin code, similar to the original one - it allows to embed AWT Frame into any SWT Composite, and can be used as basis for an application integration. Detailed description of changes: dircmp: Directory not found: c:\eclipse\2.1-org\source\plugins\com.windriver.rb.eclipse.awt *** ET: see SwingPlugin.java at the and of this file. It defines a class which allows to create AWT Frame embeded into SWT Composite. dircmp: Files are not equal: < Wed Jun 18 00:09:23 2003, size 41450, summ 0 eclipse\workspace\org.eclipse.core.boot\src\org\eclipse\core\internal\boot\Inter nalBootLoader.java > Mon Mar 10 17:09:50 2003, size 40712, summ 0 c:\eclipse\2.1- org\source\plugins\org.eclipse.core.boot\src\org\eclipse\core\internal\boot\Inte rnalBootLoader.java diff eclipse\workspace\org.eclipse.core.boot\src\org\eclipse\core\internal\boot\Inter nalBootLoader.java c:\eclipse\2.1- org\source\plugins\org.eclipse.core.boot\src\org\eclipse\core\internal\boot\Inte rnalBootLoader.java | more *** ET: code below passes control from Main thread to AWT dispatch thread during bootstrap. AWT dispatch thread from now on will be the main and only one UI handlig thread. 835,873c835,852 < public static Object run( < final String applicationName/*R1.0 compatibility*/, < final URL pluginPathLocation/*R1.0 compatibility*/, < final String location, < final String[] args, < final Runnable handler) throws Exception { < final Object result[] = { null }; < final Exception error[] = { null }; < java.awt.EventQueue.invokeAndWait(new Runnable() { < public void run() { < try { < applicationR10 = applicationName; // for R1.0 compatibility < String[] applicationArgs = startup(pluginPathLocation, location, args, handler); < < String application = getCurrentPlatformConfiguration ().getApplicationIdentifier(); < IPlatformRunnable runnable = getRunnable(application); < if (runnable == null) < throw new IllegalArgumentException(Policy.bind ("application.notFound", application)); //$NON-NLS-1$ < try { < result[0] = runnable.run(applicationArgs); < } < catch (Throwable e) { < e.printStackTrace(); < throw new InvocationTargetException(e); < } < finally { < shutdown(); < } < } < catch (Exception x) { < error[0] = x; < } < catch (Throwable x) { < error[0] = new Exception(x); < } < } < }); < if (error[0] != null) throw error[0]; < return result[0]; --- > public static Object run(String applicationName/*R1.0 compatibility*/, URL pluginPathLocation/*R1.0 compatibility*/, String location, String[] args, Runnable handler) throws Exception { > Object result = null; > applicationR10 = applicationName; // for R1.0 compatibility > String[] applicationArgs = startup(pluginPathLocation, location, args, handler); > > String application = getCurrentPlatformConfiguration ().getApplicationIdentifier(); > IPlatformRunnable runnable = getRunnable(application); > if (runnable == null) > throw new IllegalArgumentException(Policy.bind ("application.notFound", application)); //$NON-NLS-1$ > try { > result = runnable.run(applicationArgs); > } catch (Throwable e) { > e.printStackTrace(); > throw new InvocationTargetException(e); > } finally { > shutdown(); > } > return result; dircmp: Files are not equal: < Fri Jun 13 09:24:40 2003, size 73433, summ 0 eclipse\workspace\org.eclipse.swt\Eclipse SWT\win32 \org\eclipse\swt\widgets\Display.java > Thu Mar 27 21:52:48 2003, size 70497, summ 0 c:\eclipse\2.1-org\source\plugins\org.eclipse.swt\Eclipse SWT\win32 \org\eclipse\swt\widgets\Display.java diff "eclipse\workspace\org.eclipse.swt\Eclipse SWT\win32 \org\eclipse\swt\widgets\Display.java" "c:\eclipse\2.1- org\source\plugins\org.eclipse.swt\Eclipse SWT\win32 \org\eclipse\swt\widgets\Display.java" | more *** ET: AWTWatchDog thread is responsible to call wake() when Display is sleeping and AWT event queue got an event. 231,270d230 < < private class AWTWatchDog extends Thread { < < boolean display_sleeping = false; < < private java.awt.EventQueue queue = java.awt.Toolkit < .getDefaultToolkit().getSystemEventQueue(); < < AWTWatchDog() { < setName("AWT-WatchDog"); < start(); < } < < public void run() { < for (;;) { < try { < synchronized (this) { < if (!display_sleeping) { < wait(); < continue; < } < } < sun.awt.SunToolkit.flushPendingEvents(); < synchronized (queue) { < if (queue.peekEvent() == null) { < queue.wait(); < continue; < } < } < wake(); < } < catch (Throwable x) { < x.printStackTrace(); < } < } < } < } < < private final AWTWatchDog awt_watch_dog = new AWTWatchDog(); < *** ET: main purpose of changes below is to insert call to runATW() into dispatch loop. I also changed dispatch order a little, because I think it is more efficient this way, but it is not really necessary. 1576,1577c1536 < boolean res = false; < checkDevice (); --- > checkDevice (); 1579,1580c1538,1539 < if (runPopups ()) res = true; < while (OS.PeekMessage (msg, 0, 0, 0, OS.PM_REMOVE)) { --- > runPopups (); > if (OS.PeekMessage (msg, 0, 0, 0, OS.PM_REMOVE)) { 1585d1543 < res = true; 1586a1545,1546 > runDeferredEvents (); > return true; 1589,1592c1549 < if (runAWT ()) res = true; < if (eventQueue != null && runDeferredEvents ()) res = true; < if (runAsyncMessages ()) res = true; < return res; --- > return runAsyncMessages (); *** ET: method runAWT() reads AWT queue and dispatches events. It would be nice to call java.awt.EventQueue.dispatchEvent() here, but unfortunately it is not public, so I had to put together another version of dispatch code. 1913,1943d1869 < private boolean runAWT () { < if (!java.awt.EventQueue.isDispatchThread()) throw new Error("Must be dispatch thread"); < sun.awt.SunToolkit.flushPendingEvents(); < java.awt.EventQueue q = java.awt.Toolkit < .getDefaultToolkit().getSystemEventQueue(); < boolean res = false; < while (q.peekEvent() != null) { < try { < res = true; < java.awt.AWTEvent e = q.getNextEvent(); < Object src = e.getSource(); < if (e instanceof java.awt.ActiveEvent) { < ((java.awt.ActiveEvent)e).dispatch(); < } < else if (src instanceof java.awt.Component) { < ((java.awt.Component)src).dispatchEvent(e); < } < else if (src instanceof java.awt.MenuComponent) { < ((java.awt.MenuComponent)src).dispatchEvent(e); < } < else { < throw new Error("Unable to dispatch event: " + e); < } < } < catch (InterruptedException x) { < throw new Error(x); < } < } < return res; < } < *** ET: sleep() method is modified to notify AWTWatchDog (see above) 2203,2207d2128 < boolean res = false; < synchronized (awt_watch_dog) { < awt_watch_dog.display_sleeping = true; < awt_watch_dog.notifyAll(); < } 2209,2210c2130,2131 < OS.MsgWaitForMultipleObjectsEx (0, 0, OS.INFINITE, OS.QS_ALLINPUT, OS.MWMO_INPUTAVAILABLE); < res = true; --- > OS.MsgWaitForMultipleObjectsEx (0, 0, OS.INFINITE, OS.QS_ALLINPUT, OS.MWMO_INPUTAVAILABLE); > return true; 2212,2219c2133 < else { < res = OS.WaitMessage (); < } < synchronized (awt_watch_dog) { < awt_watch_dog.display_sleeping = false; < awt_watch_dog.notifyAll(); < } < return res; --- > return OS.WaitMessage (); *** ET: SwingPlugin.java package com.windriver.rb.eclipse.awt; import java.awt.*; import org.eclipse.core.runtime.IPluginDescriptor; import org.eclipse.swt.SWT; import org.eclipse.swt.internal.win32.OS; import org.eclipse.swt.internal.win32.RECT; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.ui.plugin.AbstractUIPlugin; import sun.awt.windows.WEmbeddedFrame; public class SwingPlugin extends AbstractUIPlugin { private Display display; private static class EFrame extends WEmbeddedFrame implements Listener { private Composite parent; private String name; EFrame(Composite parent, String name) { super(parent.handle); this.parent = parent; this.name = name; parent.addListener(SWT.Resize, this); parent.addListener(SWT.Move, this); parent.addListener(SWT.Dispose, this); parent.getShell().addListener(SWT.Move, this); setFrameBounds(); setCursor(Cursor.getDefaultCursor()); addNotify(); validate(); } public void handleEvent(Event e) { if (e.type == SWT.Dispose) { parent.removeListener(SWT.Resize, this); parent.removeListener(SWT.Move, this); parent.removeListener(SWT.Dispose, this); parent.getShell().removeListener(SWT.Move, this); } else { setFrameBounds(); } } private void setFrameBounds() { org.eclipse.swt.graphics.Rectangle rc = parent.getClientArea(); RECT rw = new RECT(); OS.GetWindowRect(parent.handle, rw); setBounds(rw.left + rc.x, rw.top + rc.y, rc.width, rc.height); validate(); } public void paint(Graphics g) { super.paint(g); sun.awt.windows.WGlobalCursorManager.getCursorManager ().updateCursorImmediately(); } } public SwingPlugin(IPluginDescriptor descriptor) { super(descriptor); } public static Frame createFrame(Composite parent, String name) { return new EFrame(parent, name); } public static Composite getParentComposite(Component f) { if (f instanceof EFrame) return ((EFrame)f).parent; return null; } }
SSQ and SN to evaluate the code and the approach.
Created attachment 5361 [details] fixed: unnecessary delays in AWT event dispatch
I've found and fixed couple defects which were causing unnecessary delays in AWT event dispatch. New version of Display.java is attached. I'm still working on a version for Motif. I also would like to point out that this code provides only low level integration, i.e. event dispatch and windows parent/child coupling. It would be nice to include focus management integration, shortcuts support, etc. For now it has to be done on a client side. Providing such support would make an application integration job much easier.
Created attachment 5410 [details] stand alone test case The following attachment is a stand alone test case for the code.
It seems that the only advantage of this strategy is that application code does not have to call syncExec(), asyncExec(), invokeAndWait() or invokeLater () in order to communicate between threads. There are still two user interface threads in the operating system. One is AWT's user interace thread and the other is the SWT's user interface thread (which happens also to be AWT's event dispatcher thread). Runing the test case above after applying the changes to Display causes a deadlock when you resize the window to be as small as possible. Debugging this under Eclipse shows the deadlock. AWT's user interface thread is trying to calling getGraphicsConfiguration(): Thread [AWT-Windows] (Suspended) AWT_SWT_SingleLoop$EFrame(Window).getGraphicsConfiguration() line: 1857 [local variables unavailable] WEmbeddedFramePeer(WComponentPeer).getGraphicsConfiguration() line: 301 WEmbeddedFramePeer(WComponentPeer).getDeviceColorModel() line: 361 Win32SurfaceData.createData(WComponentPeer, int) line: 219 WEmbeddedFramePeer(WComponentPeer).replaceSurfaceData() line: 319 WToolkit.eventLoop() line: not available [native method] WToolkit.run() line: 222 Thread.run() line: 539 AWT's event dispatcher (also SWT's user interface thread) is trying to resize the AWT embedded frame: Thread [AWT-EventQueue-0] (Suspended) WWindowPeer.reshapeFrame(int, int, int, int) line: not available [native method] WEmbeddedFramePeer(WFramePeer).reshape(int, int, int, int) line: 50 WEmbeddedFramePeer(WComponentPeer).setBounds(int, int, int, int) line: 130 AWT_SWT_SingleLoop$EFrame(Component).reshape(int, int, int, int) line: 1686 AWT_SWT_SingleLoop$EFrame(Component).setBounds(int, int, int, int) line: 1645 AWT_SWT_SingleLoop$EFrame.setFrameBounds() line: 52 AWT_SWT_SingleLoop$EFrame.handleEvent(Event) line: 44 EventTable.sendEvent(Event) line: 82 Shell(Widget).sendEvent(Event) line: 848 Shell(Widget).sendEvent(int, Event, boolean) line: 872 Shell(Widget).sendEvent(int) line: 853 Shell(Control).WM_SIZE(int, int) line: 4017 Shell(Scrollable).WM_SIZE(int, int) line: 308 Shell(Composite).WM_SIZE(int, int) line: 780 Shell(Decorations).WM_SIZE(int, int) line: 1415 Shell(Control).windowProc(int, int, int) line: 2893 Shell(Decorations).windowProc(int, int, int) line: 1280 Display.windowProc(int, int, int, int) line: 2640 OS.DefWindowProcW(int, int, int, int) line: not available [native method] OS.DefWindowProc(int, int, int, int) line: 1332 Shell.callWindowProc(int, int, int) line: 397 Shell(Control).windowProc(int, int, int) line: 2906 Shell(Decorations).windowProc(int, int, int) line: 1280 Display.windowProc(int, int, int, int) line: 2640 OS.DefWindowProcW(int, int, int, int) line: not available [native method] OS.DefWindowProc(int, int, int, int) line: 1332 Shell.callWindowProc(int, int, int) line: 397 Shell(Control).windowProc(int, int, int) line: 2906 Shell(Decorations).windowProc(int, int, int) line: 1280 Display.windowProc(int, int, int, int) line: 2640 OS.DefWindowProcW(int, int, int, int) line: not available [native method] OS.DefWindowProc(int, int, int, int) line: 1332 Shell.callWindowProc(int, int, int) line: 397 Shell(Control).windowProc(int, int, int) line: 2906 Shell(Decorations).windowProc(int, int, int) line: 1280 Display.windowProc(int, int, int, int) line: 2640 OS.DispatchMessageW(MSG) line: not available [native method] OS.DispatchMessage(MSG) line: 1337 Display.readAndDispatch() line: 1787 AWT_SWT_SingleLoop$1.run() line: 75 InvocationEvent.dispatch() line: 174 EventQueue.dispatchEvent(AWTEvent) line: 446 EventDispatchThread.pumpOneEventForHierarchy(int, Component) line: 193 EventDispatchThread.pumpEventsForHierarchy(int, Conditional, Component) line: 147 EventDispatchThread.pumpEvents(int, Conditional) line: 141 EventDispatchThread.pumpEvents(Conditional) line: 133 EventDispatchThread.run() line: 101 It seems that this strategy can deadlock (easily?) in code that is beyond our control. We think that automatically attempting to hide synchronozation issues will not work. SN and SSQ
I investigated the deadlock problem found by Silenio. Here is what I've found: 1. Deadlock caused by invalid synchronization code in AWT, two threads acquiring locks in different order: Thread [AWT-EventQueue-0] synchronized (Component.getTreeLock()) { ... synchronized (WWindowPeer.this) { ... Thread [AWT-Windows] synchronized (WWindowPeer.this) { ... synchronized (Component.getTreeLock()) { ... 2. Bug has nothing to do with SWT or Eclipse, it exists in pure Java. 3. Bug exists only in 1.4.0 beta and release candidate versions of JDK. 1.3.1 does not have relevant code at all. 1.4.0 final has it fixed by substituting call to WComponentPeer.replaceSurfaceData() with calling WComponentPeer.replaceSurfaceDataLater(), which is similar to using asyncExec(). I also would like to add: I argree with Silenio - the only advantage of this strategy is that application code does not have to call syncExec(), asyncExec(), invokeAndWait() or invokeLater() in order to communicate between threads. However, IMHO, this is HUGE advantage! It changes tight integration of Swing based application from practically impossible to relatively simple. BTW, "There are still two user interface threads ..." - it is not exactly true. AWT-Windows thread never executes application code. It's scope is limited to AWT native code. After proposed changes application UI code is executed by single thread, which eliminates all (well, almost) synchronization problems.
Nope, I'm sorry, there are still 2 Windows user interface threads with their associated Windows message queues and other hidden Windows thread specific data that Windows creates behind the scenes when you create the first HWND in a thread. Windows knows when a thread is a UI-thread and treats it differently. SSQ and SN to continue the investigation of deadlock issues with a better VM.
Steve, you right, there are two Windows event queues and two threads reading Windows events. But that is not what I worry about. I'm developing application code on top of JDK and Eclipse. To get synchronization right I only need to know which threads will run into MY code (application code) through call-backs, public interfaces, etc., and which threads I have to use to call public APIs. There are two such threads: "main" from Eclipse and "AWT-EventQueue" from AWT. "AWT-Windows" thread never reaches my code, so I don't care much about it as long as it stays that way. The idea is to make sure there is exactly one such thread (it does not really matter which one - I have modified version of AWT which uses "main" for dispatch - works fine). Having one thread in UI part of my application would make my job a LOT easier.
I agree. There is only one "user interface thread" as far as the programmer is concerned. Here's a thought: If the AWT-Windows thread (OS UI-thread for AWT) ever blocks and can no longer process messages for the AWT Event-Queue thread (which also happens to be the the OS UI-thread for SWT), then we can deadlock.
Created attachment 5423 [details] code using only API Here is an implementation of the strategy the uses SWT API only. No hacks in Display are necessary.
> Here is an implementation of the strategy the uses SWT API only... Cool! The only hack needed after that is invokeAndWait() somewhere in Eclipse booter code.
Eugene, I would like to close this PR as there doesn't seem that there is anything left for SWT to do. Your thoughts?
Hi Steve, You can close it. The solution works perfectly for Windows. I still have to make it work on Unix, but I can open new PR for that if it will be necessary.
Thanks. Good luck.