Mercurial > projects > dwt-addons
annotate dwtx/core/internal/jobs/ThreadJob.d @ 163:e5dd0081ccba
exclude jface.text from dsss.conf
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Mon, 08 Sep 2008 01:01:30 +0200 |
parents | 9d0585bcb7aa |
children | 862b05e0334a |
rev | line source |
---|---|
122 | 1 /******************************************************************************* |
2 * Copyright (c) 2004, 2007 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.ThreadJob; | |
14 | |
15 import dwt.dwthelper.utils; | |
16 import tango.text.convert.Format; | |
17 import tango.io.Stdout; | |
18 import tango.core.Thread; | |
19 import tango.core.sync.Mutex; | |
20 import tango.core.sync.Condition; | |
21 | |
22 import dwtx.core.internal.runtime.RuntimeLog; | |
23 import dwtx.core.runtime.Assert; | |
24 import dwtx.core.runtime.IProgressMonitor; | |
25 import dwtx.core.runtime.IStatus; | |
26 import dwtx.core.runtime.OperationCanceledException; | |
27 import dwtx.core.runtime.Status; | |
28 import dwtx.core.runtime.jobs.ISchedulingRule; | |
29 import dwtx.core.runtime.jobs.Job; | |
30 import dwtx.core.internal.jobs.JobManager; | |
31 import dwtx.core.internal.jobs.InternalJob; | |
32 import dwtx.core.internal.jobs.LockManager; | |
33 | |
34 /** | |
35 * Captures the implicit job state for a given thread. | |
36 */ | |
37 class ThreadJob : Job { | |
38 /** | |
39 * The notifier is a shared object used to wake up waiting thread jobs | |
40 * when another job completes that is releasing a scheduling rule. | |
41 */ | |
42 | |
43 static const Mutex mutex; | |
44 static const Condition condition; | |
45 static this(){ | |
46 mutex = new Mutex(); | |
47 condition = new Condition(mutex); | |
48 } | |
49 | |
50 private const JobManager manager; | |
51 /** | |
52 * Set to true if this thread job is running in a thread that did | |
53 * not own a rule already. This means it needs to acquire the | |
54 * rule during beginRule, and must release the rule during endRule. | |
55 */ | |
56 /+protected+/ bool acquireRule = false; | |
57 | |
58 /** | |
59 * Indicates that this thread job did report to the progress manager | |
60 * that it will be blocked, and therefore when it begins it must | |
61 * be reported to the job manager when it is no longer blocked. | |
62 */ | |
63 bool isBlocked = false; | |
64 | |
65 /** | |
66 * True if this ThreadJob has begun execution | |
67 */ | |
68 /+protected+/ bool isRunning_ = false; | |
69 | |
70 /** | |
71 * Used for diagnosing mismatched begin/end pairs. This field | |
72 * is only used when in debug mode, to capture the stack trace | |
73 * of the last call to beginRule. | |
74 */ | |
75 private RuntimeException lastPush = null; | |
76 /** | |
77 * The actual job that is running in the thread that this | |
78 * ThreadJob represents. This will be null if this thread | |
79 * job is capturing a rule acquired outside of a job. | |
80 */ | |
81 protected package Job realJob; | |
82 /** | |
83 * The stack of rules that have been begun in this thread, but not yet ended. | |
84 */ | |
85 private ISchedulingRule[] ruleStack; | |
86 /** | |
87 * Rule stack pointer. | |
88 */ | |
89 private int top; | |
90 | |
91 this(JobManager manager, ISchedulingRule rule) { | |
92 super("Implicit Job"); //$NON-NLS-1$ | |
93 this.manager = manager; | |
94 setSystem(true); | |
95 setPriority(Job.INTERACTIVE); | |
96 ruleStack = new ISchedulingRule[2]; | |
97 top = -1; | |
98 setRule(rule); | |
99 } | |
100 | |
101 /** | |
102 * An endRule was called that did not match the last beginRule in | |
103 * the stack. Report and log a detailed informational message. | |
104 * @param rule The rule that was popped | |
105 */ | |
106 private void illegalPop(ISchedulingRule rule) { | |
107 StringBuffer buf = new StringBuffer("Attempted to endRule: "); //$NON-NLS-1$ | |
108 buf.append(Format("{}",rule)); | |
109 if (top >= 0 && top < ruleStack.length) { | |
110 buf.append(", does not match most recent begin: "); //$NON-NLS-1$ | |
111 buf.append(Format("{}",ruleStack[top])); | |
112 } else { | |
113 if (top < 0) | |
114 buf.append(", but there was no matching beginRule"); //$NON-NLS-1$ | |
115 else | |
116 buf.append( Format(", but the rule stack was out of bounds: {}", top)); //$NON-NLS-1$ | |
117 } | |
118 buf.append(". See log for trace information if rule tracing is enabled."); //$NON-NLS-1$ | |
119 String msg = buf.toString(); | |
120 if (JobManager.DEBUG || JobManager.DEBUG_BEGIN_END) { | |
121 Stdout.formatln("{}",msg); | |
163
e5dd0081ccba
exclude jface.text from dsss.conf
Frank Benoit <benoit@tionex.de>
parents:
122
diff
changeset
|
122 Exception t = lastPush is null ? cast(Exception)new IllegalArgumentException("") : cast(Exception)lastPush; |
122 | 123 IStatus error = new Status(IStatus.ERROR, JobManager.PI_JOBS, 1, msg, t); |
124 RuntimeLog.log(error); | |
125 } | |
126 Assert.isLegal(false, msg); | |
127 } | |
128 | |
129 /** | |
130 * Client has attempted to begin a rule that is not contained within | |
131 * the outer rule. | |
132 */ | |
133 private void illegalPush(ISchedulingRule pushRule, ISchedulingRule baseRule) { | |
134 StringBuffer buf = new StringBuffer("Attempted to beginRule: "); //$NON-NLS-1$ | |
135 buf.append(Format("{}",pushRule)); | |
136 buf.append(", does not match outer scope rule: "); //$NON-NLS-1$ | |
137 buf.append(Format("{}",baseRule)); | |
138 String msg = buf.toString(); | |
139 if (JobManager.DEBUG) { | |
140 Stdout.formatln("{}",msg); | |
141 IStatus error = new Status(IStatus.ERROR, JobManager.PI_JOBS, 1, msg, new IllegalArgumentException("")); | |
142 RuntimeLog.log(error); | |
143 } | |
144 Assert.isLegal(false, msg); | |
145 | |
146 } | |
147 | |
148 /** | |
149 * Returns true if the monitor is canceled, and false otherwise. | |
150 * Protects the caller from exception in the monitor implementation. | |
151 */ | |
152 private bool isCanceled(IProgressMonitor monitor) { | |
153 try { | |
154 return monitor.isCanceled(); | |
155 } catch (RuntimeException e) { | |
156 //logged message should not be translated | |
157 IStatus status = new Status(IStatus.ERROR, JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, "ThreadJob.isCanceled", e); //$NON-NLS-1$ | |
158 RuntimeLog.log(status); | |
159 } | |
160 return false; | |
161 } | |
162 | |
163 /** | |
164 * Returns true if this thread job was scheduled and actually started running. | |
165 */ | |
166 bool isRunning() { | |
167 synchronized(mutex){ | |
168 return isRunning_; | |
169 } | |
170 } | |
171 | |
172 /** | |
173 * Schedule the job and block the calling thread until the job starts running. | |
174 * Returns the ThreadJob instance that was started. | |
175 */ | |
176 ThreadJob joinRun(IProgressMonitor monitor) { | |
177 if (isCanceled(monitor)) | |
178 throw new OperationCanceledException(); | |
179 //check if there is a blocking thread before waiting | |
180 InternalJob blockingJob = manager.findBlockingJob_package(this); | |
181 Thread blocker = blockingJob is null ? null : blockingJob.getThread_package(); | |
182 ThreadJob result = this; | |
183 try { | |
184 //just return if lock listener decided to grant immediate access | |
185 if (manager.getLockManager().aboutToWait(blocker)) | |
186 return this; | |
187 try { | |
188 waitStart(monitor, blockingJob); | |
189 final Thread getThis = Thread.getThis(); | |
190 while (true) { | |
191 if (isCanceled(monitor)) | |
192 throw new OperationCanceledException(); | |
193 //try to run the job | |
194 if (manager.runNow_package(this)) | |
195 return this; | |
196 //update blocking job | |
197 blockingJob = manager.findBlockingJob_package(this); | |
198 //the rule could have been transferred to this thread while we were waiting | |
199 blocker = blockingJob is null ? null : blockingJob.getThread_package(); | |
200 if (blocker is getThis && cast(ThreadJob)blockingJob ) { | |
201 //now we are just the nested acquire case | |
202 result = cast(ThreadJob) blockingJob; | |
203 result.push(getRule()); | |
204 result.isBlocked = this.isBlocked; | |
205 return result; | |
206 } | |
207 //just return if lock listener decided to grant immediate access | |
208 if (manager.getLockManager().aboutToWait(blocker)) | |
209 return this; | |
210 //must lock instance before calling wait | |
211 synchronized (mutex) { | |
212 try { | |
213 condition.wait(0.250); | |
214 } catch (InterruptedException e) { | |
215 //ignore | |
216 } | |
217 } | |
218 } | |
219 } finally { | |
220 if (this is result) | |
221 waitEnd(monitor); | |
222 } | |
223 } finally { | |
224 manager.getLockManager().aboutToRelease(); | |
225 } | |
226 } | |
227 | |
228 /** | |
229 * Pops a rule. Returns true if it was the last rule for this thread | |
230 * job, and false otherwise. | |
231 */ | |
232 bool pop(ISchedulingRule rule) { | |
233 if (top < 0 || ruleStack[top] !is rule) | |
234 illegalPop(rule); | |
235 ruleStack[top--] = null; | |
236 return top < 0; | |
237 } | |
238 | |
239 /** | |
240 * Adds a new scheduling rule to the stack of rules for this thread. Throws | |
241 * a runtime exception if the new rule is not compatible with the base | |
242 * scheduling rule for this thread. | |
243 */ | |
244 void push(ISchedulingRule rule) { | |
245 ISchedulingRule baseRule = getRule(); | |
246 if (++top >= ruleStack.length) { | |
247 ISchedulingRule[] newStack = new ISchedulingRule[ruleStack.length * 2]; | |
248 SimpleType!(ISchedulingRule).arraycopy(ruleStack, 0, newStack, 0, ruleStack.length); | |
249 ruleStack = newStack; | |
250 } | |
251 ruleStack[top] = rule; | |
252 if (JobManager.DEBUG_BEGIN_END) | |
253 lastPush = new RuntimeException()/+).fillInStackTrace()+/; | |
254 //check for containment last because we don't want to fail again on endRule | |
255 if (baseRule !is null && rule !is null && !baseRule.contains(rule)) | |
256 illegalPush(rule, baseRule); | |
257 } | |
258 | |
259 /** | |
260 * Reset all of this job's fields so it can be reused. Returns false if | |
261 * reuse is not possible | |
262 */ | |
263 bool recycle() { | |
264 //don't recycle if still running for any reason | |
265 if (getState() !is Job.NONE) | |
266 return false; | |
267 //clear and reset all fields | |
268 acquireRule = isRunning_ = isBlocked = false; | |
269 realJob = null; | |
270 setRule(null); | |
271 setThread(null); | |
272 if (ruleStack.length !is 2) | |
273 ruleStack = new ISchedulingRule[2]; | |
274 else | |
275 ruleStack[0] = ruleStack[1] = null; | |
276 top = -1; | |
277 return true; | |
278 } | |
279 | |
280 /** (non-Javadoc) | |
281 * @see dwtx.core.runtime.jobs.Job#run(dwtx.core.runtime.IProgressMonitor) | |
282 */ | |
283 public IStatus run(IProgressMonitor monitor) { | |
284 synchronized (this) { | |
285 isRunning_ = true; | |
286 } | |
287 return ASYNC_FINISH; | |
288 } | |
289 | |
290 /** | |
291 * Records the job that is actually running in this thread, if any | |
292 * @param realJob The running job | |
293 */ | |
294 void setRealJob(Job realJob) { | |
295 this.realJob = realJob; | |
296 } | |
297 | |
298 /** | |
299 * Returns true if this job should cause a self-canceling job | |
300 * to cancel itself, and false otherwise. | |
301 */ | |
302 bool shouldInterrupt() { | |
303 return realJob is null ? true : !realJob.isSystem(); | |
304 } | |
305 | |
306 /* (non-javadoc) | |
307 * For debugging purposes only | |
308 */ | |
309 public String toString() { | |
310 StringBuffer buf = new StringBuffer("ThreadJob"); //$NON-NLS-1$ | |
311 buf.append('(').append(Format("{}",realJob)).append(',').append('['); | |
312 for (int i = 0; i <= top && i < ruleStack.length; i++) | |
313 buf.append(Format("{}",ruleStack[i])).append(','); | |
314 buf.append(']').append(')'); | |
315 return buf.toString(); | |
316 } | |
317 | |
318 /** | |
319 * Reports that this thread was blocked, but is no longer blocked and is able | |
320 * to proceed. | |
321 * @param monitor The monitor to report unblocking to. | |
322 */ | |
323 private void waitEnd(IProgressMonitor monitor) { | |
324 final LockManager lockManager = manager.getLockManager(); | |
325 final Thread getThis = Thread.getThis(); | |
326 if (isRunning()) { | |
327 lockManager.addLockThread(getThis, getRule()); | |
328 //need to re-acquire any locks that were suspended while this thread was blocked on the rule | |
329 lockManager.resumeSuspendedLocks(getThis); | |
330 } else { | |
331 //tell lock manager that this thread gave up waiting | |
332 lockManager.removeLockWaitThread(getThis, getRule()); | |
333 } | |
334 } | |
335 | |
336 /** | |
337 * Indicates the start of a wait on a scheduling rule. Report the | |
338 * blockage to the progress manager and update the lock manager. | |
339 * @param monitor The monitor to report blocking to | |
340 * @param blockingJob The job that is blocking this thread, or <code>null</code> | |
341 */ | |
342 private void waitStart(IProgressMonitor monitor, InternalJob blockingJob) { | |
343 manager.getLockManager().addLockWaitThread(Thread.getThis(), getRule()); | |
344 isBlocked = true; | |
345 manager.reportBlocked(monitor, blockingJob); | |
346 } | |
347 } |