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 }