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 }