Package sim.engine

Class Schedule

java.lang.Object
sim.engine.Schedule
All Implemented Interfaces:
Serializable

public class Schedule extends Object implements Serializable
Schedule defines a threadsafe scheduling queue in which events can be scheduled to occur at future time. The time of the most recent event which has already occured is given by the getTime() method. If the current time is BEFORE_SIMULATION (defined to be EPOCH - 1), then the schedule is set to the "time before time" (the schedule hasn't started running yet). If the current time is AFTER_SIMULATION (positive infinity), then the schedule has run out of time. EPOCH (0.0) is defined as the first timestep for which you can legally schedule a value. EPOCH_PLUS_ESPILON is defined as the smallest possible second timestep for which you can legally sechedule a value. If you're scheduling events to occur on integer timesteps, you may want to ensure that your simulation does not run beyond MAXIMUM_INTEGER (9007199254740992L or 9.007199254740992E15). For values of a double d >= MAXIMUM_INTEGER, d + 1 == d !

An event is defined as a Steppable object. You can schedule events to either occur a single time or to occur repeatedly at some interval. If the event occurs repeatedly, the schedule will provide you with a Stoppable object on which you can call stop() to cancel all future repeats of the event. If instead you wish to "stop" a single-time event from occuring before its time has come, you should do so through the use of a TentativeStep object. At present you cannot delete objects from the Schedule -- just stop them and let them drop out in due course.

The schedule is pulsed by calling its step(...) method. Each pulse, the schedule finds the minimum time at which events are scheduled, moves ahead to that time, and then calls all the events scheduled at that time. Multiple events may be scheduled for the same time. No event may be scheduled for a time earlier than getTime(). If at time getTime() you schedule a new event for time getTime(), then actually this event will occur at time getTime()+epsilon, that is, the smallest possible slice of time greater than getTime().

IMPORTANT NOTE: we have disabled the setShuffling() procedure by making the methods private. The reason for this is that although turning off shuffling causes the Steppables to be stepped in a predictable order, they will not necessarily be stepped in the order in which they were submitted, which was the whole point of the methods. The reason for this is that a binary heap is not "stable": it doesn't break ties by returning elements in the same order in which they appeared. This potentially could cause bugs in simulations and we want to make it very clear.

Events at a step are further subdivided and scheduled according to their ordering, an integer. Objects for scheduled for lower orderings for a given time will be executed before objects with higher orderings for the same time. If objects are scheduled for the same time and have the same ordering value, their execution will be randomly ordered with respect to one another.

You might be wondering: why bother with using orderings? After all, can't you achieve the same thing by just stretching elements out in time? There are two reasons to use orderings. First, it allows you to use the getTime() method to keep tabs on the current time in a way that might be convenient to you. But second and more importantly, MASON's GUI facility will update its displays and inspectors only after all Steppables scheduled for a given timestamp have completed, and so orderings give you a way of subdividing the interval of time between GUI updates.

A schedule may be sealed meaning that it will refuse to accept any further scheduled events even if its time is not yet AFTER_SIMULATION. This is largely done internally by MASON code: you probably will never want to do this. Once a schedule is sealed it cannot be unsealed until it is reset().

You can clear out the entire Schedule, unseal it, and restart it to BEFORE_SIMULATION by calling reset(). However, this does not prevent AsynchronousSteppables from suddenly rescheduling themselves in the queue. Stopping the simulation from within a Steppable object's step() method is best done by calling SimState.kill(). From the main thread, the most straightforward way to stop a simulation is to just stop calling schedule.step(...), and proceed directly to SimState.finish().

You can get the number of times that step(...) has been called on the schedule by calling the getSteps() method. This value is incremented just as the Schedule exits its step(...) method and only if the method returned true. Additionally, you can get a string version of the current time with the getTimestamp(...) method.

Note on Synchronization. In order to maximize the ability for threads to access the Schedule at any time, Schedule uses two locks for synchronization. First, the step() method synchronizes on the Schedule itself. This prevents step() from being called simultaneously from different threads; also step() tests to make sure that it's not called reentrantly from within the same thread. Second, many methods synchronize on an internal lock, including step(). This allows step() to synchronize on the lock only to suck out the relevant Steppables from the Heap and to advance the timestep; all other portions of step() are outside of the lock. Thus when step() actually steps the Steppables, even in different threads (like AsynchronousSteppable or ParallelSequence), they can turn around and submit step-requests to the Schedule even while it's still in its step() method.

One downside to this flexibility is that it's very inefficient to check, at each step of a Steppable, whether the Schedule has been reset or not. Thus now if you call reset() or [better] SimState.kill(), the Schedule will continue to step Steppables until it has exhausted ones scheduled for the current timestep. Only at that point will it cease.

Heaps and Calendar Queues. Schedule uses a plain-old binary heap for its queueing mechanism. This is reasonably efficient, but it could be made more efficient with a Calendar Queue designed for the purposes of your simulation. We settled on a Heap because we do not know what the expected scheduling pattern will be for any given simulation, and so had to go for the most general case. If you'd care to customize your queue, you can do so by overriding the createHeap() method in a custom Schedule. We imagine this would be rare.

See Also:
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    protected static class 
    Timestamps stored as keys in the heap.
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final double
    The time which indicates that the Schedule is finished.
    static final double
    The time which indicates that the Schedule hasn't started yet.
    static final double
    The first possible schedulable time.
    static final double
    The second possible schedulable time.
    protected Object
    The schedule lock.
    static final double
    The last time beyond which the schedule is no longer able to precisely maintain integer values due to loss of precision.
    protected Heap
    The Schedule's queue.
    protected boolean
    Whether the schedule is sealed, as returned by isSealed().
    protected long
    The current steps, as returned by getSteps().
    protected double
    The current time, as returned by getTime().
  • Constructor Summary

    Constructors
    Constructor
    Description
    Creates a Schedule.
  • Method Summary

    Modifier and Type
    Method
    Description
    protected boolean
    Schedules an item.
    void
    Adds a steppable to be called every iteration of the Schedule immediately after any other Steppables are called for that step.
    void
    Adds a steppable to be called every iteration of the Schedule immediately before any other Steppables are called for that step.
    void
    Empties out the schedule but does not reset the time or steps.
    protected Heap
    Returns a Heap to be used by the Schedule.
    long
    Returns the number of steps the Schedule has pulsed so far.
    double
    Returns the current timestep
    getTimestamp(double time, String beforeSimulationString, String afterSimulationString)
    Returns a given time in string format.
    getTimestamp(String beforeSimulationString, String afterSimulationString)
    Returns the current time in string format.
    boolean
    Returns whether or not the schedule is sealed (nothing more can be scheduled, even if the schedule isn't at AFTER_SIMULATION yet).
    void
    merge(Schedule other)
    Merge a given schedule into this one.
    void
    Empties out the schedule and resets it to a pristine state BEFORE_SIMULATION, with steps = 0.
    boolean
    Returns true if the schedule has nothing left to do.
    boolean
    scheduleOnce(double time, int ordering, Steppable event)
    Schedules the event to occur at the provided time, and in the ordering provided.
    boolean
    scheduleOnce(double time, Steppable event)
    Schedules the event to occur at the provided time, 0 ordering.
    boolean
    Schedules the event to occur at getTime() + 1.0, 0 ordering.
    boolean
    scheduleOnce(Steppable event, int ordering)
    Schedules the event to occur at getTime() + 1.0, and in the ordering provided.
    boolean
    scheduleOnceIn(double delta, Steppable event)
    Schedules the event to occur at getTime() + delta, 0 ordering.
    boolean
    scheduleOnceIn(double delta, Steppable event, int ordering)
    Schedules the event to occur at getTime() + delta, and in the ordering provided.
    scheduleRepeating(double time, int ordering, Steppable event)
    Schedules the event to recur at an interval of 1.0 starting at the provided time, and in the ordering provided.
    scheduleRepeating(double time, int ordering, Steppable event, double interval)
    Schedules the event to recur at the specified interval starting at the provided time, and in the ordering provided.
    scheduleRepeating(double time, Steppable event)
    Schedules the event to recur at the specified interval starting at the provided time, and at 0 ordering.
    scheduleRepeating(double time, Steppable event, double interval)
    Schedules the event to recur at the specified interval starting at the provided time, in ordering 0.
    Schedules the event to recur at an interval of 1.0 starting at getTime() + 1.0, and at 0 ordering.
    scheduleRepeating(Steppable event, double interval)
    Schedules the event to recur at the specified interval starting at getTime() + interval, and at 0 ordering.
    scheduleRepeating(Steppable event, int ordering, double interval)
    Schedules the event to recur at the specified interval starting at getTime() + interval, and at the provided ordering.
    void
    Seals the schedule: after a schedule is sealed, no further Steppables may be scheduled on it.
    boolean
    step(SimState state)
    Steps the schedule, gathering and ordering all the items to step on the next time step (skipping blank time steps), and then stepping all of them in the decided order.
    double
    Deprecated.
    use getTime()

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • EPOCH

      public static final double EPOCH
      The first possible schedulable time.
      See Also:
    • BEFORE_SIMULATION

      public static final double BEFORE_SIMULATION
      The time which indicates that the Schedule hasn't started yet. Less than EPOCH.
      See Also:
    • AFTER_SIMULATION

      public static final double AFTER_SIMULATION
      The time which indicates that the Schedule is finished. Equal positive infinity, and thus greater than any schedulable time.
      See Also:
    • EPOCH_PLUS_EPSILON

      public static final double EPOCH_PLUS_EPSILON
      The second possible schedulable time.
    • MAXIMUM_INTEGER

      public static final double MAXIMUM_INTEGER
      The last time beyond which the schedule is no longer able to precisely maintain integer values due to loss of precision. That is, MAXIMUM_INTEGER + 1.0 == MAXIMUM_INTEGER.
      See Also:
    • queue

      protected Heap queue
      The Schedule's queue.
    • time

      protected double time
      The current time, as returned by getTime(). If you modify this in a subclass, be sure to synchronize on Schedule.lock first.
    • steps

      protected long steps
      The current steps, as returned by getSteps(). If you modify this in a subclass, be sure to synchronize on Schedule.lock first.
    • sealed

      protected boolean sealed
      Whether the schedule is sealed, as returned by isSealed(). If you modify this in a subclass, be sure to synchronize on Schedule.lock first.
    • lock

      protected Object lock
      The schedule lock. Many methods synchronize on this lock before modifying internal variables.
  • Constructor Details

    • Schedule

      public Schedule()
      Creates a Schedule.
  • Method Details

    • createHeap

      protected Heap createHeap()
      Returns a Heap to be used by the Schedule. By default, returns a binary heap. Override this to provide your own subclass of Heap tuned for your particular problem.
    • time

      public double time()
      Deprecated.
      use getTime()
      Returns the current timestep
    • getTime

      public double getTime()
      Returns the current timestep
    • isSealed

      public boolean isSealed()
      Returns whether or not the schedule is sealed (nothing more can be scheduled, even if the schedule isn't at AFTER_SIMULATION yet). Calling reset() will unseal a Schedule, and calling seal() will seal it.
    • getTimestamp

      public String getTimestamp(String beforeSimulationString, String afterSimulationString)
      Returns the current time in string format. If the time is BEFORE_SIMULATION, then beforeSimulationString is returned. If the time is AFTER_SIMULATION, then afterSimulationString is returned. Otherwise a numerical representation of the time is returned.
    • getTimestamp

      public String getTimestamp(double time, String beforeSimulationString, String afterSimulationString)
      Returns a given time in string format. If the time is earlier than EPOCH (such as BEFORE_SIMULATION), then beforeSimulationString is returned. If the time is AFTER_SIMULATION, then afterSimulationString is returned. Otherwise a numerical representation of the time is returned.
    • getSteps

      public long getSteps()
      Returns the number of steps the Schedule has pulsed so far.
    • clear

      public void clear()
      Empties out the schedule but does not reset the time or steps. If you're looking for a way to kill your simulation from a Steppable, use SimState.kill() instead. Note that any agents presently at THIS TIME STEP will STILL be stepped -- including possibly reinserting themselves in the schedule.
    • seal

      public void seal()
      Seals the schedule: after a schedule is sealed, no further Steppables may be scheduled on it. To unseal a schedule, you must reset() it. If you're looking for a way to kill your simulation from a Steppable, use SimState.kill() instead.
    • reset

      public void reset()
      Empties out the schedule and resets it to a pristine state BEFORE_SIMULATION, with steps = 0. If you're looking for a way to kill your simulation from a Steppable, use SimState.kill() instead.
    • scheduleComplete

      public boolean scheduleComplete()
      Returns true if the schedule has nothing left to do.
    • merge

      public void merge(Schedule other)
      Merge a given schedule into this one. The other schedule is not modified, but the queue of the original schedule is changed. NOTE: this method is not threadsafe and should be only performed when there are NO other threads which might want to manipulate the schedule.
    • addBefore

      public void addBefore(Steppable step)
      Adds a steppable to be called every iteration of the Schedule immediately before any other Steppables are called for that step. You should do this only in your SimState.start() method (super.start() clears out all current steppables of this type). If multiple items are added with this method, no guarantee is made on the order in which the Schedule will call them.
    • addAfter

      public void addAfter(Steppable step)
      Adds a steppable to be called every iteration of the Schedule immediately after any other Steppables are called for that step. You should do this only in your SimState.start() method (super.start() clears out all current steppables of this type). If multiple items are added with this method, no guarantee is made on the order in which the Schedule will call them.
    • step

      public boolean step(SimState state)
      Steps the schedule, gathering and ordering all the items to step on the next time step (skipping blank time steps), and then stepping all of them in the decided order. Returns FALSE if nothing was stepped -- the schedule is exhausted or time has run out.
    • scheduleOnce

      public boolean scheduleOnce(Steppable event)
      Schedules the event to occur at getTime() + 1.0, 0 ordering. If this is a valid time and event, schedules the event and returns TRUE. This method at present returns FALSE if the schedule cannot schedule any more events (it's sealed or the time is AFTER_SIMULATION), or if the event is being scheduled for AFTER_SIMULATION. The method throws an IllegalArgumentException if the event is being scheduled for an invalid time, or is null.
    • scheduleOnceIn

      public boolean scheduleOnceIn(double delta, Steppable event)
      Schedules the event to occur at getTime() + delta, 0 ordering. If this is a valid time and event, schedules the event and returns TRUE. If the delta = 0, then the first event is instead scheduled to occur at getTime() + delta + epsilon (the minimum possible next timestamp). This method at present returns FALSE if the schedule cannot schedule any more events (it's sealed or the time is AFTER_SIMULATION), or if the event is being scheduled for AFTER_SIMULATION. The method throws an IllegalArgumentException if the event is being scheduled for an invalid time, or is null.
    • scheduleOnce

      public boolean scheduleOnce(Steppable event, int ordering)
      Schedules the event to occur at getTime() + 1.0, and in the ordering provided. If this is a valid time and event, schedules the event and returns TRUE. This method at present returns FALSE if the schedule cannot schedule any more events (it's sealed or the time is AFTER_SIMULATION), or if the event is being scheduled for AFTER_SIMULATION. The method throws an IllegalArgumentException if the event is being scheduled for an invalid time, or is null.
    • scheduleOnceIn

      public boolean scheduleOnceIn(double delta, Steppable event, int ordering)
      Schedules the event to occur at getTime() + delta, and in the ordering provided. If this is a valid time and event, schedules the event and returns TRUE. If the delta = 0, then the first event is instead scheduled to occur This method at present returns FALSE if the schedule cannot schedule any more events (it's sealed or the time is AFTER_SIMULATION), or if the event is being scheduled for AFTER_SIMULATION. The method throws an IllegalArgumentException if the event is being scheduled for an invalid time, or is null.
    • scheduleOnce

      public boolean scheduleOnce(double time, Steppable event)
      Schedules the event to occur at the provided time, 0 ordering. If the getTime() == the provided time, then the event is instead scheduled to occur at getTime() + epsilon (the minimum possible next timestamp). If this is a valid time and event, schedules the event and returns TRUE. This method at present returns FALSE if the schedule cannot schedule any more events (it's sealed or the time is AFTER_SIMULATION), or if the event is being scheduled for AFTER_SIMULATION. The method throws an IllegalArgumentException if the event is being scheduled for an invalid time, or is null.
    • scheduleOnce

      public boolean scheduleOnce(double time, int ordering, Steppable event)
      Schedules the event to occur at the provided time, and in the ordering provided. If the getTime() == the provided time, then the event is instead scheduled to occur at getTime() + epsilon (the minimum possible next timestamp). If this is a valid time, ordering, and event, schedules the event and returns TRUE. This method at present returns FALSE if the schedule cannot schedule any more events (it's sealed or the time is AFTER_SIMULATION), or if the event is being scheduled for AFTER_SIMULATION. The method throws an IllegalArgumentException if the event is being scheduled for an invalid time, or is null.
    • _scheduleOnce

      protected boolean _scheduleOnce(Schedule.Key key, Steppable event)
      Schedules an item. You must synchronize on this.lock before calling this method. This allows us to avoid synchronizing twice, and incurring any overhead (not sure if that's an issue really). This method at present returns FALSE if the schedule cannot schedule any more events (it's sealed or the time is AFTER_SIMULATION), or if the event is being scheduled for AFTER_SIMULATION. The method throws an IllegalArgumentException if the event is being scheduled for an invalid time, or is null.
    • scheduleRepeating

      public IterativeRepeat scheduleRepeating(Steppable event)
      Schedules the event to recur at an interval of 1.0 starting at getTime() + 1.0, and at 0 ordering. If this is a valid event, schedules the event and returns a Stoppable, else returns null. The recurrence will continue until getTime() >= AFTER_SIMULATION, the Schedule is cleared out, or the Stoppable's stop() method is called, whichever happens first.

      This method at present returns null if the schedule cannot schedule any more events (it's sealed or the time is AFTER_SIMULATION). The method throws an IllegalArgumentException if the event is being scheduled for an invalid time, or is null.

      Note that calling stop() on the Stoppable will not only stop the repeating, but will also make the Schedule completely forget (lose the pointer to) the Steppable scheduled here. This is particularly useful if you need to make the Schedule NOT serialize certain Steppable objects.

    • scheduleRepeating

      public IterativeRepeat scheduleRepeating(Steppable event, double interval)
      Schedules the event to recur at the specified interval starting at getTime() + interval, and at 0 ordering. If this is a valid interval (must be > 0) and event, schedules the event and returns a Stoppable, else returns null. The recurrence will continue until getTime() >= AFTER_SIMULATION, the Schedule is cleared out, or the Stoppable's stop() method is called, whichever happens first.

      WARNING:Use of this method in start() might lead to confusion. At start(), the timestep is -1 because the first actual timestep in the simulation is 0. This means that if you schedule an agent with this method for interval 10 (say), it won't start at 0 but rather will start at 9. Instead you might want to use scheduleRepeating(0, event, interval).

      This method at present returns null if the schedule cannot schedule any more events (it's sealed or the time is AFTER_SIMULATION). The method throws an IllegalArgumentException if the event is being scheduled for an invalid time, or is null.

      Note that calling stop() on the Stoppable will not only stop the repeating, but will also make the Schedule completely forget (lose the pointer to) the Steppable scheduled here. This is particularly useful if you need to make the Schedule NOT serialize certain Steppable objects.

    • scheduleRepeating

      public IterativeRepeat scheduleRepeating(Steppable event, int ordering, double interval)
      Schedules the event to recur at the specified interval starting at getTime() + interval, and at the provided ordering. If this is a valid interval (must be > 0) and event, schedules the event and returns a Stoppable, else returns null. The recurrence will continue until getTime() >= AFTER_SIMULATION, the Schedule is cleared out, or the Stoppable's stop() method is called, whichever happens first.

      WARNING:Use of this method in start() might lead to confusion. At start(), the timestep is -1 because the first actual timestep in the simulation is 0. This means that if you schedule an agent with this method for interval 10 (say), it won't start at 0 but rather will start at 9. Instead you might want to use scheduleRepeating(0, event, ordering, interval).

      This method at present returns null if the schedule cannot schedule any more events (it's sealed or the time is AFTER_SIMULATION). The method throws an IllegalArgumentException if the event is being scheduled for an invalid time, or is null.

      Note that calling stop() on the Stoppable will not only stop the repeating, but will also make the Schedule completely forget (lose the pointer to) the Steppable scheduled here. This is particularly useful if you need to make the Schedule NOT serialize certain Steppable objects.

    • scheduleRepeating

      public IterativeRepeat scheduleRepeating(double time, Steppable event)
      Schedules the event to recur at the specified interval starting at the provided time, and at 0 ordering. If the getTime() == the provided time, then the first event is instead scheduled to occur at getTime() + epsilon (the minimum possible next timestamp). If this is a valid time, ordering, interval (must be positive), and event, schedules the event and returns a Stoppable, else returns null. The recurrence will continue until getTime() >= AFTER_SIMULATION, the Schedule is cleared out, or the Stoppable's stop() method is called, whichever happens first.

      This method at present returns null if the schedule cannot schedule any more events (it's sealed or the time is AFTER_SIMULATION). The method throws an IllegalArgumentException if the event is being scheduled for an invalid time, or is null.

      Note that calling stop() on the Stoppable will not only stop the repeating, but will also make the Schedule completely forget (lose the pointer to) the Steppable scheduled here. This is particularly useful if you need to make the Schedule NOT serialize certain Steppable objects.

    • scheduleRepeating

      public IterativeRepeat scheduleRepeating(double time, Steppable event, double interval)
      Schedules the event to recur at the specified interval starting at the provided time, in ordering 0. If the getTime() == the provided time, then the first event is instead scheduled to occur at getTime() + epsilon (the minimum possible next timestamp). If this is a valid time, interval (must be > 0), and event, schedules the event and returns a Stoppable, else returns null. The recurrence will continue until getTime() >= AFTER_SIMULATION, the Schedule is cleared out, or the Stoppable's stop() method is called, whichever happens first.

      This method at present returns null if the schedule cannot schedule any more events (it's sealed or the time is AFTER_SIMULATION). The method throws an IllegalArgumentException if the event is being scheduled for an invalid time, or is null.

      Note that calling stop() on the Stoppable will not only stop the repeating, but will also make the Schedule completely forget (lose the pointer to) the Steppable scheduled here. This is particularly useful if you need to make the Schedule NOT serialize certain Steppable objects.

    • scheduleRepeating

      public IterativeRepeat scheduleRepeating(double time, int ordering, Steppable event)
      Schedules the event to recur at an interval of 1.0 starting at the provided time, and in the ordering provided. If the getTime() == the provided time, then the first event is instead scheduled to occur at getTime() + epsilon (the minimum possible next timestamp). If this is a valid time, ordering, and event, schedules the event and returns a Stoppable, else returns null. The recurrence will continue until getTime() >= AFTER_SIMULATION, the Schedule is cleared out, or the Stoppable's stop() method is called, whichever happens first.

      This method at present returns null if the schedule cannot schedule any more events (it's sealed or the time is AFTER_SIMULATION). The method throws an IllegalArgumentException if the event is being scheduled for an invalid time, or is null.

      Note that calling stop() on the Stoppable will not only stop the repeating, but will also make the Schedule completely forget (lose the pointer to) the Steppable scheduled here. This is particularly useful if you need to make the Schedule NOT serialize certain Steppable objects.

    • scheduleRepeating

      public IterativeRepeat scheduleRepeating(double time, int ordering, Steppable event, double interval)
      Schedules the event to recur at the specified interval starting at the provided time, and in the ordering provided. If the getTime() == the provided time, then the first event is instead scheduled to occur at getTime() + epsilon (the minimum possible next timestamp). If this is a valid time, ordering, interval (must be > 0), and event, schedules the event and returns a Stoppable. The recurrence will continue until getTime() >= AFTER_SIMULATION, the Schedule is cleared out, or the Stoppable's stop() method is called, whichever happens first.

      This method at present returns null if the schedule cannot schedule any more events (it's sealed or the time is AFTER_SIMULATION). The method throws an IllegalArgumentException if the event is being scheduled for an invalid time, or is null.

      Note that calling stop() on the Stoppable will not only stop the repeating, but will also make the Schedule completely forget (lose the pointer to) the Steppable scheduled here. This is particularly useful if you need to make the Schedule NOT serialize certain Steppable objects.