KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > internal > jobs > ThreadJob


1 /*******************************************************************************
2  * Copyright (c) 2004, 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  *******************************************************************************/

11 package org.eclipse.core.internal.jobs;
12
13 import org.eclipse.core.internal.runtime.RuntimeLog;
14 import org.eclipse.core.runtime.*;
15 import org.eclipse.core.runtime.jobs.ISchedulingRule;
16 import org.eclipse.core.runtime.jobs.Job;
17
18 /**
19  * Captures the implicit job state for a given thread.
20  */

21 class ThreadJob extends Job {
22     /**
23      * The notifier is a shared object used to wake up waiting thread jobs
24      * when another job completes that is releasing a scheduling rule.
25      */

26     static final Object JavaDoc notifier = new Object JavaDoc();
27     
28     private final JobManager manager;
29     /**
30      * Set to true if this thread job is running in a thread that did
31      * not own a rule already. This means it needs to acquire the
32      * rule during beginRule, and must release the rule during endRule.
33      */

34     protected boolean acquireRule = false;
35
36     /**
37      * Indicates that this thread job did report to the progress manager
38      * that it will be blocked, and therefore when it begins it must
39      * be reported to the job manager when it is no longer blocked.
40      */

41     boolean isBlocked = false;
42
43     /**
44      * True if this ThreadJob has begun execution
45      */

46     protected boolean isRunning = false;
47
48     /**
49      * Used for diagnosing mismatched begin/end pairs. This field
50      * is only used when in debug mode, to capture the stack trace
51      * of the last call to beginRule.
52      */

53     private RuntimeException JavaDoc lastPush = null;
54     /**
55      * The actual job that is running in the thread that this
56      * ThreadJob represents. This will be null if this thread
57      * job is capturing a rule acquired outside of a job.
58      */

59     protected Job realJob;
60     /**
61      * The stack of rules that have been begun in this thread, but not yet ended.
62      */

63     private ISchedulingRule[] ruleStack;
64     /**
65      * Rule stack pointer.
66      */

67     private int top;
68
69     ThreadJob(JobManager manager, ISchedulingRule rule) {
70         super("Implicit Job"); //$NON-NLS-1$
71
this.manager = manager;
72         setSystem(true);
73         setPriority(Job.INTERACTIVE);
74         ruleStack = new ISchedulingRule[2];
75         top = -1;
76         setRule(rule);
77     }
78
79     /**
80      * An endRule was called that did not match the last beginRule in
81      * the stack. Report and log a detailed informational message.
82      * @param rule The rule that was popped
83      */

84     private void illegalPop(ISchedulingRule rule) {
85         StringBuffer JavaDoc buf = new StringBuffer JavaDoc("Attempted to endRule: "); //$NON-NLS-1$
86
buf.append(rule);
87         if (top >= 0 && top < ruleStack.length) {
88             buf.append(", does not match most recent begin: "); //$NON-NLS-1$
89
buf.append(ruleStack[top]);
90         } else {
91             if (top < 0)
92                 buf.append(", but there was no matching beginRule"); //$NON-NLS-1$
93
else
94                 buf.append(", but the rule stack was out of bounds: " + top); //$NON-NLS-1$
95
}
96         buf.append(". See log for trace information if rule tracing is enabled."); //$NON-NLS-1$
97
String JavaDoc msg = buf.toString();
98         if (JobManager.DEBUG || JobManager.DEBUG_BEGIN_END) {
99             System.out.println(msg);
100             Throwable JavaDoc t = lastPush == null ? new IllegalArgumentException JavaDoc() : lastPush;
101             IStatus error = new Status(IStatus.ERROR, JobManager.PI_JOBS, 1, msg, t);
102             RuntimeLog.log(error);
103         }
104         Assert.isLegal(false, msg);
105     }
106
107     /**
108      * Client has attempted to begin a rule that is not contained within
109      * the outer rule.
110      */

111     private void illegalPush(ISchedulingRule pushRule, ISchedulingRule baseRule) {
112         StringBuffer JavaDoc buf = new StringBuffer JavaDoc("Attempted to beginRule: "); //$NON-NLS-1$
113
buf.append(pushRule);
114         buf.append(", does not match outer scope rule: "); //$NON-NLS-1$
115
buf.append(baseRule);
116         String JavaDoc msg = buf.toString();
117         if (JobManager.DEBUG) {
118             System.out.println(msg);
119             IStatus error = new Status(IStatus.ERROR, JobManager.PI_JOBS, 1, msg, new IllegalArgumentException JavaDoc());
120             RuntimeLog.log(error);
121         }
122         Assert.isLegal(false, msg);
123
124     }
125
126     /**
127      * Returns true if the monitor is canceled, and false otherwise.
128      * Protects the caller from exception in the monitor implementation.
129      */

130     private boolean isCanceled(IProgressMonitor monitor) {
131         try {
132             return monitor.isCanceled();
133         } catch (RuntimeException JavaDoc e) {
134             //logged message should not be translated
135
IStatus status = new Status(IStatus.ERROR, JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, "ThreadJob.isCanceled", e); //$NON-NLS-1$
136
RuntimeLog.log(status);
137         }
138         return false;
139     }
140
141     /**
142      * Returns true if this thread job was scheduled and actually started running.
143      */

144     synchronized boolean isRunning() {
145         return isRunning;
146     }
147     
148     /**
149      * Schedule the job and block the calling thread until the job starts running.
150      * Returns the ThreadJob instance that was started.
151      */

152     ThreadJob joinRun(IProgressMonitor monitor) {
153         if (isCanceled(monitor))
154             throw new OperationCanceledException();
155         //check if there is a blocking thread before waiting
156
InternalJob blockingJob = manager.findBlockingJob(this);
157         Thread JavaDoc blocker = blockingJob == null ? null : blockingJob.getThread();
158         ThreadJob result = this;
159         try {
160             //just return if lock listener decided to grant immediate access
161
if (manager.getLockManager().aboutToWait(blocker))
162                 return this;
163             try {
164                 waitStart(monitor, blockingJob);
165                 final Thread JavaDoc currentThread = Thread.currentThread();
166                 while (true) {
167                     if (isCanceled(monitor))
168                         throw new OperationCanceledException();
169                     //try to run the job
170
if (manager.runNow(this))
171                         return this;
172                     //update blocking job
173
blockingJob = manager.findBlockingJob(this);
174                     //the rule could have been transferred to this thread while we were waiting
175
blocker = blockingJob == null ? null : blockingJob.getThread();
176                     if (blocker == currentThread && blockingJob instanceof ThreadJob) {
177                         //now we are just the nested acquire case
178
result = (ThreadJob)blockingJob;
179                         result.push(getRule());
180                         result.isBlocked = this.isBlocked;
181                         return result;
182                     }
183                     //just return if lock listener decided to grant immediate access
184
if (manager.getLockManager().aboutToWait(blocker))
185                         return this;
186                     //must lock instance before calling wait
187
synchronized (notifier) {
188                         try {
189                             notifier.wait(250);
190                         } catch (InterruptedException JavaDoc e) {
191                             //ignore
192
}
193                     }
194                 }
195             } finally {
196                 if (this == result)
197                     waitEnd(monitor);
198             }
199         } finally {
200             manager.getLockManager().aboutToRelease();
201         }
202     }
203
204     /**
205      * Pops a rule. Returns true if it was the last rule for this thread
206      * job, and false otherwise.
207      */

208     boolean pop(ISchedulingRule rule) {
209         if (top < 0 || ruleStack[top] != rule)
210             illegalPop(rule);
211         ruleStack[top--] = null;
212         return top < 0;
213     }
214
215     /**
216      * Adds a new scheduling rule to the stack of rules for this thread. Throws
217      * a runtime exception if the new rule is not compatible with the base
218      * scheduling rule for this thread.
219      */

220     void push(final ISchedulingRule rule) {
221         final ISchedulingRule baseRule = getRule();
222         if (++top >= ruleStack.length) {
223             ISchedulingRule[] newStack = new ISchedulingRule[ruleStack.length * 2];
224             System.arraycopy(ruleStack, 0, newStack, 0, ruleStack.length);
225             ruleStack = newStack;
226         }
227         ruleStack[top] = rule;
228         if (JobManager.DEBUG_BEGIN_END)
229             lastPush = (RuntimeException JavaDoc) new RuntimeException JavaDoc().fillInStackTrace();
230         //check for containment last because we don't want to fail again on endRule
231
if (baseRule != null && rule != null && !baseRule.contains(rule))
232             illegalPush(rule, baseRule);
233     }
234
235     /**
236      * Reset all of this job's fields so it can be reused. Returns false if
237      * reuse is not possible
238      */

239     boolean recycle() {
240         //don't recycle if still running for any reason
241
if (getState() != Job.NONE)
242             return false;
243         //clear and reset all fields
244
acquireRule = isRunning = isBlocked = false;
245         realJob = null;
246         setRule(null);
247         setThread(null);
248         if (ruleStack.length != 2)
249             ruleStack = new ISchedulingRule[2];
250         else
251             ruleStack[0] = ruleStack[1] = null;
252         top = -1;
253         return true;
254     }
255
256     /** (non-Javadoc)
257      * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
258      */

259     public IStatus run(IProgressMonitor monitor) {
260         synchronized (this) {
261             isRunning = true;
262         }
263         return ASYNC_FINISH;
264     }
265
266     /**
267      * Records the job that is actually running in this thread, if any
268      * @param realJob The running job
269      */

270     void setRealJob(Job realJob) {
271         this.realJob = realJob;
272     }
273
274     /**
275      * Returns true if this job should cause a self-canceling job
276      * to cancel itself, and false otherwise.
277      */

278     boolean shouldInterrupt() {
279         return realJob == null ? true : !realJob.isSystem();
280     }
281
282     /* (non-javadoc)
283      * For debugging purposes only
284      */

285     public String JavaDoc toString() {
286         StringBuffer JavaDoc buf = new StringBuffer JavaDoc("ThreadJob"); //$NON-NLS-1$
287
buf.append('(').append(realJob).append(',').append('[');
288         for (int i = 0; i <= top && i < ruleStack.length; i++)
289             buf.append(ruleStack[i]).append(',');
290         buf.append(']').append(')');
291         return buf.toString();
292     }
293
294     /**
295      * Reports that this thread was blocked, but is no longer blocked and is able
296      * to proceed.
297      * @param monitor The monitor to report unblocking to.
298      */

299     private void waitEnd(IProgressMonitor monitor) {
300         final LockManager lockManager = manager.getLockManager();
301         final Thread JavaDoc currentThread = Thread.currentThread();
302         if (isRunning()) {
303             lockManager.addLockThread(currentThread, getRule());
304             //need to re-acquire any locks that were suspended while this thread was blocked on the rule
305
lockManager.resumeSuspendedLocks(currentThread);
306         } else {
307             //tell lock manager that this thread gave up waiting
308
lockManager.removeLockWaitThread(currentThread, getRule());
309         }
310     }
311
312     /**
313      * Indicates the start of a wait on a scheduling rule. Report the
314      * blockage to the progress manager and update the lock manager.
315      * @param monitor The monitor to report blocking to
316      * @param blockingJob The job that is blocking this thread, or <code>null</code>
317      */

318     private void waitStart(IProgressMonitor monitor, InternalJob blockingJob) {
319         manager.getLockManager().addLockWaitThread(Thread.currentThread(), getRule());
320         isBlocked = true;
321         manager.reportBlocked(monitor, blockingJob);
322     }
323 }
Popular Tags