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 
24 /**
25  * Instances of this class provide synchronization support
26  * for displays. A default instance is created automatically
27  * for each display, and this instance is sufficient for almost
28  * all applications.
29  * <p>
30  * <b>IMPORTANT:</b> Typical application code <em>never</em>
31  * needs to deal with this class. It is provided only to
32  * allow applications which require non-standard
33  * synchronization behavior to plug in the support they
34  * require. <em>Subclasses which override the methods in
35  * this class must ensure that the superclass methods are
36  * invoked in their implementations</em>
37  * </p>
38  *
39  * @see Display#setSynchronizer
40  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
41  */
42 public class Synchronizer {
43     Display display;
44     int messageCount;
45     RunnableLock [] messages;
46     Object messageLock;
47     Thread syncThread;
48     static const int GROW_SIZE = 4;
49     static const int MESSAGE_LIMIT = 64;
50 
51 /**
52  * Constructs a new instance of this class.
53  *
54  * @param display the display to create the synchronizer on
55  */
56 public this (Display display) {
57     this.display = display;
58     messageLock = new Object ();
59 }
60 
61 void addLast (RunnableLock lock) {
62     bool wake = false;
63     synchronized (messageLock) {
64         if (messages is null) messages = new RunnableLock [GROW_SIZE];
65         if (messageCount is messages.length) {
66             RunnableLock[] newMessages = new RunnableLock [messageCount + GROW_SIZE];
67             System.arraycopy (messages, 0, newMessages, 0, messageCount);
68             messages = newMessages;
69         }
70         messages [messageCount++] = lock;
71         wake = messageCount is 1;
72     }
73     if (wake) display.wakeThread ();
74 }
75 
76 /**
77  * Causes the <code>run()</code> method of the runnable to
78  * be invoked by the user-interface thread at the next
79  * reasonable opportunity. The caller of this method continues
80  * to run in parallel, and is not notified when the
81  * runnable has completed.
82  *
83  * @param runnable code to run on the user-interface thread.
84  *
85  * @see #syncExec
86  */
87 public void asyncExec (Runnable runnable) {
88     if (runnable is null) {
89         display.wake ();
90         return;
91     }
92     addLast (new RunnableLock (runnable, false));
93 }
94 
95 int getMessageCount () {
96     synchronized (messageLock) {
97         return messageCount;
98     }
99 }
100 
101 void releaseSynchronizer () {
102     display = null;
103     messages = null;
104     messageLock = null;
105     syncThread = null;
106 }
107 
108 RunnableLock removeFirst () {
109     synchronized (messageLock) {
110         if (messageCount is 0) return null;
111         RunnableLock lock = messages [0];
112         System.arraycopy (messages, 1, messages, 0, --messageCount);
113         messages [messageCount] = null;
114         if (messageCount is 0) {
115             if (messages.length > MESSAGE_LIMIT) messages = null;
116         }
117         return lock;
118     }
119 }
120 
121 bool runAsyncMessages () {
122     return runAsyncMessages (false);
123 }
124 
125 bool runAsyncMessages (bool all) {
126     bool run = false;
127     do {
128         RunnableLock lock = removeFirst ();
129         if (lock is null) return run;
130         run = true;
131         scope (exit) {
132             if (!lock.syncExec) {
133                 // Release handle of lock#cond.
134                 // If not released here, the handle will not be released until the next GC works.
135                 destroy(lock);
136             }
137         }
138         synchronized (lock) {
139             syncThread = lock.thread;
140             try {
141                 lock.run ();
142             } catch (Exception t) {
143                 lock.throwable = t;
144                 SWT.error (SWT.ERROR_FAILED_EXEC, t);
145             } finally {
146                 syncThread = null;
147                 lock.notifyAll ();
148             }
149         }
150     } while (all);
151     return run;
152 }
153 
154 /**
155  * Causes the <code>run()</code> method of the runnable to
156  * be invoked by the user-interface thread at the next
157  * reasonable opportunity. The thread which calls this method
158  * is suspended until the runnable completes.
159  *
160  * @param runnable code to run on the user-interface thread.
161  *
162  * @exception SWTException <ul>
163  *    <li>ERROR_FAILED_EXEC - if an exception occurred when executing the runnable</li>
164  * </ul>
165  *
166  * @see #asyncExec
167  */
168 public void syncExec (Runnable runnable) {
169     RunnableLock lock = null;
170     synchronized (Device.classinfo) {
171         if (display is null || display.isDisposed ()) SWT.error (SWT.ERROR_DEVICE_DISPOSED);
172         if (!display.isValidThread ()) {
173             if (runnable is null) {
174                 display.wake ();
175                 return;
176             }
177             lock = new RunnableLock (runnable, true);
178             /*
179              * Only remember the syncThread for syncExec.
180              */
181             lock.thread = Thread.currentThread();
182             addLast (lock);
183         }
184     }
185     if (lock is null) {
186         if (runnable !is null) runnable.run ();
187         return;
188     }
189     scope (exit) {
190         // Release handle of lock#cond.
191         // If not released here, the handle will not be released until the next GC works.
192         destroy(lock);
193     }
194     synchronized (lock) {
195         bool interrupted = false;
196         while (!lock.done ()) {
197             lock.wait();
198         }
199         if (interrupted) {
200             Compatibility.interrupt();
201         }
202         if (lock.throwable !is null) {
203             SWT.error (SWT.ERROR_FAILED_EXEC, lock.throwable);
204         }
205     }
206 }
207 
208 }