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