Concurrency infrastructure

An introduction to jobs

The org.eclipse.core.runtime.jobs package provides infrastructure for scheduling, executing, and managing concurrently running operations. The basic unit of concurrently running work is represented by the Job class. When a job is scheduled, it is added to a job queue managed by the platform. A background thread will then remove jobs from the queue and invoke the Job.run method. Let's look at a trivial example of a job:

   class TrivialJob extends Job {
      public TrivialJob() {
         super("Trivial Job");
      }
      public IStatus run(IProgressMonitor monitor) {
         System.out.println("This is a job");
         return Status.OK_STATUS;
      }
   }
This job can be created and scheduled to execute as follows:
   TrivialJob job = new TrivialJob();
   System.out.println("About to schedule a job");
   job.schedule();
   System.out.println("Finished scheduling a job");
The output of this program is timing dependent. That is, there is no way to be sure when the job's run method will execute in relation to the thread that invoked schedule. In this case, the output will either be:
   About to schedule a job
   This is a job
   Finished scheduling a job
Or:
   About to schedule a job
   Finished scheduling a job
   This is a job

If you want to be certain that a job has completed before continuing, you can use the Job.join method. As with java.lang.Thread.join(), this method will block the caller until the job has completed, or until the calling thread is interrupted. Let's rewrite our snippet from above in a more deterministic manner:

   TrivialJob job = new TrivialJob();
   System.out.println("About to schedule a job");
   job.schedule();
   job.join();
   if (job.getResult().isOk())
      System.out.println("Job completed with success");
   else
      System.out.println("Job did not complete successfully");
Assuming the join call is not interrupted, this method is guaranteed to return the following result:
   About to schedule a job
   This is a job
   Job completed with success

Of course, it is generally not useful to join a job immediately after scheduling it, since you obtain no concurrency by doing so. In this case you might as well call Job.run directly in the calling thread. However, we will see some examples later on where join is more useful.

You'll notice this new example also makes use of the job result. The result is the IStatus object that is returned from the Job's run method. By creating your own subclass of Status, you can use the result to pass arbitrary objects back from the job's run method. The result can also be used to indicate failure (by returning an IStatus with severity IStatus.ERROR), or cancelation (IStatus.CANCEL).

Common job operations

We've seen the schedule and join methods, but jobs have a number of other interesting methods to allow you to interact with them. If you schedule a job but then decide it is no longer needed, the job can be stopped using the cancel method. If the job has not yet started running when cancel is called, the job is immediately discarded and will not run. If, on the other hand, the job has already started running, it is up to the job whether it wants to respond to the cancelation. This is a situation where joining the job comes in handy. Here is a common idiom for canceling a job, and ensuring that the job has finished before proceeding:

   if (!job.cancel())
      job.join();
If the cancelation did not take effect immediately, then cancel will return false, and the join is needed to wait for the job to respond to the cancelation request.

Slightly less drastic than cancelation is the Job.sleep method. Again, if the job has not yet started running, the sleep method will cause the job to be put on hold indefinitely. The job will still be remembered by the platform, and a call to Job.wakeUp will cause the job to be added to the wait queue where it will eventually be executed.

Job families

Sometimes you may want to query or modify a whole group of related jobs. This can be accomplished using job families. A job declares that it belongs to a certain family by overriding the belongsTo method:

   public static final String MY_FAMILY = "myJobFamily";
   ...
   class FamilyJob extends Job {
      ...
      public boolean belongsTo(Object family) {
         return family == MY_FAMILY;
      }
   }
You can now cancel, join, sleep, or find all jobs in that family by using methods on IJobManager:
   IJobManager jobMan = Platform.getJobManager();
   jobMan.cancel(MY_FAMILY);
   jobMan.join(MY_FAMILY, null);
Note that since job families are arbitrary objects, you can store interesting state in the job family itself, and jobs can dynamically decide what families they belong to as needed. Just be sure to use a family object that is fairly unique, to avoid accidental interaction with the families created by other plug-ins. Families are also a convenient way of locating particular groups of jobs. The method IJobManager.find(Object family) can be used to locate instances of all running, waiting, and sleeping jobs at any given time.

Copyright IBM Corporation and others 2000, 2003.