1 /*******************************************************************************
2  * Copyright (c) 2000, 2008 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  * Port to the D programming language:
11  *     Frank Benoit <benoit@tionex.de>
12  *******************************************************************************/
13 module org.eclipse.swt.widgets.Synchronizer;
14 
15 import org.eclipse.swt.widgets.Display;
16 import org.eclipse.swt.widgets.RunnableLock;
17 import org.eclipse.swt.internal.Compatibility;
18 import java.lang.all;
19 
20 import org.eclipse.swt.SWT;
21 import java.lang.Thread;
22 import org.eclipse.swt.graphics.Device;
23 version(Tango){
24     import tango.core.Exception;
25 } else { // Phobos
26     import java.nonstandard.sync.exception: SyncException;
27 }
28 
29 /**
30  * Instances of this class provide synchronization support
31  * for displays. A default instance is created automatically
32  * for each display, and this instance is sufficient for almost
33  * all applications.
34  * <p>
35  * <b>IMPORTANT:</b> Typical application code <em>never</em>
36  * needs to deal with this class. It is provided only to
37  * allow applications which require non-standard
38  * synchronization behavior to plug in the support they
39  * require. <em>Subclasses which override the methods in
40  * this class must ensure that the superclass methods are
41  * invoked in their implementations</em>
42  * </p>
43  *
44  * @see Display#setSynchronizer
45  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
46  */
47 public class Synchronizer {
48     Display display;
49     int messageCount;
50     RunnableLock [] messages;
51     Object messageLock;
52     Thread syncThread;
53     static const int GROW_SIZE = 4;
54     static const int MESSAGE_LIMIT = 64;
55 
56 /**
57  * Constructs a new instance of this class.
58  *
59  * @param display the display to create the synchronizer on
60  */
61 public this (Display display) {
62     this.display = display;
63     messageLock = new Object ();
64 }
65 
66 void addLast (RunnableLock lock) {
67     bool wake = false;
68     synchronized (messageLock) {
69         if (messages is null) messages = new RunnableLock [GROW_SIZE];
70         if (messageCount is messages.length) {
71             RunnableLock[] newMessages = new RunnableLock [messageCount + GROW_SIZE];
72             System.arraycopy (messages, 0, newMessages, 0, messageCount);
73             messages = newMessages;
74         }
75         messages [messageCount++] = lock;
76         wake = messageCount is 1;
77     }
78     if (wake) display.wakeThread ();
79 }
80 
81 /**
82  * Causes the <code>run()</code> method of the runnable to
83  * be invoked by the user-interface thread at the next
84  * reasonable opportunity. The caller of this method continues
85  * to run in parallel, and is not notified when the
86  * runnable has completed.
87  *
88  * @param runnable code to run on the user-interface thread.
89  *
90  * @see #syncExec
91  */
92 public void asyncExec (Runnable runnable) {
93     if (runnable is null) {
94         display.wake ();
95         return;
96     }
97     addLast (new RunnableLock (runnable));
98 }
99 
100 int getMessageCount () {
101     synchronized (messageLock) {
102         return messageCount;
103     }
104 }
105 
106 void releaseSynchronizer () {
107     display = null;
108     messages = null;
109     messageLock = null;
110     syncThread = null;
111 }
112 
113 RunnableLock removeFirst () {
114     synchronized (messageLock) {
115         if (messageCount is 0) return null;
116         RunnableLock lock = messages [0];
117         System.arraycopy (messages, 1, messages, 0, --messageCount);
118         messages [messageCount] = null;
119         if (messageCount is 0) {
120             if (messages.length > MESSAGE_LIMIT) messages = null;
121         }
122         return lock;
123     }
124 }
125 
126 bool runAsyncMessages () {
127     return runAsyncMessages (false);
128 }
129 
130 bool runAsyncMessages (bool all) {
131     bool run = false;
132     do {
133         RunnableLock lock = removeFirst ();
134         if (lock is null) return run;
135         run = true;
136         synchronized (lock) {
137             syncThread = lock.thread;
138             try {
139                 lock.run ();
140             } catch (Exception t) {
141                 lock.throwable = t;
142                 SWT.error (SWT.ERROR_FAILED_EXEC, t);
143             } finally {
144                 syncThread = null;
145                 lock.notifyAll ();
146             }
147         }
148     } while (all);
149     return run;
150 }
151 
152 /**
153  * Causes the <code>run()</code> method of the runnable to
154  * be invoked by the user-interface thread at the next
155  * reasonable opportunity. The thread which calls this method
156  * is suspended until the runnable completes.
157  *
158  * @param runnable code to run on the user-interface thread.
159  *
160  * @exception SWTException <ul>
161  *    <li>ERROR_FAILED_EXEC - if an exception occurred when executing the runnable</li>
162  * </ul>
163  *
164  * @see #asyncExec
165  */
166 public void syncExec (Runnable runnable) {
167     RunnableLock lock = null;
168     synchronized (Device.classinfo) {
169         if (display is null || display.isDisposed ()) SWT.error (SWT.ERROR_DEVICE_DISPOSED);
170         if (!display.isValidThread ()) {
171             if (runnable is null) {
172                 display.wake ();
173                 return;
174             }
175             lock = new RunnableLock (runnable);
176             /*
177              * Only remember the syncThread for syncExec.
178              */
179             lock.thread = Thread.currentThread();
180             addLast (lock);
181         }
182     }
183     if (lock is null) {
184         if (runnable !is null) runnable.run ();
185         return;
186     }
187     synchronized (lock) {
188         bool interrupted = false;
189         while (!lock.done ()) {
190             try {
191                 lock.wait ();
192             } catch (SyncException e) {
193                 interrupted = true;
194             }
195         }
196         if (interrupted) {
197             Compatibility.interrupt();
198         }
199         if (lock.throwable !is null) {
200             SWT.error (SWT.ERROR_FAILED_EXEC, lock.throwable);
201         }
202     }
203 }
204 
205 }