Mercurial > projects > dwt-addons
comparison dwtx/core/internal/jobs/LockManager.d @ 122:9d0585bcb7aa
Add core.jobs package
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Tue, 12 Aug 2008 02:34:21 +0200 |
parents | |
children | 862b05e0334a |
comparison
equal
deleted
inserted
replaced
121:c0304616ea23 | 122:9d0585bcb7aa |
---|---|
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 dwtx.core.internal.jobs.LockManager; | |
14 | |
15 import tango.core.Thread; | |
16 import dwt.dwthelper.utils; | |
17 import dwtx.dwtxhelper.Collection; | |
18 | |
19 import dwtx.core.internal.runtime.RuntimeLog; | |
20 import dwtx.core.runtime.CoreException; | |
21 import dwtx.core.runtime.IStatus; | |
22 import dwtx.core.runtime.MultiStatus; | |
23 import dwtx.core.runtime.Status; | |
24 import dwtx.core.runtime.jobs.ISchedulingRule; | |
25 import dwtx.core.runtime.jobs.LockListener; | |
26 | |
27 import dwtx.core.internal.jobs.OrderedLock; | |
28 import dwtx.core.internal.jobs.DeadlockDetector; | |
29 import dwtx.core.internal.jobs.Deadlock; | |
30 import dwtx.core.internal.jobs.JobManager; | |
31 import dwtx.core.internal.jobs.Worker; | |
32 | |
33 /** | |
34 * Stores the only reference to the graph that contains all the known | |
35 * relationships between locks, rules, and the threads that own them. | |
36 * Synchronizes all access to the graph on the only instance that exists in this class. | |
37 * | |
38 * Also stores the state of suspended locks so that they can be re-acquired with | |
39 * the proper lock depth. | |
40 */ | |
41 public class LockManager { | |
42 /** | |
43 * This class captures the state of suspended locks. | |
44 * Locks are suspended if deadlock is detected. | |
45 */ | |
46 private static class LockState { | |
47 private int depth; | |
48 private OrderedLock lock; | |
49 | |
50 /** | |
51 * Suspends ownership of the given lock, and returns the saved state. | |
52 */ | |
53 protected static LockState suspend(OrderedLock lock) { | |
54 LockState state = new LockState(); | |
55 state.lock = lock; | |
56 state.depth = lock.forceRelease_package(); | |
57 return state; | |
58 } | |
59 | |
60 /** | |
61 * Re-acquires a suspended lock and reverts to the correct lock depth. | |
62 */ | |
63 public void resume() { | |
64 //spin until the lock is successfully acquired | |
65 //NOTE: spinning here allows the UI thread to service pending syncExecs | |
66 //if the UI thread is waiting to acquire a lock. | |
67 while (true) { | |
68 try { | |
69 if (lock.acquire(Long.MAX_VALUE)) | |
70 break; | |
71 } catch (InterruptedException e) { | |
72 //ignore and loop | |
73 } | |
74 } | |
75 lock.setDepth_package(depth); | |
76 } | |
77 } | |
78 | |
79 //the lock listener for this lock manager | |
80 protected LockListener lockListener; | |
81 /* | |
82 * The internal data structure that stores all the relationships | |
83 * between the locks (or rules) and the threads that own them. | |
84 */ | |
85 private DeadlockDetector locks; | |
86 /* | |
87 * Stores thread - stack pairs where every entry in the stack is an array | |
88 * of locks that were suspended while the thread was acquiring more locks | |
89 * (a stack is needed because when a thread tries to re-aquire suspended locks, | |
90 * it can cause deadlock, and some locks it owns can be suspended again) | |
91 */ | |
92 private HashMap suspendedLocks; | |
93 | |
94 public this() { | |
95 // super(); | |
96 locks = new DeadlockDetector(); | |
97 suspendedLocks = new HashMap(); | |
98 } | |
99 | |
100 /* (non-Javadoc) | |
101 * Method declared on LockListener | |
102 */ | |
103 public void aboutToRelease() { | |
104 if (lockListener is null) | |
105 return; | |
106 try { | |
107 lockListener.aboutToRelease(); | |
108 } catch (Exception e) { | |
109 handleException(e); | |
110 // } catch (LinkageError e) { | |
111 // handleException(e); | |
112 } | |
113 } | |
114 | |
115 /* (non-Javadoc) | |
116 * Method declared on LockListener | |
117 */ | |
118 public bool aboutToWait(Thread lockOwner) { | |
119 if (lockListener is null) | |
120 return false; | |
121 try { | |
122 return lockListener.aboutToWait(lockOwner); | |
123 } catch (Exception e) { | |
124 handleException(e); | |
125 // } catch (LinkageError e) { | |
126 // handleException(e); | |
127 } | |
128 return false; | |
129 } | |
130 | |
131 /** | |
132 * This thread has just acquired a lock. Update graph. | |
133 */ | |
134 void addLockThread(Thread thread, ISchedulingRule lock) { | |
135 if (locks is null) | |
136 return; | |
137 try { | |
138 synchronized (locks) { | |
139 locks.lockAcquired(thread, lock); | |
140 } | |
141 } catch (Exception e) { | |
142 handleInternalError(e); | |
143 } | |
144 } | |
145 | |
146 /** | |
147 * This thread has just been refused a lock. Update graph and check for deadlock. | |
148 */ | |
149 void addLockWaitThread(Thread thread, ISchedulingRule lock) { | |
150 if (locks is null) | |
151 return; | |
152 try { | |
153 Deadlock found = null; | |
154 synchronized (locks) { | |
155 found = locks.lockWaitStart(thread, lock); | |
156 } | |
157 if (found is null) | |
158 return; | |
159 // if deadlock was detected, the found variable will contain all the information about it, | |
160 // including which locks to suspend for which thread to resolve the deadlock. | |
161 ISchedulingRule[] toSuspend = found.getLocks(); | |
162 LockState[] suspended = new LockState[toSuspend.length]; | |
163 for (int i = 0; i < toSuspend.length; i++) | |
164 suspended[i] = LockState.suspend(cast(OrderedLock) toSuspend[i]); | |
165 synchronized (suspendedLocks) { | |
166 Stack prevLocks = cast(Stack) suspendedLocks.get(found.getCandidate()); | |
167 if (prevLocks is null) | |
168 prevLocks = new Stack(); | |
169 prevLocks.push(new ArrayWrapperObject(suspended)); | |
170 suspendedLocks.put(found.getCandidate(), prevLocks); | |
171 } | |
172 } catch (Exception e) { | |
173 handleInternalError(e); | |
174 } | |
175 } | |
176 | |
177 /** | |
178 * Handles exceptions that occur while calling third party code from within the | |
179 * LockManager. This is essentially an in-lined version of Platform.run(ISafeRunnable) | |
180 */ | |
181 private static void handleException(Exception e) { | |
182 IStatus status; | |
183 if (cast(CoreException)e ) { | |
184 //logged message should not be translated | |
185 status = new MultiStatus(JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, "LockManager.handleException", e); //$NON-NLS-1$ | |
186 (cast(MultiStatus) status).merge((cast(CoreException) e).getStatus()); | |
187 } else { | |
188 status = new Status(IStatus.ERROR, JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, "LockManager.handleException", e); //$NON-NLS-1$ | |
189 } | |
190 RuntimeLog.log(status); | |
191 } | |
192 | |
193 /** | |
194 * There was an internal error in the deadlock detection code. Shut the entire | |
195 * thing down to prevent further errors. Recovery is too complex as it | |
196 * requires freezing all threads and inferring the present lock state. | |
197 */ | |
198 private void handleInternalError(Exception t) { | |
199 try { | |
200 handleException(t); | |
201 handleException(new Exception(locks.toDebugString())); | |
202 } catch (Exception e2) { | |
203 //ignore failure to log or to create the debug string | |
204 } | |
205 //discard the deadlock detector for good | |
206 locks = null; | |
207 } | |
208 | |
209 /** | |
210 * Returns true IFF the underlying graph is empty. | |
211 * For debugging purposes only. | |
212 */ | |
213 public bool isEmpty() { | |
214 return locks.isEmpty(); | |
215 } | |
216 | |
217 /** | |
218 * Returns true IFF this thread either owns, or is waiting for, any locks or rules. | |
219 */ | |
220 public bool isLockOwner() { | |
221 //all job threads have to be treated as lock owners because UI thread | |
222 //may try to join a job | |
223 Thread current = Thread.getThis(); | |
224 if (cast(Worker)current ) | |
225 return true; | |
226 if (locks is null) | |
227 return false; | |
228 synchronized (locks) { | |
229 return locks.contains(Thread.getThis()); | |
230 } | |
231 } | |
232 | |
233 /** | |
234 * Creates and returns a new lock. | |
235 */ | |
236 public synchronized OrderedLock newLock() { | |
237 return new OrderedLock(this); | |
238 } | |
239 | |
240 /** | |
241 * Releases all the acquires that were called on the given rule. Needs to be called only once. | |
242 */ | |
243 void removeLockCompletely(Thread thread, ISchedulingRule rule) { | |
244 if (locks is null) | |
245 return; | |
246 try { | |
247 synchronized (locks) { | |
248 locks.lockReleasedCompletely(thread, rule); | |
249 } | |
250 } catch (Exception e) { | |
251 handleInternalError(e); | |
252 } | |
253 } | |
254 | |
255 /** | |
256 * This thread has just released a lock. Update graph. | |
257 */ | |
258 void removeLockThread(Thread thread, ISchedulingRule lock) { | |
259 try { | |
260 synchronized (locks) { | |
261 locks.lockReleased(thread, lock); | |
262 } | |
263 } catch (Exception e) { | |
264 handleInternalError(e); | |
265 } | |
266 } | |
267 | |
268 /** | |
269 * This thread has just stopped waiting for a lock. Update graph. | |
270 */ | |
271 void removeLockWaitThread(Thread thread, ISchedulingRule lock) { | |
272 try { | |
273 synchronized (locks) { | |
274 locks.lockWaitStop(thread, lock); | |
275 } | |
276 } catch (Exception e) { | |
277 handleInternalError(e); | |
278 } | |
279 } | |
280 | |
281 /** | |
282 * Resumes all the locks that were suspended while this thread was waiting to acquire another lock. | |
283 */ | |
284 void resumeSuspendedLocks(Thread owner) { | |
285 LockState[] toResume; | |
286 synchronized (suspendedLocks) { | |
287 Stack prevLocks = cast(Stack) suspendedLocks.get(owner); | |
288 if (prevLocks is null) | |
289 return; | |
290 toResume = arrayFromObject!(LockState)( prevLocks.pop() ); | |
291 if (prevLocks.empty()) | |
292 suspendedLocks.remove(owner); | |
293 } | |
294 for (int i = 0; i < toResume.length; i++) | |
295 toResume[i].resume(); | |
296 } | |
297 | |
298 public void setLockListener(LockListener listener) { | |
299 this.lockListener = listener; | |
300 } | |
301 } |