33. Changing the Simulation Framework

Part of CS:2820 Object Oriented Software Development Notes, Spring 2021
by Douglas W. Jones
THE UNIVERSITY OF IOWA Department of Computer Science

 

Introduction

We have created a road-network simulation and an epidemic simulation using one framework. The interesting question is, can we change the framework without destroying the other work we have done? The answer to this question is, to a large extent, yes. Adopting a simulation framework at the start of a project is important, but within reasonably wide bounds, a change of framework is not outrageously expensive.

In previous semesters, this class has explored more ambitious changes to the simulation framework, but this semester's epidemic project has posed a more pressing issue. Our model schedules many events that are then nullified nullified by future developments. In the posted solutions to the machine problems, we dealt with this using ad-hoc methods. A systematic solution involves a change to the simulation framework.

The Existing Framework

The existing framework allows events to be scheduled as follows:

Simulator.schedule( time, (double t)-> some.method( t, other.stuff ) );

Once scheduled, the framework offers no alternative. At the given time, some.method will be called. There is no way to cancel or reschedule it.

A goal

We would like to retain the existing framework, but also allow something like this:

Event e = Simulator.schedule(
    time, (double t)-> some.method( t, other.stuff )
);

If nothing is done to interfere with e, the behavior of the simulator should be unchanged. However, we want two additional operations. Assuming that the above line of code has been executed, we want to be able to cancel the event e, using something like one of the following:

Simulator.cancel( e );
e.cancel();

If the action for e has already been triggered, this should do nothing, but if the action has not yet been triggered, which is to say, if e is a handle for an event still in the pending event set, then the event e will never occur, which is to say, it will be as if e was never scheduled in the first place.

The second useful operation is rescheduling an event, using something like one of the following:

Simulator.reschedule( e, time );
e.reschedule( time );

Rescheduling an event does not change the associated action, it merely changes the time of that action. In principle, rescheduling is never really needed because you could just cancel the event and then sechedule a new replacement event. Rescheduling offers potential performance improvements because it offers the potential to avoid creating new event objects and allows the action to be specified just once when the event is first created.

An Initial Implementation

It's important to prove that something is feasible before worrying about the best way to do it, so we'll begin with some minimal thinking. Our original class Simulator had a private class Event. Here is the old schedule method using that class:

public static void schedule( double t, Action a ) {
    eventSet.add( new Event( t, a ) );
}

We can rewrite that as follows, making class Event public and returning the scheduled event. If the caller wants to ignore the return value, this is fully compatable with the original.

public static Event schedule( double t, Action a ) {
    Event e = new Event( t, a );
    eventSet.add( e );
    return e;
}

Given the above, we can now add code to cancel a previously scheduled event as follows:

public static void cancel( Event e ) {
    eventSet.remove( e );
}

This takes advantage of the fact that the remove(e) operation on Java's class PriorityQueue returns true if it successfully removes the item, and returns false if the item was not in the queue. Here, we simply ignore the return value in order to make the cancel operation conform to our tentative specification.

The designers of the Java collections framework had the option of making remove(e) throw an exception if e was not in the queue or other kind of collection. Had they done that, our code would have been significantly more awkward.

Rescheduling can be done as follows:

public static void reschedule( Event e, double time ) {
    eventSet.remove( e );
    e.time = time;
    eventSet.add( e );
}

Note that we couldn't just change the time of an object already entered in the priority queue. Changing the time (or other priority indicator) of an object that is in such a data structure is extremely dangerous. For most possible implementatios of priority queues, it destroys fundamental properties of the queue. This is certainly the case for heaps, the foundation of Java's PriorityQueue class.

Critique

The above suggested simulation framework allows the kind of event cancellation and rescheduling that the epidemic simulator needs, but it exposes details of the event set to abuse by users. Making class Event public allows all kinds of mischief, including the possibility that users might change the actions associated with events or change the times of events that are currently in the queue.

Can we expose an Event class outside the simulator that is just a handle on the real thing used inside class Simulator? As it turns out, the answer is yes, but at a cost that we might not want to pay. This is a strong test for the access control mechanisms of a programming language and Java, as it turns out, sits on the edge between being able to meet our requirements and unable to do so.