/* this is still buggy */ package abbot.swt; import java.util.*; import org.eclipse.swt.widgets.Display; /** * Since SWT does not use Java's synchronization support, this class was created * in order to coordinate calls to Display.syncExec(Runnable) among multiple * displays. This is accomplished by making all calls to Display.syncExec(Runnable) * from a dedicated Synchronizer Thread while preventing Displays associated with the * original calling thread from blocking. **/ public class Synchronizer{ public static final String copyright = "Licensed Materials -- Property of IBM\n"+ "(c) Copyright International Business Machines Corporation, 2003\nUS Government "+ "Users Restricted Rights - Use, duplication or disclosure restricted by GSA "+ "ADP Schedule Contract with IBM Corp."; boolean disposed; LinkedList displays = new LinkedList(); LinkedList actions = new LinkedList(); LinkedList pendingRequests = new LinkedList(); static final int SLEEP_TIME_NS = 10; static Synchronizer singleton; /* Init the synchronizer thread */ private Synchronizer(){ disposed = false; // the thread that monitors requests Thread syncThread = new Thread(){ public void run(){ while(!isDisposed()){ if(waitingRequest()) serviceRequest(); } } }; syncThread.setName("Abbot Synchronizer Thread"); syncThread.start(); } /* Returns the singleton instance of this class, if it exists; otherwise it * returns a new Synchronizer instance. */ public static Synchronizer getSynchronizer(){ if(singleton==null){ singleton = new Synchronizer(); } return singleton; } /* Execute a Runnable on the given Display's thread, making sure to * prevent the Display associated with the calling thread, if it exists, from * blocking. */ public void syncExec(Display display, Runnable action){ Display currentThreadsDisplay = Display.findDisplay(Thread.currentThread()); if(currentThreadsDisplay==display){ display.syncExec(action); } if(display==null) System.out.println("FOUND NULL DISPLAY"); else{ makeRequest(display, action); while(!requestServiced()){ if(currentThreadsDisplay!=null){ currentThreadsDisplay.readAndDispatch(); } else{ try{ Thread.sleep(0,SLEEP_TIME_NS); }catch(InterruptedException ie){ ie.printStackTrace(); } } } } } /* Make a request for a syncExec call */ private synchronized void makeRequest(Display display, Runnable action){ if(display.isDisposed()) System.out.println("FOUND DISPOSED DISPLAY"); displays.add(display); actions.add(action); pendingRequests.add(Thread.currentThread()); } /* Indicates if the request made by the current thread has been serviced yet */ private synchronized boolean requestServiced(){ return !pendingRequests.contains(Thread.currentThread()); } /* Called by the synchronizer thread to actually make the next syncExec call */ private void serviceRequest(){ Display display; Runnable action; synchronized(this){ // get the display and runnable for the next request display = (Display)displays.getFirst(); displays.removeFirst(); action = (Runnable)actions.getFirst(); actions.removeFirst(); } // make the requested syncExec call if(display.isDisposed()) System.out.println("FOUND DISPOSED DISPLAY"); display.wake(); display.syncExec(action); synchronized(this){ // note that the request was serviced pendingRequests.removeFirst(); } } /* Indicates if there is a request that is awaiting servicing */ private synchronized boolean waitingRequest(){ return pendingRequests.size()>0; } /* Dispose of the synchronizer */ public void dispose(){ disposed = true; } /* Indicates if the synchronizer has been disposed yet */ public synchronized boolean isDisposed(){ return disposed; } } //public class Synchronizer { // Hashtable locks = new Hashtable(); // ArrayList queue = new ArrayList(); // static Synchronizer synchronizer; // Hashtable flags = new Hashtable(); // static final int SLEEP_TIME_NS = 100; // // Synchronizer(){// make sure that no one can instantiate their own // } // // public synchronized void initFlag(Thread callingThread){ // flags.put(callingThread,new Boolean(false)); // } // // public synchronized void clearFlag(Thread callingThread){ // flags.remove(callingThread); // } // // /* To be called inside of an syncExec/asyncExec call */ // public synchronized void setFlag(Object obj, Thread callingThread){ // Display display = Display.findDisplay(Thread.currentThread()); // flags.put(callingThread, new Boolean(true)); // try{ // if(obj!=null) // obj.notifyAll(); // }catch(IllegalMonitorStateException imse){ // imse.printStackTrace(); // } // notifyAll(); // } // // public synchronized boolean getFlag(Thread callingThread){ //// if(display==null) //// System.err.println("null display in getFlag"); //// if(flags.get(display)==null) //// System.err.println("null flag in getFlag"); // return ((Boolean)flags.get(callingThread)).booleanValue(); // } // // // DOESN'T WORK... breaks when multiple threads are waiting... // /** // * // * @param display the Display whose thread the Runnable is to be run on // * @param obj the Object inside whose synchronized method this is called or null // * @param action the action to be executed // */ // public void syncExec(Display display, final Object obj,final Runnable action){ // syncExecEntry(display); // display.syncExec(action); // syncExecExit(display); // //// final Thread callingThread = Thread.currentThread(); //// initFlag(callingThread); //// //// //// display.asyncExec(new Runnable(){ //// public void run(){ //// if(obj!=null){ //// synchronized(obj){ //// action.run(); //// setFlag(obj,callingThread); //// } //// } //// else{ //// action.run(); //// setFlag(obj,callingThread); //// } //// } //// }); //// //// while(!getFlag(callingThread)){ ////// Robot.waitForIdle(display); //// if(display.getThread()==Thread.currentThread()){ //// display.readAndDispatch(); //// } //// else{ //// try{ //// if(obj==null) //// wait(); //// //Thread.sleep(0,SLEEP_TIME_NS); //// else //// try{ //// wait(); //// obj.wait(); //// }catch(IllegalMonitorStateException imse){ //// imse.printStackTrace(); //// } //// } //// catch(InterruptedException ie){ //// ie.printStackTrace(); //// } //// } //// } //// //// clearFlag(callingThread); // } // // /** Returns the singleton instance of this class **/ // public static Synchronizer getSynchronizer(){ // if(synchronizer==null) // synchronizer = new Synchronizer(); // return synchronizer; // } // Hashtable callerDisplays = new Hashtable(); // Hashtable calleeDisplays = new Hashtable(); // // HashSet lockedDisplays = new HashSet(); // // only lock the calling thread's display, if any // // if this doesn't work // public void syncExecEntry(Display display){ // boolean locked; // Display thisThreadsDisplay = Display.findDisplay(Thread.currentThread()); // // while(true){ // synchronized(this){ // locked = lockedDisplays.contains(display); // } // if(locked) // thisThreadsDisplay.readAndDispatch(); // else{ // lockedDisplays.add(display); // } // } // } // // public void syncExecExit(Display display){ // synchronized(this){ // lockedDisplays.remove(display); // } // } // // /* Prevents the Display object associated with the current thread, if it exists, // * from blocking, and locks the given object. The Object parameter should be // * the object on which a method is synchronized, or null. // */ // public void syncEntry(Object obj){ // boolean locked; // Display display; // synchronized(this){ // if(!locks.containsKey(obj)) // locks.put(obj,new Boolean(false)); // // locked =((Boolean)locks.get(obj)).booleanValue(); // if(!locked){ // lock // locks.put(obj, new Boolean(true)); // return; // } // } // display = Display.findDisplay(Thread.currentThread()); // if(display==null && obj!=null){ // try{obj.wait();} // NOT SURE ABOUT THIS ONE. // catch(Exception e){ // e.printStackTrace(); // } // } // // while(true){ // synchronized(this){ // locked =((Boolean)locks.get(obj)).booleanValue(); // if(!locked){ // locks.put(obj, new Boolean(true)); // return; // } // } // if(!display.isDisposed()) // display.readAndDispatch(); // } // } // // public synchronized void syncExit(Object obj){ // locks.put(obj,new Boolean(false)); // obj.notifyAll(); // } //} // /** Execute the given Runnable on the Display's Thread in a synchronized // * manner. // */ // public void syncExec(Display display, Runnable action){ // lock(display); // display.syncExec(action); // unlock(display); // } // // private synchronized void lock(final Display display){ // if(!locks.containsKey(display)){ // locks.put(display,new Boolean(false)); // } // boolean locked = ((Boolean)locks.get(display)).booleanValue(); // // if(locked){ // try{ // wait(); // } // catch(InterruptedException ie){ // ie.printStackTrace(); // } // } // display.syncExec(new Runnable(){ // public void run(){ // locks.put(display,new Boolean(true)); // } // }); // } // // private synchronized void unlock(final Display display){ // display.syncExec(new Runnable(){ // public void run(){ // locks.put(display,new Boolean(false)); // } // }); // notifyAll(); // } //} /* Here's the plan. We're going to have 4 synchronization methods- * syncExecEntry(Display)/syncExecExit(Display) * syncEntry(Object)/syncExit(Object) * And they're all going to use the same set of locks. * * Inside a now-synchronized method (which will no longer be once this is ready), * we'll do this: * public void myMethod(){ * syncEntry(this); * ... * Robot.syncExec() // which calls syncExecEntry/...Exit * * synchronized(this){ * syncExit(this); * return res; * } * } * Note how we overlapped the synchronization so that we can return a result while * still protected. This will require that you do something other that obj.wait * inside of syncEntry. You should wait on some other object that is associated * with obj. * * Just to summarize, the purpose of syncExecEntry/Exit is to make sure that the * calling thread doesn't block so that calls to syncExec on the display associated * with this calling thread do not block as well and cause deadlock. * * The syncEntry/Exit is to make sure that, inside a syncExec block, we don't block * on a synchronized method while someone else blocks on another call to syncExec/ * * The existing code is not correct, but its getting somewhere. Just think about * these 2 situations and it should work out. */