comparison org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/OrderedLock.d @ 12:bc29606a740c

Added dwt-addons in original directory structure of eclipse.org
author Frank Benoit <benoit@tionex.de>
date Sat, 14 Mar 2009 18:23:29 +0100
parents
children 6f068362a363
comparison
equal deleted inserted replaced
11:43904fec5dca 12:bc29606a740c
1 /*******************************************************************************
2 * Copyright (c) 2003, 2006 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 - Initial API and implementation
10 * Port to the D programming language:
11 * Frank Benoit <benoit@tionex.de>
12 *******************************************************************************/
13 module org.eclipse.core.internal.jobs.OrderedLock;
14
15 import tango.text.convert.Format;
16 import java.lang.JThread;
17 import tango.io.Stdout;
18 import java.lang.all;
19 import java.util.Set;
20
21 import org.eclipse.core.runtime.Assert;
22 import org.eclipse.core.runtime.jobs.ILock;
23 import org.eclipse.core.runtime.jobs.ISchedulingRule;
24
25 import org.eclipse.core.internal.jobs.LockManager;
26 import org.eclipse.core.internal.jobs.Queue;
27 import org.eclipse.core.internal.jobs.Semaphore;
28
29 /**
30 * A lock used to control write access to an exclusive resource.
31 *
32 * The lock avoids circular waiting deadlocks by detecting the deadlocks
33 * and resolving them through the suspension of all locks owned by one
34 * of the threads involved in the deadlock. This makes it impossible for n such
35 * locks to deadlock while waiting for each other. The downside is that this means
36 * that during an interval when a process owns a lock, it can be forced
37 * to give the lock up and wait until all locks it requires become
38 * available. This removes the feature of exclusive access to the
39 * resource in contention for the duration between acquire() and
40 * release() calls.
41 *
42 * The lock implementation prevents starvation by granting the
43 * lock in the same order in which acquire() requests arrive. In
44 * this scheme, starvation is only possible if a thread retains
45 * a lock indefinitely.
46 */
47 public class OrderedLock : ILock, ISchedulingRule {
48
49 private static const bool DEBUG = false;
50 /**
51 * Locks are sequentially ordered for debugging purposes.
52 */
53 private static int nextLockNumber = 0;
54 /**
55 * The thread of the operation that currently owns the lock.
56 */
57 private /+volatile+/ JThread currentOperationThread;
58 /**
59 * Records the number of successive acquires in the same
60 * thread. The lock is released only when the depth
61 * reaches zero.
62 */
63 private int depth;
64 /**
65 * The manager that implements the deadlock detection and resolution protocol.
66 */
67 private const LockManager manager;
68 private const int number;
69
70 /**
71 * Queue of semaphores for threads currently waiting
72 * on the lock. This queue is not thread-safe, so access
73 * to this queue must be synchronized on the lock instance.
74 */
75 private const Queue operations;
76
77 /**
78 * Creates a new workspace lock.
79 */
80 this(LockManager manager) {
81
82 operations = new Queue();
83
84 this.manager = manager;
85 this.number = nextLockNumber++;
86 }
87
88 /* (non-Javadoc)
89 * @see Locks.ILock#acquire()
90 */
91 public void acquire() {
92 //spin until the lock is successfully acquired
93 //NOTE: spinning here allows the UI thread to service pending syncExecs
94 //if the UI thread is waiting to acquire a lock.
95 while (true) {
96 try {
97 if (acquire(Long.MAX_VALUE))
98 return;
99 } catch (InterruptedException e) {
100 //ignore and loop
101 }
102 }
103 }
104
105 /* (non-Javadoc)
106 * @see Locks.ILock#acquire(long)
107 */
108 public bool acquire(long delay) {
109 implMissing(__FILE__, __LINE__ );
110 // if (Thread.interrupted())
111 // throw new InterruptedException();
112
113 bool success = false;
114 if (delay <= 0)
115 return attempt();
116 Semaphore semaphore = createSemaphore();
117 if (semaphore is null)
118 return true;
119 if (DEBUG)
120 Stdout.formatln("[{}] Operation waiting to be executed... ", JThread.currentThread(), this); //$NON-NLS-1$ //$NON-NLS-2$
121 success = doAcquire(semaphore, delay);
122 manager.resumeSuspendedLocks(JThread.currentThread());
123 if (DEBUG && success)
124 Stdout.formatln("[{}] Operation started... ", JThread.currentThread(), this); //$NON-NLS-1$ //$NON-NLS-2$
125 else if (DEBUG)
126 Stdout.formatln("[{}] Operation timed out... ", JThread.currentThread(), this); //$NON-NLS-1$ //$NON-NLS-2$
127 return success;
128 }
129
130 /**
131 * Attempts to acquire the lock. Returns false if the lock is not available and
132 * true if the lock has been successfully acquired.
133 */
134 private synchronized bool attempt() {
135 //return true if we already own the lock
136 //also, if nobody is waiting, grant the lock immediately
137 if ((currentOperationThread is JThread.currentThread()) || (currentOperationThread is null && operations.isEmpty())) {
138 depth++;
139 setCurrentOperationThread(JThread.currentThread());
140 return true;
141 }
142 return false;
143 }
144
145 /* (non-Javadoc)
146 * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule)
147 */
148 public bool contains(ISchedulingRule rule) {
149 return false;
150 }
151
152 /**
153 * Returns null if acquired and a Semaphore object otherwise. If a
154 * waiting semaphore already exists for this thread, it will be returned,
155 * otherwise a new semaphore will be created, enqueued, and returned.
156 */
157 private synchronized Semaphore createSemaphore() {
158 return attempt() ? null : enqueue(new Semaphore(JThread.currentThread()));
159 }
160
161 /**
162 * Attempts to acquire this lock. Callers will block until this lock comes available to
163 * them, or until the specified delay has elapsed.
164 */
165 private bool doAcquire(Semaphore semaphore, long delay) {
166 bool success = false;
167 //notify hook to service pending syncExecs before falling asleep
168 if (manager.aboutToWait(this.currentOperationThread)) {
169 //hook granted immediate access
170 //remove semaphore for the lock request from the queue
171 //do not log in graph because this thread did not really get the lock
172 removeFromQueue(semaphore);
173 depth++;
174 manager.addLockThread(currentOperationThread, this);
175 return true;
176 }
177 //Make sure the semaphore is in the queue before we start waiting
178 //It might have been removed from the queue while servicing syncExecs
179 //This is will return our existing semaphore if it is still in the queue
180 semaphore = createSemaphore();
181 if (semaphore is null)
182 return true;
183 manager.addLockWaitThread(JThread.currentThread(), this);
184 try {
185 success = semaphore.acquire(delay);
186 } catch (InterruptedException e) {
187 if (DEBUG)
188 Stdout.formatln(Format("[{}] Operation interrupted while waiting... :-|", JThread.currentThread())); //$NON-NLS-1$ //$NON-NLS-2$
189 throw e;
190 }
191 if (success) {
192 depth++;
193 updateCurrentOperation();
194 } else {
195 removeFromQueue(semaphore);
196 manager.removeLockWaitThread(JThread.currentThread(), this);
197 }
198 return success;
199 }
200
201 /**
202 * Releases this lock from the thread that used to own it.
203 * Grants this lock to the next thread in the queue.
204 */
205 private synchronized void doRelease() {
206 //notify hook
207 manager.aboutToRelease();
208 depth = 0;
209 Semaphore next = cast(Semaphore) operations.peek();
210 setCurrentOperationThread(null);
211 if (next !is null)
212 next.release();
213 }
214
215 /**
216 * If there is another semaphore with the same runnable in the
217 * queue, the other is returned and the new one is not added.
218 */
219 private synchronized Semaphore enqueue(Semaphore newSemaphore) {
220 Semaphore semaphore = cast(Semaphore) operations.get(newSemaphore);
221 if (semaphore is null) {
222 operations.enqueue(newSemaphore);
223 return newSemaphore;
224 }
225 return semaphore;
226 }
227
228 /**
229 * Suspend this lock by granting the lock to the next lock in the queue.
230 * Return the depth of the suspended lock.
231 */
232 protected int forceRelease() {
233 int oldDepth = depth;
234 doRelease();
235 return oldDepth;
236 }
237 package int forceRelease_package() {
238 return forceRelease();
239 }
240
241 /* (non-Javadoc)
242 * @see Locks.ILock#getDepth()
243 */
244 public int getDepth() {
245 return depth;
246 }
247
248 /* (non-Javadoc)
249 * @see org.eclipse.core.runtime.jobs.ISchedulingRule#isConflicting(org.eclipse.core.runtime.jobs.ISchedulingRule)
250 */
251 public bool isConflicting(ISchedulingRule rule) {
252 return rule is this;
253 }
254
255 /* (non-Javadoc)
256 * @see Locks.ILock#release()
257 */
258 public void release() {
259 if (depth is 0)
260 return;
261 //only release the lock when the depth reaches zero
262 Assert.isTrue(depth >= 0, "Lock released too many times"); //$NON-NLS-1$
263 if (--depth is 0)
264 doRelease();
265 else
266 manager.removeLockThread(currentOperationThread, this);
267 }
268
269 /**
270 * Removes a semaphore from the queue of waiting operations.
271 *
272 * @param semaphore The semaphore to remove
273 */
274 private synchronized void removeFromQueue(Semaphore semaphore) {
275 operations.remove(semaphore);
276 }
277
278 /**
279 * If newThread is null, release this lock from its previous owner.
280 * If newThread is not null, grant this lock to newThread.
281 */
282 private void setCurrentOperationThread(JThread newThread) {
283 if ((currentOperationThread !is null) && (newThread is null))
284 manager.removeLockThread(currentOperationThread, this);
285 this.currentOperationThread = newThread;
286 if (currentOperationThread !is null)
287 manager.addLockThread(currentOperationThread, this);
288 }
289
290 /**
291 * Forces the lock to be at the given depth.
292 * Used when re-acquiring a suspended lock.
293 */
294 protected void setDepth(int newDepth) {
295 for (int i = depth; i < newDepth; i++) {
296 manager.addLockThread(currentOperationThread, this);
297 }
298 this.depth = newDepth;
299 }
300 package void setDepth_package(int newDepth) {
301 return setDepth(newDepth);
302 }
303
304 /**
305 * For debugging purposes only.
306 */
307 public String toString() {
308 return Format("OrderedLock ({})", number ); //$NON-NLS-1$ //$NON-NLS-2$
309 }
310
311 /**
312 * This lock has just been granted to a new thread (the thread waited for it).
313 * Remove the request from the queue and update both the graph and the lock.
314 */
315 private synchronized void updateCurrentOperation() {
316 operations.dequeue();
317 setCurrentOperationThread(JThread.currentThread());
318 }
319 }