27. Process Oriented Simulation
Part of
CS:2820 Object Oriented Software Development Notes, Fall 2017
|
From the point of view of the vehicle in our traffic simulation, a trip looks something like this:
vehicle created at source intersection loop pick outgoing road enter outgoing road reach end of outgoing road arrive at intersection while it is not a sink intersection vehicle destroyed at sink
in more detail:
Intersection currentIntersection Road currentRoad = null; [vehicle created at source intersection, setting currentIntersection] loop [consult road network to pick outgoing road, setting currentRoad] [tell currentIntersection that this car is leaving it] currentIntersection = null; [tell currentRoad that we are entering it, setting travelTime] wait for travel time [tell currentRoad that we're exiting it, setting currentIntersection] [tell currentIntersection that we've arrived, it may make us wait] currentRoad = null; while it is not a sink intersection vehicle destroyed
The material in square brackets above involves interaction between the code for the vehicle and the code for the road network. We're focusing on the vehicle here, so we'll ignore the details of this interaction.
How can we translate this to an object-oriented discrete-event simulation model? We coluld preserve the above control structure if we were using multithreaded programming, but that is a topic for a class in parallel computing. Here, we'll look at how to do this without going to multiple threads.
In an event driven framework, each logical process is denied the right to its own control structure. Instead, each segment of the process schedules the next segment. Let's explore this with class Vehicle:
class Vehicle { Intersection currentIntersection Road currentRoad = null; Vehicle( float t, Intersection i ) { // constructor currentIntersection = i; schedule( t, (float time)->this.pickOutgoing( time ) ); } // head of loop void pickOutgoing( float t ) { // called when vehicle is in an intersection [consult road network to pick outgoing road, setting currentRoad] [tell currentIntersection that this car is leaving it] currentIntersection = null; [tell currentRoad that we are entering it, setting travelTime] schedule( t + travelTime, (float time)->this.leaveRoad( time ) ); } // tail of loop void leaveRoad( float t ) { // called when vehicle arrives at the end of a road [tell currentRoad that we're exiting it, setting currentIntersection] currentRoad = null; [tell currentIntersection that we've arrived, it may make us wait] if is not a sink intersection { schedule( t, (float time)->this.pickOutgoing( time ) ); } else { vehicle destroyed } } }
Event-driven frameworks are very common in modern computing:
Window managers are typically written with mouse events, keypress events, and so on, so applications are written as collections of methods that are called when these events occur. The top level structure of many GUI based applications looks like this:
[ first initialize everything ] while (true) { WindowEvent e = gui.nextEvent(); switch (e.type) { case mouseMove: e.widget().mouseMove( e.mouseCoords() ); break; case mouseClick: e.widget().mousePress( e.mouseButton() ); break; case mouseClick: e.widget().mouseRelease( e.mouseButton() ); break; case keyPress: e.widget().keyPress( e.key() ); break; case keyRelease: e.widget().keyRelease( e.key() ); break; } }
In the above, the GUI software uses its model of screen focus to determine which widget on the screen to deliver the event to. Windows, push buttons, text-editing boxes and similar things are all subclasses of widget. The event notice includes what widget the event occurs in, and what happened in that widget, a mouse move, mouse button click, key press, or key release. The main loop calls the appropriate method of the widget for the particular kind of event, and passes that method the details of the event such as where was the mouse, what mouse button was pressed, or what key on the keyboard was involved. It is up to the programmer to write the event service routines in each widget to make the widget behave in a coherent way in response to the sequence of events reported to it.
Transaction processing servers are typically written with an event-driven structure. When you fill out a web form, you are working locally on your client machine until you hit the submit button. At that point, the completed form is delivered, as a single lump, with a "form completed" event. There is typically one event handler for each type of form that a user might fill out.
The notion of a particular client process state is created by variables maintained on the server or, in some cases, by cookies stored on the client's machine and submitted to the server with the form. The logical process, from the client perspective, is the sequence of forms that the user fills out as they visit a the web site managed by that server.