Mercurial > projects > dwt-addons
annotate dwtx/core/internal/jobs/JobManager.d @ 192:c3583c6ec027
Added missing default cases for switch statements
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Mon, 03 Nov 2008 22:52:26 +0100 |
parents | 862b05e0334a |
children |
rev | line source |
---|---|
122 | 1 /******************************************************************************* |
2 * Copyright (c) 2003, 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 Corporation - initial API and implementation | |
10 * Port to the D programming language: | |
11 * Frank Benoit <benoit@tionex.de> | |
12 *******************************************************************************/ | |
13 module dwtx.core.internal.jobs.JobManager; | |
14 | |
15 import dwt.dwthelper.utils; | |
16 import dwtx.dwtxhelper.Collection; | |
17 import tango.io.Stdout; | |
18 import tango.text.convert.Format; | |
19 import tango.time.WallClock; | |
20 import tango.time.Time; | |
167 | 21 import dwtx.dwtxhelper.JThread; |
122 | 22 import tango.text.convert.Format; |
23 | |
24 //don't use ICU because this is used for debugging only (see bug 135785) | |
25 // import java.text.DateFormat; | |
26 // import java.text.FieldPosition; | |
27 // import java.text.SimpleDateFormat; | |
28 | |
29 import dwtx.core.internal.runtime.RuntimeLog; | |
30 import dwtx.core.runtime.Assert; | |
31 import dwtx.core.runtime.IProgressMonitor; | |
32 import dwtx.core.runtime.IProgressMonitorWithBlocking; | |
33 import dwtx.core.runtime.IStatus; | |
34 import dwtx.core.runtime.NullProgressMonitor; | |
35 import dwtx.core.runtime.OperationCanceledException; | |
36 import dwtx.core.runtime.Status; | |
37 import dwtx.core.runtime.jobs.IJobChangeEvent; | |
38 import dwtx.core.runtime.jobs.IJobChangeListener; | |
39 import dwtx.core.runtime.jobs.IJobManager; | |
40 import dwtx.core.runtime.jobs.ILock; | |
41 import dwtx.core.runtime.jobs.ISchedulingRule; | |
42 import dwtx.core.runtime.jobs.Job; | |
43 import dwtx.core.runtime.jobs.JobChangeAdapter; | |
44 import dwtx.core.runtime.jobs.LockListener; | |
45 import dwtx.core.runtime.jobs.ProgressProvider; | |
46 import dwtx.osgi.util.NLS; | |
47 | |
48 import dwtx.core.internal.jobs.ImplicitJobs; | |
49 import dwtx.core.internal.jobs.WorkerPool; | |
50 import dwtx.core.internal.jobs.JobListeners; | |
51 import dwtx.core.internal.jobs.LockManager; | |
52 import dwtx.core.internal.jobs.JobQueue; | |
53 import dwtx.core.internal.jobs.InternalJob; | |
54 import dwtx.core.internal.jobs.ThreadJob; | |
55 import dwtx.core.internal.jobs.JobOSGiUtils; | |
56 import dwtx.core.internal.jobs.Worker; | |
57 import dwtx.core.internal.jobs.Semaphore; | |
58 import dwtx.core.internal.jobs.JobChangeEvent; | |
59 import dwtx.core.internal.jobs.JobMessages; | |
60 import dwtx.core.internal.jobs.JobStatus; | |
61 | |
62 /** | |
63 * Implementation of API type IJobManager | |
64 * | |
65 * Implementation note: all the data structures of this class are protected | |
66 * by a single lock object held as a private field in this class. The JobManager | |
67 * instance itself is not used because this class is publicly reachable, and third | |
68 * party clients may try to synchronize on it. | |
69 * | |
70 * The WorkerPool class uses its own monitor for synchronizing its data | |
71 * structures. To avoid deadlock between the two classes, the JobManager | |
72 * must NEVER call the worker pool while its own monitor is held. | |
73 */ | |
74 public class JobManager : IJobManager { | |
75 | |
76 /** | |
77 * The unique identifier constant of this plug-in. | |
78 */ | |
79 public static const String PI_JOBS = "dwtx.core.jobs"; //$NON-NLS-1$ | |
80 | |
81 /** | |
82 * Status code constant indicating an error occurred while running a plug-in. | |
83 * For backward compatibility with Platform.PLUGIN_ERROR left at (value = 2). | |
84 */ | |
85 public static const int PLUGIN_ERROR = 2; | |
86 | |
87 private static const String OPTION_DEADLOCK_ERROR = PI_JOBS ~ "/jobs/errorondeadlock"; //$NON-NLS-1$ | |
88 private static const String OPTION_DEBUG_BEGIN_END = PI_JOBS ~ "/jobs/beginend"; //$NON-NLS-1$ | |
89 private static const String OPTION_DEBUG_JOBS = PI_JOBS ~ "/jobs"; //$NON-NLS-1$ | |
90 private static const String OPTION_DEBUG_JOBS_TIMING = PI_JOBS ~ "/jobs/timing"; //$NON-NLS-1$ | |
91 private static const String OPTION_LOCKS = PI_JOBS ~ "/jobs/locks"; //$NON-NLS-1$ | |
92 private static const String OPTION_SHUTDOWN = PI_JOBS ~ "/jobs/shutdown"; //$NON-NLS-1$ | |
93 | |
94 static bool DEBUG = false; | |
95 static bool DEBUG_BEGIN_END = false; | |
96 static bool DEBUG_DEADLOCK = false; | |
97 static bool DEBUG_LOCKS = false; | |
98 static bool DEBUG_TIMING = false; | |
99 static bool DEBUG_SHUTDOWN = false; | |
100 // private static DateFormat DEBUG_FORMAT; | |
101 | |
102 /** | |
103 * The singleton job manager instance. It must be a singleton because | |
104 * all job instances maintain a reference (as an optimization) and have no way | |
105 * of updating it. | |
106 */ | |
107 private static JobManager instance = null; | |
108 /** | |
109 * Scheduling rule used for validation of client-defined rules. | |
110 */ | |
111 private static ISchedulingRule nullRule; | |
112 private static void initNullRule(){ | |
113 if( nullRule !is null ) return; | |
114 nullRule = new class ISchedulingRule { | |
115 public bool contains(ISchedulingRule rule) { | |
116 return rule is this; | |
117 } | |
118 | |
119 public bool isConflicting(ISchedulingRule rule) { | |
120 return rule is this; | |
121 } | |
122 }; | |
123 } | |
124 | |
125 /** | |
126 * True if this manager is active, and false otherwise. A job manager | |
127 * starts out active, and becomes inactive if it has been shutdown | |
128 * and not restarted. | |
129 */ | |
130 private /+volatile+/ bool active = true; | |
131 | |
132 const ImplicitJobs implicitJobs; | |
133 | |
134 private const JobListeners jobListeners; | |
135 | |
136 /** | |
137 * The lock for synchronizing all activity in the job manager. To avoid deadlock, | |
138 * this lock must never be held for extended periods, and must never be | |
139 * held while third party code is being called. | |
140 */ | |
141 private const Object lock; | |
142 | |
143 private const LockManager lockManager; | |
144 | |
145 /** | |
146 * The pool of worker threads. | |
147 */ | |
148 private WorkerPool pool; | |
149 | |
150 private ProgressProvider progressProvider = null; | |
151 /** | |
152 * Jobs that are currently running. Should only be modified from changeState | |
153 */ | |
154 private const HashSet running; | |
155 | |
156 /** | |
157 * Jobs that are sleeping. Some sleeping jobs are scheduled to wake | |
158 * up at a given start time, while others will sleep indefinitely until woken. | |
159 * Should only be modified from changeState | |
160 */ | |
161 private const JobQueue sleeping; | |
162 /** | |
163 * True if this manager has been suspended, and false otherwise. A job manager | |
164 * starts out not suspended, and becomes suspended when <code>suspend</code> | |
165 * is invoked. Once suspended, no jobs will start running until <code>resume</code> | |
166 * is called. | |
167 */ | |
168 private bool suspended = false; | |
169 | |
170 /** | |
171 * jobs that are waiting to be run. Should only be modified from changeState | |
172 */ | |
173 private const JobQueue waiting; | |
174 | |
175 /** | |
176 * Counter to record wait queue insertion order. | |
177 */ | |
178 private long waitQueueCounter; | |
179 | |
180 public static void debug_(String msg) { | |
181 StringBuffer msgBuf = new StringBuffer(msg.length + 40); | |
182 if (DEBUG_TIMING) { | |
183 //lazy initialize to avoid overhead when not debugging | |
184 // if (DEBUG_FORMAT is null) | |
185 // DEBUG_FORMAT = new SimpleDateFormat("HH:mm:ss.SSS"); //$NON-NLS-1$ | |
186 // DEBUG_FORMAT.format(new Date(), msgBuf, new FieldPosition(0)); | |
187 auto time = WallClock.now(); | |
188 msgBuf.append(Format("{:d2}:{:d2}:{:d2}:{:d3}", | |
189 time.time.span.hours, | |
190 time.time.span.minutes, | |
191 time.time.span.seconds, | |
192 time.time.span.millis )); | |
193 msgBuf.append('-'); | |
194 } | |
195 msgBuf.append('['); | |
167 | 196 msgBuf.append(JThread.currentThread().toString()); |
122 | 197 msgBuf.append(']'); |
198 msgBuf.append(msg); | |
199 Stdout.formatln( "{}", msgBuf.toString()); | |
200 } | |
201 | |
202 /** | |
203 * Returns the job manager singleton. For internal use only. | |
204 */ | |
205 static synchronized JobManager getInstance() { | |
206 if (instance is null) | |
207 new JobManager(); | |
208 return instance; | |
209 } | |
210 | |
211 /** | |
212 * For debugging purposes only | |
213 */ | |
214 private static String printJobName(Job job) { | |
215 if (cast(ThreadJob)job ) { | |
216 Job realJob = (cast(ThreadJob) job).realJob; | |
217 if (realJob !is null) | |
218 return realJob.classinfo.name; | |
219 return Format("ThreadJob on rule: {}", job.getRule()); //$NON-NLS-1$ | |
220 } | |
221 return job.classinfo.name; | |
222 } | |
223 | |
224 /** | |
225 * For debugging purposes only | |
226 */ | |
227 public static String printState(int state) { | |
228 switch (state) { | |
229 case Job.NONE : | |
230 return "NONE"; //$NON-NLS-1$ | |
231 case Job.WAITING : | |
232 return "WAITING"; //$NON-NLS-1$ | |
233 case Job.SLEEPING : | |
234 return "SLEEPING"; //$NON-NLS-1$ | |
235 case Job.RUNNING : | |
236 return "RUNNING"; //$NON-NLS-1$ | |
237 case InternalJob.BLOCKED : | |
238 return "BLOCKED"; //$NON-NLS-1$ | |
239 case InternalJob.ABOUT_TO_RUN : | |
240 return "ABOUT_TO_RUN"; //$NON-NLS-1$ | |
241 case InternalJob.ABOUT_TO_SCHEDULE : | |
242 return "ABOUT_TO_SCHEDULE";//$NON-NLS-1$ | |
192
c3583c6ec027
Added missing default cases for switch statements
Frank Benoit <benoit@tionex.de>
parents:
167
diff
changeset
|
243 default: |
122 | 244 } |
245 return "UNKNOWN"; //$NON-NLS-1$ | |
246 } | |
247 | |
248 /** | |
249 * Note that although this method is not API, clients have historically used | |
250 * it to force jobs shutdown in cases where OSGi shutdown does not occur. | |
251 * For this reason, this method should be considered near-API and should not | |
252 * be changed if at all possible. | |
253 */ | |
254 public static void shutdown() { | |
255 if (instance !is null) { | |
256 instance.doShutdown(); | |
257 instance = null; | |
258 } | |
259 } | |
260 | |
261 private this() { | |
262 // DWT instance init | |
263 implicitJobs = new ImplicitJobs(this); | |
264 jobListeners = new JobListeners(); | |
265 lock = new Object(); | |
266 lockManager = new LockManager(); | |
267 | |
268 instance = this; | |
269 | |
270 initDebugOptions(); | |
271 synchronized (lock) { | |
272 waiting = new JobQueue(false); | |
273 sleeping = new JobQueue(true); | |
274 running = new HashSet(10); | |
275 pool = new WorkerPool(this); | |
276 } | |
277 pool.setDaemon(JobOSGiUtils.getDefault().useDaemonThreads()); | |
278 } | |
279 | |
280 /* (non-Javadoc) | |
281 * @see dwtx.core.runtime.jobs.IJobManager#addJobListener(dwtx.core.runtime.jobs.IJobChangeListener) | |
282 */ | |
283 public void addJobChangeListener(IJobChangeListener listener) { | |
284 jobListeners.add(listener); | |
285 } | |
286 | |
287 /* (non-Javadoc) | |
288 * @see dwtx.core.runtime.jobs.IJobManager#beginRule(dwtx.core.runtime.jobs.ISchedulingRule, dwtx.core.runtime.IProgressMonitor) | |
289 */ | |
290 public void beginRule(ISchedulingRule rule, IProgressMonitor monitor) { | |
291 validateRule(rule); | |
292 implicitJobs.begin(rule, monitorFor(monitor), false); | |
293 } | |
294 | |
295 /** | |
296 * Cancels a job | |
297 */ | |
298 protected bool cancel(InternalJob job) { | |
299 IProgressMonitor monitor = null; | |
300 synchronized (lock) { | |
301 switch (job.getState_package()) { | |
302 case Job.NONE : | |
303 return true; | |
304 case Job.RUNNING : | |
305 //cannot cancel a job that has already started (as opposed to ABOUT_TO_RUN) | |
306 if (job.internalGetState() is Job.RUNNING) { | |
307 monitor = job.getProgressMonitor(); | |
308 break; | |
309 } | |
310 //signal that the job should be canceled before it gets a chance to run | |
311 job.setAboutToRunCanceled(true); | |
312 return true; | |
313 default : | |
314 changeState(job, Job.NONE); | |
315 } | |
316 } | |
317 //call monitor outside sync block | |
318 if (monitor !is null) { | |
319 if (!monitor.isCanceled()) { | |
320 monitor.setCanceled(true); | |
321 job.canceling(); | |
322 } | |
323 return false; | |
324 } | |
325 //only notify listeners if the job was waiting or sleeping | |
326 jobListeners.done(cast(Job) job, Status.CANCEL_STATUS, false); | |
327 return true; | |
328 } | |
329 package bool cancel_package(InternalJob job) { | |
330 return cancel(job); | |
331 } | |
332 | |
333 /* (non-Javadoc) | |
334 * @see dwtx.core.runtime.jobs.IJobManager#cancel(java.lang.String) | |
335 */ | |
336 public void cancel(Object family) { | |
337 //don't synchronize because cancel calls listeners | |
338 for (Iterator it = select(family).iterator(); it.hasNext();) | |
339 cancel(cast(InternalJob) it.next()); | |
340 } | |
341 | |
342 /** | |
343 * Atomically updates the state of a job, adding or removing from the | |
344 * necessary queues or sets. | |
345 */ | |
346 private void changeState(InternalJob job, int newState) { | |
347 bool blockedJobs = false; | |
348 synchronized (lock) { | |
349 int oldState = job.internalGetState(); | |
350 switch (oldState) { | |
351 case Job.NONE : | |
352 case InternalJob.ABOUT_TO_SCHEDULE : | |
353 break; | |
354 case InternalJob.BLOCKED : | |
355 //remove this job from the linked list of blocked jobs | |
356 job.remove(); | |
357 break; | |
358 case Job.WAITING : | |
359 try { | |
360 waiting.remove(job); | |
361 } catch (RuntimeException e) { | |
362 Assert.isLegal(false, "Tried to remove a job that wasn't in the queue"); //$NON-NLS-1$ | |
363 } | |
364 break; | |
365 case Job.SLEEPING : | |
366 try { | |
367 sleeping.remove(job); | |
368 } catch (RuntimeException e) { | |
369 Assert.isLegal(false, "Tried to remove a job that wasn't in the queue"); //$NON-NLS-1$ | |
370 } | |
371 break; | |
372 case Job.RUNNING : | |
373 case InternalJob.ABOUT_TO_RUN : | |
374 running.remove(job); | |
375 //add any blocked jobs back to the wait queue | |
376 InternalJob blocked = job.previous(); | |
377 job.remove(); | |
378 blockedJobs = blocked !is null; | |
379 while (blocked !is null) { | |
380 InternalJob previous = blocked.previous(); | |
381 changeState(blocked, Job.WAITING); | |
382 blocked = previous; | |
383 } | |
384 break; | |
385 default : | |
386 Assert.isLegal(false, Format("Invalid job state: {}, state: {}", job, oldState)); //$NON-NLS-1$ //$NON-NLS-2$ | |
387 } | |
388 job.internalSetState(newState); | |
389 switch (newState) { | |
390 case Job.NONE : | |
391 job.setStartTime(InternalJob.T_NONE); | |
392 job.setWaitQueueStamp(InternalJob.T_NONE); | |
393 case InternalJob.BLOCKED : | |
394 break; | |
395 case Job.WAITING : | |
396 waiting.enqueue(job); | |
397 break; | |
398 case Job.SLEEPING : | |
399 try { | |
400 sleeping.enqueue(job); | |
401 } catch (RuntimeException e) { | |
402 throw new RuntimeException(Format("Error changing from state: ", oldState)); //$NON-NLS-1$ | |
403 } | |
404 break; | |
405 case Job.RUNNING : | |
406 case InternalJob.ABOUT_TO_RUN : | |
407 job.setStartTime(InternalJob.T_NONE); | |
408 job.setWaitQueueStamp(InternalJob.T_NONE); | |
409 running.add(job); | |
410 break; | |
411 case InternalJob.ABOUT_TO_SCHEDULE : | |
412 break; | |
413 default : | |
414 Assert.isLegal(false, Format("Invalid job state: {}, state: {}", job, newState)); //$NON-NLS-1$ //$NON-NLS-2$ | |
415 } | |
416 } | |
417 //notify queue outside sync block | |
418 if (blockedJobs) | |
419 pool.jobQueued(); | |
420 } | |
421 | |
422 /** | |
423 * Returns a new progress monitor for this job, belonging to the given | |
424 * progress group. Returns null if it is not a valid time to set the job's group. | |
425 */ | |
426 protected IProgressMonitor createMonitor(InternalJob job, IProgressMonitor group, int ticks) { | |
427 synchronized (lock) { | |
428 //group must be set before the job is scheduled | |
429 //this includes the ABOUT_TO_SCHEDULE state, during which it is still | |
430 //valid to set the progress monitor | |
431 if (job.getState_package() !is Job.NONE) | |
432 return null; | |
433 IProgressMonitor monitor = null; | |
434 if (progressProvider !is null) | |
435 monitor = progressProvider.createMonitor(cast(Job) job, group, ticks); | |
436 if (monitor is null) | |
437 monitor = new NullProgressMonitor(); | |
438 return monitor; | |
439 } | |
440 } | |
441 package IProgressMonitor createMonitor_package(InternalJob job, IProgressMonitor group, int ticks) { | |
442 return createMonitor(job, group, ticks); | |
443 } | |
444 | |
445 /** | |
446 * Returns a new progress monitor for this job. Never returns null. | |
447 */ | |
448 private IProgressMonitor createMonitor(Job job) { | |
449 IProgressMonitor monitor = null; | |
450 if (progressProvider !is null) | |
451 monitor = progressProvider.createMonitor(job); | |
452 if (monitor is null) | |
453 monitor = new NullProgressMonitor(); | |
454 return monitor; | |
455 } | |
456 | |
457 /* (non-Javadoc) | |
458 * @see dwtx.core.runtime.jobs.IJobManager#createProgressGroup() | |
459 */ | |
460 public IProgressMonitor createProgressGroup() { | |
461 if (progressProvider !is null) | |
462 return progressProvider.createProgressGroup(); | |
463 return new NullProgressMonitor(); | |
464 } | |
465 | |
466 /* (non-Javadoc) | |
467 * @see dwtx.core.runtime.jobs.IJobManager#currentJob() | |
468 */ | |
469 public Job currentJob() { | |
167 | 470 JThread current = JThread.currentThread(); |
122 | 471 if (cast(Worker)current ) |
472 return (cast(Worker) current).currentJob(); | |
473 synchronized (lock) { | |
474 for (Iterator it = running.iterator(); it.hasNext();) { | |
475 Job job = cast(Job) it.next(); | |
476 if (job.getThread() is current) | |
477 return job; | |
478 } | |
479 } | |
480 return null; | |
481 } | |
482 | |
483 /** | |
484 * Returns the delay in milliseconds that a job with a given priority can | |
485 * tolerate waiting. | |
486 */ | |
487 private long delayFor(int priority) { | |
488 //these values may need to be tweaked based on machine speed | |
489 switch (priority) { | |
490 case Job.INTERACTIVE : | |
491 return 0L; | |
492 case Job.SHORT : | |
493 return 50L; | |
494 case Job.LONG : | |
495 return 100L; | |
496 case Job.BUILD : | |
497 return 500L; | |
498 case Job.DECORATE : | |
499 return 1000L; | |
500 default : | |
501 Assert.isTrue(false, Format("Job has invalid priority: {}", priority)); //$NON-NLS-1$ | |
502 return 0; | |
503 } | |
504 } | |
505 | |
506 /** | |
507 * Performs the scheduling of a job. Does not perform any notifications. | |
508 */ | |
509 private void doSchedule(InternalJob job, long delay) { | |
510 synchronized (lock) { | |
511 //if it's a decoration job with no rule, don't run it right now if the system is busy | |
512 if (job.getPriority() is Job.DECORATE && job.getRule() is null) { | |
513 long minDelay = running.size() * 100; | |
514 delay = Math.max(delay, minDelay); | |
515 } | |
516 if (delay > 0) { | |
517 job.setStartTime(System.currentTimeMillis() + delay); | |
518 changeState(job, Job.SLEEPING); | |
519 } else { | |
520 job.setStartTime(System.currentTimeMillis() + delayFor(job.getPriority())); | |
521 job.setWaitQueueStamp(waitQueueCounter++); | |
522 changeState(job, Job.WAITING); | |
523 } | |
524 } | |
525 } | |
526 | |
527 /** | |
528 * Shuts down the job manager. Currently running jobs will be told | |
529 * to stop, but worker threads may still continue processing. | |
530 * (note: This implemented IJobManager.shutdown which was removed | |
531 * due to problems caused by premature shutdown) | |
532 */ | |
533 private void doShutdown() { | |
534 Job[] toCancel = null; | |
535 synchronized (lock) { | |
536 if (active) { | |
537 active = false; | |
538 //cancel all running jobs | |
539 toCancel = arraycast!(Job)( running.toArray()); | |
540 //clean up | |
541 sleeping.clear(); | |
542 waiting.clear(); | |
543 running.clear(); | |
544 } | |
545 } | |
546 | |
547 // Give running jobs a chance to finish. Wait 0.1 seconds for up to 3 times. | |
548 if (toCancel !is null && toCancel.length > 0) { | |
549 for (int i = 0; i < toCancel.length; i++) { | |
550 cancel(cast(InternalJob)toCancel[i]); // cancel jobs outside sync block to avoid deadlock | |
551 } | |
552 | |
553 for (int waitAttempts = 0; waitAttempts < 3; waitAttempts++) { | |
167 | 554 JThread.yield(); |
122 | 555 synchronized (lock) { |
556 if (running.isEmpty()) | |
557 break; | |
558 } | |
559 if (DEBUG_SHUTDOWN) { | |
560 JobManager.debug_(Format("Shutdown - job wait cycle #{}", (waitAttempts + 1))); //$NON-NLS-1$ | |
561 Job[] stillRunning = null; | |
562 synchronized (lock) { | |
563 stillRunning = arraycast!(Job)( running.toArray()); | |
564 } | |
565 if (stillRunning !is null) { | |
566 for (int j = 0; j < stillRunning.length; j++) { | |
567 JobManager.debug_(Format("\tJob: {}", printJobName(stillRunning[j]))); //$NON-NLS-1$ | |
568 } | |
569 } | |
570 } | |
571 try { | |
167 | 572 JThread.sleep(100); |
122 | 573 } catch (InterruptedException e) { |
574 //ignore | |
575 } | |
167 | 576 JThread.yield(); |
122 | 577 } |
578 | |
579 synchronized (lock) { // retrieve list of the jobs that are still running | |
580 toCancel = arraycast!(Job)( running.toArray()); | |
581 } | |
582 } | |
583 | |
584 if (toCancel !is null) { | |
585 for (int i = 0; i < toCancel.length; i++) { | |
586 String jobName = printJobName(toCancel[i]); | |
587 //this doesn't need to be translated because it's just being logged | |
588 String msg = "Job found still running after platform shutdown. Jobs should be canceled by the plugin that scheduled them during shutdown: " ~ jobName; //$NON-NLS-1$ | |
589 RuntimeLog.log(new Status(IStatus.WARNING, JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, msg, null)); | |
590 | |
591 // TODO the RuntimeLog.log in its current implementation won't produce a log | |
592 // during this stage of shutdown. For now add a standard error output. | |
593 // One the logging story is improved, the System.err output below can be removed: | |
594 Stderr.formatln("{}", msg); | |
595 } | |
596 } | |
597 | |
598 pool.shutdown_package(); | |
599 } | |
600 | |
601 /** | |
602 * Indicates that a job was running, and has now finished. Note that this method | |
603 * can be called under OutOfMemoryError conditions and thus must be paranoid | |
604 * about allocating objects. | |
605 */ | |
606 protected void endJob(InternalJob job, IStatus result, bool notify) { | |
607 long rescheduleDelay = InternalJob.T_NONE; | |
608 synchronized (lock) { | |
609 //if the job is finishing asynchronously, there is nothing more to do for now | |
610 if (result is Job.ASYNC_FINISH) | |
611 return; | |
612 //if job is not known then it cannot be done | |
613 if (job.getState_package() is Job.NONE) | |
614 return; | |
615 if (JobManager.DEBUG && notify) | |
616 JobManager.debug_(Format("Ending job: {}", job)); //$NON-NLS-1$ | |
617 job.setResult(result); | |
618 job.setProgressMonitor(null); | |
619 job.setThread_package(null); | |
620 rescheduleDelay = job.getStartTime(); | |
621 changeState(job, Job.NONE); | |
622 } | |
623 //notify listeners outside sync block | |
624 final bool reschedule = active && rescheduleDelay > InternalJob.T_NONE && job.shouldSchedule_package(); | |
625 if (notify) | |
626 jobListeners.done(cast(Job) job, result, reschedule); | |
627 //reschedule the job if requested and we are still active | |
628 if (reschedule) | |
629 schedule(job, rescheduleDelay, reschedule); | |
630 } | |
631 package void endJob_package(InternalJob job, IStatus result, bool notify) { | |
632 endJob(job, result, notify); | |
633 } | |
634 | |
635 /* (non-Javadoc) | |
636 * @see dwtx.core.runtime.jobs.IJobManager#endRule(dwtx.core.runtime.jobs.ISchedulingRule) | |
637 */ | |
638 public void endRule(ISchedulingRule rule) { | |
639 implicitJobs.end(rule, false); | |
640 } | |
641 | |
642 /* (non-Javadoc) | |
643 * @see dwtx.core.runtime.jobs.IJobManager#find(java.lang.String) | |
644 */ | |
645 public Job[] find(Object family) { | |
646 List members = select(family); | |
647 return arraycast!(Job)( members.toArray()); | |
648 } | |
649 | |
650 /** | |
651 * Returns a running or blocked job whose scheduling rule conflicts with the | |
652 * scheduling rule of the given waiting job. Returns null if there are no | |
653 * conflicting jobs. A job can only run if there are no running jobs and no blocked | |
654 * jobs whose scheduling rule conflicts with its rule. | |
655 */ | |
656 protected InternalJob findBlockingJob(InternalJob waitingJob) { | |
657 if (waitingJob.getRule() is null) | |
658 return null; | |
659 synchronized (lock) { | |
660 if (running.isEmpty()) | |
661 return null; | |
662 //check the running jobs | |
663 bool hasBlockedJobs = false; | |
664 for (Iterator it = running.iterator(); it.hasNext();) { | |
665 InternalJob job = cast(InternalJob) it.next(); | |
666 if (waitingJob.isConflicting(job)) | |
667 return job; | |
668 if (!hasBlockedJobs) | |
669 hasBlockedJobs = job.previous() !is null; | |
670 } | |
671 //there are no blocked jobs, so we are done | |
672 if (!hasBlockedJobs) | |
673 return null; | |
674 //check all jobs blocked by running jobs | |
675 for (Iterator it = running.iterator(); it.hasNext();) { | |
676 InternalJob job = cast(InternalJob) it.next(); | |
677 while (true) { | |
678 job = job.previous(); | |
679 if (job is null) | |
680 break; | |
681 if (waitingJob.isConflicting(job)) | |
682 return job; | |
683 } | |
684 } | |
685 } | |
686 return null; | |
687 } | |
688 package InternalJob findBlockingJob_package(InternalJob waitingJob) { | |
689 return findBlockingJob(waitingJob); | |
690 } | |
691 | |
692 public LockManager getLockManager() { | |
693 return lockManager; | |
694 } | |
695 | |
696 private void initDebugOptions() { | |
697 DEBUG = JobOSGiUtils.getDefault().getBooleanDebugOption(OPTION_DEBUG_JOBS, false); | |
698 DEBUG_BEGIN_END = JobOSGiUtils.getDefault().getBooleanDebugOption(OPTION_DEBUG_BEGIN_END, false); | |
699 DEBUG_DEADLOCK = JobOSGiUtils.getDefault().getBooleanDebugOption(OPTION_DEADLOCK_ERROR, false); | |
700 DEBUG_LOCKS = JobOSGiUtils.getDefault().getBooleanDebugOption(OPTION_LOCKS, false); | |
701 DEBUG_TIMING = JobOSGiUtils.getDefault().getBooleanDebugOption(OPTION_DEBUG_JOBS_TIMING, false); | |
702 DEBUG_SHUTDOWN = JobOSGiUtils.getDefault().getBooleanDebugOption(OPTION_SHUTDOWN, false); | |
703 } | |
704 | |
705 /** | |
706 * Returns whether the job manager is active (has not been shutdown). | |
707 */ | |
708 protected bool isActive() { | |
709 return active; | |
710 } | |
711 package bool isActive_package() { | |
712 return isActive(); | |
713 } | |
714 | |
715 /** | |
716 * Returns true if the given job is blocking the execution of a non-system | |
717 * job. | |
718 */ | |
719 protected bool isBlocking(InternalJob runningJob) { | |
720 synchronized (lock) { | |
721 // if this job isn't running, it can't be blocking anyone | |
722 if (runningJob.getState_package() !is Job.RUNNING) | |
723 return false; | |
724 // if any job is queued behind this one, it is blocked by it | |
725 InternalJob previous = runningJob.previous(); | |
726 while (previous !is null) { | |
727 // ignore jobs of lower priority (higher priority value means lower priority) | |
728 if (previous.getPriority() < runningJob.getPriority()) { | |
729 if (!previous.isSystem_package()) | |
730 return true; | |
731 // implicit jobs should interrupt unless they act on behalf of system jobs | |
732 if (cast(ThreadJob)previous && (cast(ThreadJob) previous).shouldInterrupt()) | |
733 return true; | |
734 } | |
735 previous = previous.previous(); | |
736 } | |
737 // none found | |
738 return false; | |
739 } | |
740 } | |
741 package bool isBlocking_package(InternalJob runningJob) { | |
742 return isBlocking(runningJob); | |
743 } | |
744 | |
745 /* (non-Javadoc) | |
746 * @see dwtx.core.runtime.jobs.IJobManager#isIdle() | |
747 */ | |
748 public bool isIdle() { | |
749 synchronized (lock) { | |
750 return running.isEmpty() && waiting.isEmpty(); | |
751 } | |
752 } | |
753 | |
754 /* (non-Javadoc) | |
755 * @see dwtx.core.runtime.jobs.IJobManager#isSuspended() | |
756 */ | |
757 public bool isSuspended() { | |
758 synchronized (lock) { | |
759 return suspended; | |
760 } | |
761 } | |
762 | |
763 /* (non-Javadoc) | |
764 * @see dwtx.core.runtime.jobs.Job#job(dwtx.core.runtime.jobs.Job) | |
765 */ | |
766 protected void join(InternalJob job) { | |
767 IJobChangeListener listener; | |
768 Semaphore barrier; | |
769 synchronized (lock) { | |
770 int state = job.getState_package(); | |
771 if (state is Job.NONE) | |
772 return; | |
773 //don't join a waiting or sleeping job when suspended (deadlock risk) | |
774 if (suspended && state !is Job.RUNNING) | |
775 return; | |
776 //it's an error for a job to join itself | |
167 | 777 if (state is Job.RUNNING && job.getThread_package() is JThread.currentThread()) |
122 | 778 throw new IllegalStateException("Job attempted to join itself"); //$NON-NLS-1$ |
779 //the semaphore will be released when the job is done | |
780 barrier = new Semaphore(null); | |
781 listener = new class(barrier) JobChangeAdapter { | |
782 Semaphore barrier_; | |
783 this( Semaphore a ){ | |
784 barrier_ = a; | |
785 } | |
786 public void done(IJobChangeEvent event) { | |
787 barrier_.release(); | |
788 } | |
789 }; | |
790 job.addJobChangeListener_package(listener); | |
791 //compute set of all jobs that must run before this one | |
792 //add a listener that removes jobs from the blocking set when they finish | |
793 } | |
794 //wait until listener notifies this thread. | |
795 try { | |
796 while (true) { | |
797 //notify hook to service pending syncExecs before falling asleep | |
798 lockManager.aboutToWait(job.getThread_package()); | |
799 try { | |
800 if (barrier.acquire(Long.MAX_VALUE)) | |
801 break; | |
802 } catch (InterruptedException e) { | |
803 //loop and keep trying | |
804 } | |
805 } | |
806 } finally { | |
807 lockManager.aboutToRelease(); | |
808 job.removeJobChangeListener_package(listener); | |
809 } | |
810 } | |
811 package void join_package(InternalJob job) { | |
812 join(job); | |
813 } | |
814 | |
815 /* (non-Javadoc) | |
816 * @see IJobManager#join(String, IProgressMonitor) | |
817 */ | |
818 public void join(Object family_, IProgressMonitor monitor) { | |
819 monitor = monitorFor(monitor); | |
820 IJobChangeListener listener = null; | |
821 Set jobs_; | |
822 int jobCount; | |
823 Job blocking = null; | |
824 synchronized (lock) { | |
825 //don't join a waiting or sleeping job when suspended (deadlock risk) | |
826 int states = suspended ? Job.RUNNING : Job.RUNNING | Job.WAITING | Job.SLEEPING; | |
827 jobs_ = Collections.synchronizedSet(new HashSet(select(family_, states))); | |
828 jobCount = jobs_.size(); | |
829 if (jobCount > 0) { | |
830 //if there is only one blocking job, use it in the blockage callback below | |
831 if (jobCount is 1) | |
832 blocking = cast(Job) jobs_.iterator().next(); | |
833 listener = new class(family_, jobs_ )JobChangeAdapter { | |
834 Object family; | |
835 Set jobs; | |
836 this(Object a, Set b){ | |
837 family = a; | |
838 jobs = b; | |
839 } | |
840 public void done(IJobChangeEvent event) { | |
841 //don't remove from list if job is being rescheduled | |
842 if (!(cast(JobChangeEvent) event).reschedule) | |
843 jobs.remove(event.getJob()); | |
844 } | |
845 | |
846 //update the list of jobs if new ones are added during the join | |
847 public void scheduled(IJobChangeEvent event) { | |
848 //don't add to list if job is being rescheduled | |
849 if ((cast(JobChangeEvent) event).reschedule) | |
850 return; | |
851 Job job = event.getJob(); | |
852 if (job.belongsTo(family)) | |
853 jobs.add(job); | |
854 } | |
855 }; | |
856 addJobChangeListener(listener); | |
857 } | |
858 } | |
859 if (jobCount is 0) { | |
860 //use up the monitor outside synchronized block because monitors call untrusted code | |
861 monitor.beginTask(JobMessages.jobs_blocked0, 1); | |
862 monitor.done(); | |
863 return; | |
864 } | |
865 //spin until all jobs are completed | |
866 try { | |
867 monitor.beginTask(JobMessages.jobs_blocked0, jobCount); | |
868 monitor.subTask(NLS.bind(JobMessages.jobs_waitFamSub, Integer.toString(jobCount))); | |
869 reportBlocked(monitor, blocking); | |
870 int jobsLeft; | |
871 int reportedWorkDone = 0; | |
872 while ((jobsLeft = jobs_.size()) > 0) { | |
873 //don't let there be negative work done if new jobs have | |
874 //been added since the join began | |
875 int actualWorkDone = Math.max(0, jobCount - jobsLeft); | |
876 if (reportedWorkDone < actualWorkDone) { | |
877 monitor.worked(actualWorkDone - reportedWorkDone); | |
878 reportedWorkDone = actualWorkDone; | |
879 monitor.subTask(NLS.bind(JobMessages.jobs_waitFamSub, Integer.toString(jobsLeft))); | |
880 } | |
167 | 881 |
882 if (JThread.interrupted()) | |
883 throw new InterruptedException(); | |
122 | 884 if (monitor.isCanceled()) |
885 throw new OperationCanceledException(); | |
886 //notify hook to service pending syncExecs before falling asleep | |
887 lockManager.aboutToWait(null); | |
167 | 888 JThread.sleep(100); |
122 | 889 } |
890 } finally { | |
891 lockManager.aboutToRelease(); | |
892 removeJobChangeListener(listener); | |
893 reportUnblocked(monitor); | |
894 monitor.done(); | |
895 } | |
896 } | |
897 | |
898 /** | |
899 * Returns a non-null progress monitor instance. If the monitor is null, | |
900 * returns the default monitor supplied by the progress provider, or a | |
901 * NullProgressMonitor if no default monitor is available. | |
902 */ | |
903 private IProgressMonitor monitorFor(IProgressMonitor monitor) { | |
904 if (monitor is null || (cast(NullProgressMonitor)monitor )) { | |
905 if (progressProvider !is null) { | |
906 try { | |
907 monitor = progressProvider.getDefaultMonitor(); | |
908 } catch (Exception e) { | |
909 String msg = NLS.bind(JobMessages.meta_pluginProblems, JobManager.PI_JOBS); | |
910 RuntimeLog.log(new Status(IStatus.ERROR, JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, msg, e)); | |
911 } | |
912 } | |
913 } | |
914 | |
915 if (monitor is null) | |
916 return new NullProgressMonitor(); | |
917 return monitor; | |
918 } | |
919 | |
920 /* (non-Javadoc) | |
921 * @see IJobManager#newLock(java.lang.String) | |
922 */ | |
923 public ILock newLock() { | |
924 return lockManager.newLock(); | |
925 } | |
926 | |
927 /** | |
928 * Removes and returns the first waiting job in the queue. Returns null if there | |
929 * are no items waiting in the queue. If an item is removed from the queue, | |
930 * it is moved to the running jobs list. | |
931 */ | |
932 private Job nextJob() { | |
933 synchronized (lock) { | |
934 //do nothing if the job manager is suspended | |
935 if (suspended) | |
936 return null; | |
937 //tickle the sleep queue to see if anyone wakes up | |
938 long now = System.currentTimeMillis(); | |
939 InternalJob job = sleeping.peek(); | |
940 while (job !is null && job.getStartTime() < now) { | |
941 job.setStartTime(now + delayFor(job.getPriority())); | |
942 job.setWaitQueueStamp(waitQueueCounter++); | |
943 changeState(job, Job.WAITING); | |
944 job = sleeping.peek(); | |
945 } | |
946 //process the wait queue until we find a job whose rules are satisfied. | |
947 while ((job = waiting.peek()) !is null) { | |
948 InternalJob blocker = findBlockingJob(job); | |
949 if (blocker is null) | |
950 break; | |
951 //queue this job after the job that's blocking it | |
952 changeState(job, InternalJob.BLOCKED); | |
953 //assert job does not already belong to some other data structure | |
954 Assert.isTrue(job.next() is null); | |
955 Assert.isTrue(job.previous() is null); | |
956 blocker.addLast(job); | |
957 } | |
958 //the job to run must be in the running list before we exit | |
959 //the sync block, otherwise two jobs with conflicting rules could start at once | |
960 if (job !is null) { | |
961 changeState(job, InternalJob.ABOUT_TO_RUN); | |
962 if (JobManager.DEBUG) | |
963 JobManager.debug_(Format("Starting job: {}", job)); //$NON-NLS-1$ | |
964 } | |
965 return cast(Job) job; | |
966 } | |
967 } | |
968 | |
969 /* (non-Javadoc) | |
970 * @see dwtx.core.runtime.jobs.IJobManager#removeJobListener(dwtx.core.runtime.jobs.IJobChangeListener) | |
971 */ | |
972 public void removeJobChangeListener(IJobChangeListener listener) { | |
973 jobListeners.remove(listener); | |
974 } | |
975 | |
976 /** | |
977 * Report to the progress monitor that this thread is blocked, supplying | |
978 * an information message, and if possible the job that is causing the blockage. | |
979 * Important: An invocation of this method MUST be followed eventually be | |
980 * an invocation of reportUnblocked. | |
981 * @param monitor The monitor to report blocking to | |
982 * @param blockingJob The job that is blocking this thread, or <code>null</code> | |
983 * @see #reportUnblocked | |
984 */ | |
985 final void reportBlocked(IProgressMonitor monitor, InternalJob blockingJob) { | |
986 if (!(cast(IProgressMonitorWithBlocking)monitor )) | |
987 return; | |
988 IStatus reason; | |
989 if (blockingJob is null || cast(ThreadJob)blockingJob || blockingJob.isSystem_package()) { | |
990 reason = new Status(IStatus.INFO, JobManager.PI_JOBS, 1, JobMessages.jobs_blocked0, null); | |
991 } else { | |
992 String msg = NLS.bind(JobMessages.jobs_blocked1, blockingJob.getName_package()); | |
993 reason = new JobStatus(IStatus.INFO, cast(Job) blockingJob, msg); | |
994 } | |
995 (cast(IProgressMonitorWithBlocking) monitor).setBlocked(reason); | |
996 } | |
997 | |
998 /** | |
999 * Reports that this thread was blocked, but is no longer blocked and is able | |
1000 * to proceed. | |
1001 * @param monitor The monitor to report unblocking to. | |
1002 * @see #reportBlocked | |
1003 */ | |
1004 final void reportUnblocked(IProgressMonitor monitor) { | |
1005 if (cast(IProgressMonitorWithBlocking)monitor ) | |
1006 (cast(IProgressMonitorWithBlocking) monitor).clearBlocked(); | |
1007 } | |
1008 | |
1009 /*(non-Javadoc) | |
1010 * @see dwtx.core.runtime.jobs.IJobManager#resume() | |
1011 */ | |
1012 public final void resume() { | |
1013 synchronized (lock) { | |
1014 suspended = false; | |
1015 //poke the job pool | |
1016 pool.jobQueued(); | |
1017 } | |
1018 } | |
1019 | |
1020 /** (non-Javadoc) | |
1021 * @deprecated this method should not be used | |
1022 * @see dwtx.core.runtime.jobs.IJobManager#resume(dwtx.core.runtime.jobs.ISchedulingRule) | |
1023 */ | |
1024 public final void resume(ISchedulingRule rule) { | |
1025 implicitJobs.resume(rule); | |
1026 } | |
1027 | |
1028 /** | |
1029 * Attempts to immediately start a given job. Returns true if the job was | |
1030 * successfully started, and false if it could not be started immediately | |
1031 * due to a currently running job with a conflicting rule. Listeners will never | |
1032 * be notified of jobs that are run in this way. | |
1033 */ | |
1034 protected bool runNow(InternalJob job) { | |
1035 synchronized (lock) { | |
1036 //cannot start if there is a conflicting job | |
1037 if (findBlockingJob(job) !is null) | |
1038 return false; | |
1039 changeState(job, Job.RUNNING); | |
1040 job.setProgressMonitor(new NullProgressMonitor()); | |
1041 job.run_package(null); | |
1042 } | |
1043 return true; | |
1044 } | |
1045 package bool runNow_package(InternalJob job) { | |
1046 return runNow(job); | |
1047 } | |
1048 | |
1049 /* (non-Javadoc) | |
1050 * @see dwtx.core.runtime.jobs.Job#schedule(long) | |
1051 */ | |
1052 protected void schedule(InternalJob job, long delay, bool reschedule) { | |
1053 if (!active) | |
1054 throw new IllegalStateException("Job manager has been shut down."); //$NON-NLS-1$ | |
1055 Assert.isNotNull(job, "Job is null"); //$NON-NLS-1$ | |
1056 Assert.isLegal(delay >= 0, "Scheduling delay is negative"); //$NON-NLS-1$ | |
1057 synchronized (lock) { | |
1058 //if the job is already running, set it to be rescheduled when done | |
1059 if (job.getState_package() is Job.RUNNING) { | |
1060 job.setStartTime(delay); | |
1061 return; | |
1062 } | |
1063 //can't schedule a job that is waiting or sleeping | |
1064 if (job.internalGetState() !is Job.NONE) | |
1065 return; | |
1066 if (JobManager.DEBUG) | |
1067 JobManager.debug_(Format("Scheduling job: {}", job)); //$NON-NLS-1$ | |
1068 //remember that we are about to schedule the job | |
1069 //to prevent multiple schedule attempts from succeeding (bug 68452) | |
1070 changeState(job, InternalJob.ABOUT_TO_SCHEDULE); | |
1071 } | |
1072 //notify listeners outside sync block | |
1073 jobListeners.scheduled(cast(Job) job, delay, reschedule); | |
1074 //schedule the job | |
1075 doSchedule(job, delay); | |
1076 //call the pool outside sync block to avoid deadlock | |
1077 pool.jobQueued(); | |
1078 } | |
1079 package void schedule_package(InternalJob job, long delay, bool reschedule) { | |
1080 schedule(job, delay, reschedule); | |
1081 } | |
1082 | |
1083 /** | |
1084 * Adds all family members in the list of jobs to the collection | |
1085 */ | |
1086 private void select(List members, Object family, InternalJob firstJob, int stateMask) { | |
1087 if (firstJob is null) | |
1088 return; | |
1089 InternalJob job = firstJob; | |
1090 do { | |
1091 //note that job state cannot be NONE at this point | |
1092 if ((family is null || job.belongsTo_package(family)) && ((job.getState_package() & stateMask) !is 0)) | |
1093 members.add(job); | |
1094 job = job.previous(); | |
1095 } while (job !is null && job !is firstJob); | |
1096 } | |
1097 | |
1098 /** | |
1099 * Returns a list of all jobs known to the job manager that belong to the given family. | |
1100 */ | |
1101 private List select(Object family) { | |
1102 return select(family, Job.WAITING | Job.SLEEPING | Job.RUNNING); | |
1103 } | |
1104 | |
1105 /** | |
1106 * Returns a list of all jobs known to the job manager that belong to the given | |
1107 * family and are in one of the provided states. | |
1108 */ | |
1109 private List select(Object family, int stateMask) { | |
1110 List members = new ArrayList(); | |
1111 synchronized (lock) { | |
1112 if ((stateMask & Job.RUNNING) !is 0) { | |
1113 for (Iterator it = running.iterator(); it.hasNext();) { | |
1114 select(members, family, cast(InternalJob) it.next(), stateMask); | |
1115 } | |
1116 } | |
1117 if ((stateMask & Job.WAITING) !is 0) | |
1118 select(members, family, waiting.peek(), stateMask); | |
1119 if ((stateMask & Job.SLEEPING) !is 0) | |
1120 select(members, family, sleeping.peek(), stateMask); | |
1121 } | |
1122 return members; | |
1123 } | |
1124 | |
1125 /* (non-Javadoc) | |
1126 * @see IJobManager#setLockListener(LockListener) | |
1127 */ | |
1128 public void setLockListener(LockListener listener) { | |
1129 lockManager.setLockListener(listener); | |
1130 } | |
1131 | |
1132 /** | |
1133 * Changes a job priority. | |
1134 */ | |
1135 protected void setPriority(InternalJob job, int newPriority) { | |
1136 synchronized (lock) { | |
1137 int oldPriority = job.getPriority(); | |
1138 if (oldPriority is newPriority) | |
1139 return; | |
1140 job.internalSetPriority(newPriority); | |
1141 //if the job is waiting to run, re-shuffle the queue | |
1142 if (job.getState_package() is Job.WAITING) { | |
1143 long oldStart = job.getStartTime(); | |
1144 job.setStartTime(oldStart + (delayFor(newPriority) - delayFor(oldPriority))); | |
1145 waiting.resort(job); | |
1146 } | |
1147 } | |
1148 } | |
1149 package void setPriority_package(InternalJob job, int newPriority) { | |
1150 setPriority(job, newPriority); | |
1151 } | |
1152 | |
1153 /* (non-Javadoc) | |
1154 * @see IJobManager#setProgressProvider(IProgressProvider) | |
1155 */ | |
1156 public void setProgressProvider(ProgressProvider provider) { | |
1157 progressProvider = provider; | |
1158 } | |
1159 | |
1160 /* (non-Javadoc) | |
1161 * @see Job#setRule | |
1162 */ | |
1163 public void setRule(InternalJob job, ISchedulingRule rule) { | |
1164 synchronized (lock) { | |
1165 //cannot change the rule of a job that is already running | |
1166 Assert.isLegal(job.getState_package() is Job.NONE); | |
1167 validateRule(rule); | |
1168 job.internalSetRule(rule); | |
1169 } | |
1170 } | |
1171 | |
1172 /** | |
1173 * Puts a job to sleep. Returns true if the job was successfully put to sleep. | |
1174 */ | |
1175 protected bool sleep(InternalJob job) { | |
1176 synchronized (lock) { | |
1177 switch (job.getState_package()) { | |
1178 case Job.RUNNING : | |
1179 //cannot be paused if it is already running (as opposed to ABOUT_TO_RUN) | |
1180 if (job.internalGetState() is Job.RUNNING) | |
1181 return false; | |
1182 //job hasn't started running yet (aboutToRun listener) | |
1183 break; | |
1184 case Job.SLEEPING : | |
1185 //update the job wake time | |
1186 job.setStartTime(InternalJob.T_INFINITE); | |
1187 //change state again to re-shuffle the sleep queue | |
1188 changeState(job, Job.SLEEPING); | |
1189 return true; | |
1190 case Job.NONE : | |
1191 return true; | |
1192 case Job.WAITING : | |
1193 //put the job to sleep | |
1194 break; | |
192
c3583c6ec027
Added missing default cases for switch statements
Frank Benoit <benoit@tionex.de>
parents:
167
diff
changeset
|
1195 default: |
122 | 1196 } |
1197 job.setStartTime(InternalJob.T_INFINITE); | |
1198 changeState(job, Job.SLEEPING); | |
1199 } | |
1200 jobListeners.sleeping(cast(Job) job); | |
1201 return true; | |
1202 } | |
1203 package bool sleep_package(InternalJob job) { | |
1204 return sleep(job); | |
1205 } | |
1206 | |
1207 /* (non-Javadoc) | |
1208 * @see IJobManager#sleep(String) | |
1209 */ | |
1210 public void sleep(Object family) { | |
1211 //don't synchronize because sleep calls listeners | |
1212 for (Iterator it = select(family).iterator(); it.hasNext();) { | |
1213 sleep(cast(InternalJob) it.next()); | |
1214 } | |
1215 } | |
1216 | |
1217 /** | |
1218 * Returns the estimated time in milliseconds before the next job is scheduled | |
1219 * to wake up. The result may be negative. Returns InternalJob.T_INFINITE if | |
1220 * there are no sleeping or waiting jobs. | |
1221 */ | |
1222 protected long sleepHint() { | |
1223 synchronized (lock) { | |
1224 //wait forever if job manager is suspended | |
1225 if (suspended) | |
1226 return InternalJob.T_INFINITE; | |
1227 if (!waiting.isEmpty()) | |
1228 return 0L; | |
1229 //return the anticipated time that the next sleeping job will wake | |
1230 InternalJob next = sleeping.peek(); | |
1231 if (next is null) | |
1232 return InternalJob.T_INFINITE; | |
1233 return next.getStartTime() - System.currentTimeMillis(); | |
1234 } | |
1235 } | |
1236 package long sleepHint_package() { | |
1237 return sleepHint(); | |
1238 } | |
1239 /** | |
1240 * Returns the next job to be run, or null if no jobs are waiting to run. | |
1241 * The worker must call endJob when the job is finished running. | |
1242 */ | |
1243 protected Job startJob() { | |
1244 Job job = null; | |
1245 while (true) { | |
1246 job = nextJob(); | |
1247 if (job is null) | |
1248 return null; | |
1249 //must perform this outside sync block because it is third party code | |
1250 if (job.shouldRun()) { | |
1251 //check for listener veto | |
1252 jobListeners.aboutToRun(job); | |
1253 //listeners may have canceled or put the job to sleep | |
1254 synchronized (lock) { | |
1255 if (job.getState() is Job.RUNNING) { | |
1256 InternalJob internal = job; | |
1257 if (internal.isAboutToRunCanceled()) { | |
1258 internal.setAboutToRunCanceled(false); | |
1259 //fall through and end the job below | |
1260 } else { | |
1261 internal.setProgressMonitor(createMonitor(job)); | |
1262 //change from ABOUT_TO_RUN to RUNNING | |
1263 internal.internalSetState(Job.RUNNING); | |
1264 break; | |
1265 } | |
1266 } | |
1267 } | |
1268 } | |
1269 if (job.getState() !is Job.SLEEPING) { | |
1270 //job has been vetoed or canceled, so mark it as done | |
1271 endJob(job, Status.CANCEL_STATUS, true); | |
1272 continue; | |
1273 } | |
1274 } | |
1275 jobListeners.running(job); | |
1276 return job; | |
1277 | |
1278 } | |
1279 package Job startJob_package() { | |
1280 return startJob(); | |
1281 } | |
1282 | |
1283 /* non-Javadoc) | |
1284 * @see dwtx.core.runtime.jobs.IJobManager#suspend() | |
1285 */ | |
1286 public final void suspend() { | |
1287 synchronized (lock) { | |
1288 suspended = true; | |
1289 } | |
1290 } | |
1291 | |
1292 /** (non-Javadoc) | |
1293 * @deprecated this method should not be used | |
1294 * @see dwtx.core.runtime.jobs.IJobManager#suspend(dwtx.core.runtime.jobs.ISchedulingRule, dwtx.core.runtime.IProgressMonitor) | |
1295 */ | |
1296 public final void suspend(ISchedulingRule rule, IProgressMonitor monitor) { | |
1297 Assert.isNotNull(cast(Object)rule); | |
1298 implicitJobs.suspend(rule, monitorFor(monitor)); | |
1299 } | |
1300 | |
1301 /* non-Javadoc) | |
1302 * @see dwtx.core.runtime.jobs.IJobManager#transferRule() | |
1303 */ | |
167 | 1304 public void transferRule(ISchedulingRule rule, JThread destinationThread) { |
122 | 1305 implicitJobs.transfer(rule, destinationThread); |
1306 } | |
1307 | |
1308 /** | |
1309 * Validates that the given scheduling rule obeys the constraints of | |
1310 * scheduling rules as described in the <code>ISchedulingRule</code> | |
1311 * javadoc specification. | |
1312 */ | |
1313 private void validateRule(ISchedulingRule rule) { | |
1314 //null rule always valid | |
1315 if (rule is null) | |
1316 return; | |
1317 initNullRule(); | |
1318 //contains method must be reflexive | |
1319 Assert.isLegal(rule.contains(rule)); | |
1320 //contains method must return false when given an unknown rule | |
1321 Assert.isLegal(!rule.contains(nullRule)); | |
1322 //isConflicting method must be reflexive | |
1323 Assert.isLegal(rule.isConflicting(rule)); | |
1324 //isConflicting method must return false when given an unknown rule | |
1325 Assert.isLegal(!rule.isConflicting(nullRule)); | |
1326 } | |
1327 | |
1328 /* (non-Javadoc) | |
1329 * @see Job#wakeUp(long) | |
1330 */ | |
1331 protected void wakeUp(InternalJob job, long delay) { | |
1332 Assert.isLegal(delay >= 0, "Scheduling delay is negative"); //$NON-NLS-1$ | |
1333 synchronized (lock) { | |
1334 //cannot wake up if it is not sleeping | |
1335 if (job.getState_package() !is Job.SLEEPING) | |
1336 return; | |
1337 doSchedule(job, delay); | |
1338 } | |
1339 //call the pool outside sync block to avoid deadlock | |
1340 pool.jobQueued(); | |
1341 | |
1342 //only notify of wake up if immediate | |
1343 if (delay is 0) | |
1344 jobListeners.awake(cast(Job) job); | |
1345 } | |
1346 package void wakeUp_package(InternalJob job, long delay) { | |
1347 wakeUp(job, delay); | |
1348 } | |
1349 | |
1350 /* (non-Javadoc) | |
1351 * @see IJobFamily#wakeUp(String) | |
1352 */ | |
1353 public void wakeUp(Object family) { | |
1354 //don't synchronize because wakeUp calls listeners | |
1355 for (Iterator it = select(family).iterator(); it.hasNext();) { | |
1356 wakeUp(cast(InternalJob) it.next(), 0L); | |
1357 } | |
1358 } | |
1359 } |