Pattern: Scheduler

NOTE: Much of the text here was taken from the original author, Mark Grand, copied from the book book Patterns in Java, Volume 1. WPI Students can find the full text of this book online here. I figured it was better to quote and credit than rewrite.

Synopsis

Control the order in which threads are scheduled to execute single-threaded code using an object that explicitly sequences waiting threads. The Scheduler pattern provides a mechanism for implementing a scheduling policy. It is independent of any specific scheduling policy.

Context

In a situation where multiple users are using the same printer, the printer is often unable to print things out faster than the users request them. Therefore, the requests must be stored and scheduled until they can be processed. A simple queue or priority queue cannot handle when multiple requests arrive at once very well. Requests may have an explicit ordering, as well; they may not be desired in the order they are requested.

In general, if there are multiple threads that must access a single-threaded function, and one has to ensure that they are given access in an appropriate order, then the Scheduler pattern is appropriate.

Forces

Solution

The solution is to make a Scheduler object that has a function call that will not return until it is that thread's turn to use the processor.


FIGURE 2: Classes for the Scheduler Pattern

Here are descriptions of the roles the classes play in the Scheduler pattern:

Request. Classes in this role must implement the interface in the ScheduleOrdering role. Request objects encapsulate a request for a Processor object to do something.

Processor. Instances of classes in this role perform a computation described by a Request object. They may be presented with more than one Request object to process at a time, but they can process only one at a time. A Processor object delegates to a Scheduler object the responsibility for scheduling Request objects for processing, one at a time.

Scheduler. Instances of classes in this role schedule Request objects for processing by a Processor object. To promote reusability, a Scheduler class does not have any knowledge of the Request class that it schedules. Instead, it accesses Request objects through the ScheduleOrdering interface that they im plement.

A class in this role is responsible for deciding when the next request will run. It is not responsible for the order in which the requests will run. It delegates that responsibility to a ScheduleOrdering interface.

ScheduleOrdering. Request objects implement the interface that is in this role. Interfaces in this role serve two purposes:

By referring to a ScheduleOrdering interface, Processor classes avoid a dependency on a Request class.

By calling methods defined by the ScheduleOrdering interface, Scheduler classes are able to delegate the decision about which Request object will be processed next. This increases the reusability of Scheduler classes. The class diagram in Figure 1 indicates one such method, named scheduleBefore.


FIGURE 2: Interaction between objects.

The interaction between a Processor object and a Scheduler object occurs in two stages, as shown by the collaboration diagram in Figure 2.

The first interaction in Figure 2 is a call to a Processor object’s doIt method. The first thing the doIt method does is call the enter method of the Scheduler object associated with the Processor object. If there is no other thread currently executing the rest of the doIt method, then the enter method returns immediately. After the enter method returns, the Scheduler object knows that the resource it manages is busy. While its resource is busy, any calls to the Scheduler object’s enter method will not return until the resource is not busy and the Scheduler object decides it is that call’s turn to return.

A Scheduler object considers the resource it manages to be busy until the Scheduler object’s done method is called. When one thread makes a valid call to a Scheduler object’s done method, if any threads are waiting to return from the Scheduler object’s enter method, then one of them returns.

If a call to a Scheduler object’s enter method must wait before it returns and there are other calls waiting to return from the enter method, then the Scheduler object must decide which call will return next. It decides by consulting the Request objects that were passed into those calls to decide which call will return next. It does this indirectly by calling methods declared for the purpose by the ScheduleOrdering interface and implemented by the Request object.

Consequences

Implementation

In some applications, the Scheduler class has a scheduling policy that does not require it to consult Request objects to determine the order in which calls to its enter method will return. An example of such a policy is to allow calls to the enter method to return in the order in which they were called. In such cases, there is no need to pass Request objects into the enter method or to have a ScheduleOrdering interface. Another example of such a policy is to not take into account the order in which requests are scheduled but to require at least five minutes between the end of one task and the beginning of another.

Sample source code can be found here.

Related Patterns

Read/Write Lock Implementations of the Read/Write Lock pattern usually use the Scheduler pattern to ensure fairness in scheduling.


Written by Jeff Bacon, with much material taken from Mark Grand's Patterns In Java, Vol. 1