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