APPENDICES
Appendix A: Virtual Scheduler
The JSIM library provides for both pseudo-real time simulation and virtual time simulation. Virtual time simulation is suitable for fast simulations where the user does not wish to be constrained by the speed of a real time clock. Consequently, a virtual clock is used instead of a real time clock. Virtual time simulations are also suitable when the simulation model requires substantial computation. From the analysis of simulation output point view, the virtual time simulation is not only fast but also more accurate. Pseudo-real time simulation uses the system clock to keep tracking time. The location of the System.currentTimeMillis statement in the program directly affects the time results, which, in turn, will affect the simulation output results as well.
The VirtualScheduler class manages the scheduling of entities generating events on the basis of the time at which the entity is supposed to cause the event. VirtualScheduler has a Future Event List (FEL), implemented using a TemporalQueue which it uses to schedule entities. The scheduling of entities on the FEL is done on the basis of each entity's/process's activation time. The activation time of an entity is the time at which it is supposed to generate its next event. To do this, the VirtualScheduler has methods that are counterparts to Java's Thread methods, namely vStart, vStop, vSuspend, vResume and vSleep instead of the start, stop, suspend, resume and sleep methods of the Thread class.
The vStart method is invoked by the Source class during virtual time simulation to schedule a newly created entity in the VirtualScheduler. During virtual time simulation, we need to ensure that only one thread is runnable at any given point in time.
This is done by making VirtualScheduler the singular point of control in the simulation system. Every time one of the above mentioned VirtualScheduler methods is invoked, control is handed over to the VirtualScheduler which decides on the next entity to be activated based on activation time. The vStop method is invoked by an entity/process that has completed its life cycle in the simulation and is ready to be terminated. The vSuspend and vResume methods are used during virtual time simulation instead of suspend and resume methods respectively, in order to ensure that the VirtualScheduler controls all scheduling decisions. The vCurrentTime method provides the current value of the virtual clock and is the virtual time counterpart to Java's System.currentTimeMillis method that is used during the pseudo-real time simulation.
The scheduling procedure is mainly done in the vSleep method in the VirtualScheduler. From the following piece of vSleep code, the currectProcess is the process need to be scheduled. This method first sets up the activation time of this process, and then put the process into FEL. After this has been done, the vSleep wakes up the next process in front of the FEL. In this way, it guarantees that only one simulation thread is alive at any moment of time. The possible race conditions are eliminated. The only problem remained is that the nextProcess is scheduled only if the currentProcess needs to ‘sleep’. If currentProcess is caught by a Sink, the nextProcess will stay in FEL forever. To resolve this problem, a lower priority thread, virtualThread is created in VirtualScheduler. The run method of the thread is shown as following:
void vSleep (double timeDelay, Thread currentProcess)
{
double actTime; // activation time
TQ_Node firstNode = null; // dequeued node
Thread nextProcess = null; // next process on event list
Q_Node qPos; // node containing enqueued item
/**************************************************
* Insert the event into the Future Event List.
*/
actTime = vTime + timeDelay;
qPos = eventList.enqueue (currentProcess, actTime,
Thread.NORM_PRIORITY);
if (currentProcess instanceof SimObject) {
SimObject entity = (SimObject) currentProcess;
entity.setQueuePos (qPos);
}; // if
/***************************************************
* Remove the imminent event from the FEL.
*/
if (! eventList.empty () ) {
firstNode = (TQ_Node)eventList.dequeue();
nextProcess = (Thread)firstNode.getItem();
vTime = firstNode.getTime ();
resume ( nextProcess );
}
else {
err.tell ("vSleep: the Future Event List is empty");
}; // if
if ( ! nextProcess.equals (currentProcess)){
vSuspend ( currentProcess );
}; // if
}; // vSleep
public void run ()
{
while ( ! eventListEmpty () ){
wakeUpOne ();
virtualThread.yield ();
}; // while
}; // run
When the previous situation happens, the virtualThread will have the chance to run and it can wake up the nextProcess, The virtualThread immediately yields to the process waken up by it. A content switch is need for the virtualThread to wake up the nextProcess. This will slow down the simulation process. By current design, the most of scheduling task is done in the vSleep method. This means that the number of content switch has been reduced to its minimum value.
In the JSIM implementation, at most three threads will be active simultaneously (the virtual scheduler thread, the animation display thread, and the current simulation thread). Simulation threads may be a simulation entity (e.g., an ER patient), an entity source or a signal. Since only one simulation thread can be active at a time, race conditions as well as complex synchronization code can be avoided. The virtualThread loops until the FEL is empty, pulling the next thread activation off the FEL. Since the virtualThread runs at the lowest priority, the activated thread will take control until it finishes. In addition, the animation display thread wakes up every so many milliseconds reads the state of the simulation to display it graphically. This thread operates relatively independently and in particular never modifies the simulation state. Indeed, the animation could be placed in a separate package or bean, without too much of an adverse effect on performance.
Appendix: B
Bank.java
and Client.java and Client.java generated by jmodel/************************************************************
* Bank.java
* JSIM Bank simulation model
*/
import java.awt.*;
import jsim.process.*;
import jsim.queue.*;
import jsim.variate.*;
public class Bank extends Model
{
/**
* Define inner classes (one for each entity type).
*/
public class ClientMaker extends EntityMaker
{
public ClientMaker (Bank env)
{ super (env); };
public SimObject makeEntity ()
{ return new Client ((Bank) env); };
}; // inner class
/**
* Define nodes.
*/
public Transport toBank = new Transport (90, 70, 145, 170,
200, 70);
Transport [] edges1 = {toBank};
public Source genClient = new Source (
"genClient",
new Point (50, 50),
this,
1000,
new ClientMaker (this),
new Exponential (2500.0, 0),
edges1 );
public Transport frBank = new Transport (310, 70, 390, 70);
Transport [] edges2 = {frBank};
public Facility bank = new Facility (
"bank",
new Point (200, 50),
this,
1,
new FIFO_Queue (),
new Exponential (1700.0, 0),
edges2 );
public Sink endClient = new Sink (
"endClient",
new Point (380, 50),
this,
0,
null );
/*******************************************************
* Contruct a Bank simulation model.
*/
public Bank ()
{
super ("Bank");
super.start ();
}; // Bank
}; // class
/***********************************************************
* Client.java
* JSIM Client entity
*/
import jsim.process.*;
import jsim.variate.*;
public class Client extends SimObject
{
public Client (Bank env) { super (env); };
public void run ()
{
Bank e = (Bank) env;
try {
for (e.toBank.join (this); e.toBank.move (this); );
e.bank.request (this);
e.bank.use (this);
for (e.frBank.join (this); e.frBank.move (this); );
e.endClient.use (this);
} catch (EntityException ee) {};
}; // run
}; // class
Appendix C: BankModel.java and Client.java
/******************************************************************
* This is a class which generalizes a Bank bean. This bean has one
* Source, one Facility with one token, and one Sink. It has listener
* to listen to the BatchEvent fired by ModelManager and respones to
* ModelManager with the BatchDataEvent.
*/
public class BankModel extends ModelBean implements Serializable,
ActionListener, Runnable,
ModelActionEventListener {
/******************************************************************
* Adjustable properties
*/
private double arrivalTime = 2500.0;
private double serviceTime = 1700.0;
private double transPeriod = 0.0;
private int numTokens = 1;
private int clientNum = 0;
private String sinkLabel = "endClient";
private String sourceLabel = "genClient";
private String facilityLabel = "bank";
private Variate sourceDist = new Exponential(2500.0, 0);
private Variate serviceDist = new Exponential(1700.0, 0);;
/**
* Stopping rule 1: entitiesCreated < numTokens
* Stopping rule 2: env.clock.time () < stopTime
*/
private double stopTime = 1000000000000.0;
private int stopCount = 7864;
/*****************************************************************/
public Transport toBank;
public Transport frBank;
public Source genClient;
public Facility bank;
public Sink endClient;
/**
* Define inner classes (one for each entity type).
*/
public class ClientMaker extends EntityMaker
{
public ClientMaker (Model model)
{ super (model); };
public SimObject makeEntity ()
{ return new Client (model, BankModel.this); };
}; // inner class
/******************************************************************
* Constructs a bank bean with a default name.
*/
public BankModel ()
{
this("Bank");
}
/******************************************************************
* Constructs a bank bean.
*/
public BankModel (String label)
{
super(label);
this.label = label;
adBtn.addActionListener(this);
location = sinkLabel + "Sink";
}; // BankModel
/*****************************************************************
* Methods for initializing and starting the model
*/
/**
* Initialize the nodes of the model
*
* @param batched boolean to show if batched method is used
*/
public void initModel (boolean batched)
{
/**
* Define nodes.
*/
toBank = new Transport (90, 70, 145, 170, 200, 70);
Transport [] edges1 = {toBank};
genClient = new Source (
sourceLabel,
new Point (50, 50),
model,
stopCount, // stopCount = 100
stopTime, // stopTime = 1000,000
new ClientMaker (model),
sourceDist, // arrivalTime= 2500.0
edges1,
batched );
frBank = new Transport (310, 70, 400, 70);
Transport [] edges2 = {frBank};
bank = new Facility (
facilityLabel,
new Point (200, 50),
model,
numTokens, // numTokens = 1
new FIFO_Queue (),
serviceDist, // serviceTime = 2000.0
edges2,
batched );
endClient = new Sink (
sinkLabel,
new Point (380, 50),
model,
clientNum,
null,
batched );
}; // initModel
/*****************************************************************
* Methods for advertising model and setup the properties of the
* model
*/
/**
* Setup the properties of model
*
* @param data The hashtable contains properites of model sent
* by scenario agent.
*/
public void setModelProperties(Hashtable data){
Vector v, t;
Object u;
// set Source node
v = (Vector)data.get(sourceLabel);
setStopTime( ( (Double)v.get(0) ).doubleValue() );
setStopCount( ( (Double)v.get(1) ).intValue() );
sourceDist = IniModel.setDistribution( (Vector)v.get(2) );
// set Facility node
v = (Vector)data.get(facilityLabel);
setNumTokens( ( (Double)v.get(0) ).intValue() );
serviceDist = IniModel.setDistribution( (Vector)v.get(1) );
// set Sink node
v = (Vector)data.get(sinkLabel);
setClientNum( ( (Double)v.get(0) ).intValue() );
// set data collecting location
setCollectLocation( (String)data.get("Location") );
}; // setModelProperties
/**
* collect the properties of model
*
* @param data The hashtable contains properites of model sent
* by scenario agent.
*/
public void getModelProperties( ){
Vector v = new Vector();
v.addElement("Exponential");
System.out.println("ad " + arrivalTime );
v.addElement(new Double(arrivalTime));
v.addElement(new Integer(0));
ad.setSource(sourceLabel, stopTime, stopCount, v);
v = new Vector();
v.addElement("Normal");
System.out.println("ad " + serviceTime );
v.addElement(new Double(serviceTime));
v.addElement(new Double(0.2));
v.addElement(new Integer(0));
ad.setFacility(facilityLabel, numTokens, v);
ad.setSink(sinkLabel, clientNum);
ad.setLocAndSerNum ();
}; // getModelProperties
/******************************************************************
* Set and get methods for properties
*/
public void setArrivalTime (double val) { arrivalTime = val; };
public double getArrivalTime () { return arrivalTime; };
public void setServiceTime (double val) { serviceTime = val; };
public double getServiceTime () { return serviceTime; };
public void setNumTokens (int val) { numTokens = val; };
public int getNunTokens () { return numTokens; };
public void setStopCount(int val) { stopCount = val; };
public int getStopCount () { return stopCount; };
public void setStopTime(double val) { stopTime = val; };
public double getStopTime () { return stopTime; };
public void setClientNum(int val) { clientNum = val; };
public int getClientNum() { return clientNum; };
public void setSinkLabel(String val) { sinkLabel = val; };
public String getSinkLabel() { return sinkLabel; };
public void setFacilityLabel(String val) { facilityLabel = val;};
public String getFacilityLabel() { return facilityLabel; };
public void setSourceLabel(String val) { sourceLabel = val; };
public String getSourceLabel() { return sourceLabel; };
public static void main(String argv[])
{
BankModel b = new BankModel ("Bank");
b.advertiserModel();
}
}; // class
/******************************************************************
* This is a class which generalizes all client instances for the
* BankModel bean. Its constructor has two arguments, one is Model,
* the other is BankModel which is the evironment for Simulation.
*/
public class Client extends SimObject
{
private BankModel e;
/**
* Construct a Client instance
* @param model the simulation environment
* @param bank the bank model
*/
public Client (Model model, BankModel bank){
super (model);
e = bank;
};
/**
* The run method to implement the Runnable interface
*/
public void run ()
{
try {
for (e.toBank.join (this); e.toBank.move (this); );
e.bank.request (this);
e.bank.use (this);
for (e.frBank.join (this); e.frBank.move (this); );
e.endClient.use (this);
} catch (EntityException ee) {};
}; // run
}; // class