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, false));
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         scope (exit) {
137             if (!lock.syncExec) {
138                 // Release handle of lock#cond.
139                 // If not released here, the handle will not be released until the next GC works.
140                 destroy(lock);
141             }
142         }
143         synchronized (lock) {
144             syncThread = lock.thread;
145             try {
146                 lock.run ();
147             } catch (Exception t) {
148                 lock.throwable = t;
149                 SWT.error (SWT.ERROR_FAILED_EXEC, t);
150             } finally {
151                 syncThread = null;
152                 lock.notifyAll ();
153             }
154         }
155     } while (all);
156     return run;
157 }
158 
159 /**
160  * Causes the <code>run()</code> method of the runnable to
161  * be invoked by the user-interface thread at the next
162  * reasonable opportunity. The thread which calls this method
163  * is suspended until the runnable completes.
164  *
165  * @param runnable code to run on the user-interface thread.
166  *
167  * @exception SWTException <ul>
168  *    <li>ERROR_FAILED_EXEC - if an exception occurred when executing the runnable</li>
169  * </ul>
170  *
171  * @see #asyncExec
172  */
173 public void syncExec (Runnable runnable) {
174     RunnableLock lock = null;
175     synchronized (Device.classinfo) {
176         if (display is null || display.isDisposed ()) SWT.error (SWT.ERROR_DEVICE_DISPOSED);
177         if (!display.isValidThread ()) {
178             if (runnable is null) {
179                 display.wake ();
180                 return;
181             }
182             lock = new RunnableLock (runnable, true);
183             /*
184              * Only remember the syncThread for syncExec.
185              */
186             lock.thread = Thread.currentThread();
187             addLast (lock);
188         }
189     }
190     if (lock is null) {
191         if (runnable !is null) runnable.run ();
192         return;
193     }
194     scope (exit) {
195         // Release handle of lock#cond.
196         // If not released here, the handle will not be released until the next GC works.
197         destroy(lock);
198     }
199     synchronized (lock) {
200         bool interrupted = false;
201         while (!lock.done ()) {
202             try {
203                 lock.wait ();
204             } catch (SyncException e) {
205                 interrupted = true;
206             }
207         }
208         if (interrupted) {
209             Compatibility.interrupt();
210         }
211         if (lock.throwable !is null) {
212             SWT.error (SWT.ERROR_FAILED_EXEC, lock.throwable);
213         }
214     }
215 }
216 
217 }