package umontreal.iro.lecuyer.simprocs;
import java.util.ListIterator;
import umontreal.iro.lecuyer.simevents.Simulator;
import umontreal.iro.lecuyer.simevents.Accumulate;
import umontreal.iro.lecuyer.simevents.LinkedListStat;
import umontreal.iro.lecuyer.stat.Tally;
import umontreal.iro.lecuyer.util.PrintfFormat;
/**
* A Bin corresponds to a pile of identical tokens, and a list of
* processes waiting for the tokens when the list is empty.
* It is a producer/consumer process synchronization device.
* Tokens can be added to the pile (i.e., produced) by the
* method {@link #put put}.
* A process can request tokens from the pile (i.e., consume)
* by calling {@link #take take}.
*
*
* The behavior of a Bin is somewhat similar to that of a
* {@link Resource}. Each Bin has a single queue of waiting
* processes, with FIFO or LIFO service policy, and
* which can be accessed via the method {@link #waitList waitList}.
* This list actually contains objects of the class {@link UserRecord}.
* Each {@link UserRecord} points to a process
* and contains some additional information.
*
*/
public class Bin {
private static final int FIFO = 1;
private static final int LIFO = 2;
// Variables
private ProcessSimulator sim;
private String name;
private int available = 0;
private int policy = FIFO;
private LinkedListStat waitingList;
private ListIterator iter;
private Accumulate statAvail;
private boolean stats;
private double initStatTime;
/**
* Constructs a new bin, initially empty, with
* service policy FIFO and linked with the default simulator.
*
*/
public Bin() {
this ("");
}
/**
* Constructs a new bin, initially empty, with
* service policy FIFO and linked with simulator sim.
*
* @param sim Simulator associated to the current variable.
*
*
*/
public Bin(ProcessSimulator sim) {
this (sim, "");
}
/**
* Constructs a new bin, initially empty, with service policy FIFO,
* identifier name and linked with the default simulator.
*
* @param name name associated to the bin
*
*
*/
public Bin (String name) {
try {
ProcessSimulator.initDefault();
this.sim = (ProcessSimulator)Simulator.getDefaultSimulator();
waitingList = new LinkedListStat(sim);
this.name = name;
iter = waitingList.listIterator();
stats = false;
}
catch (ClassCastException e) {
throw new IllegalArgumentException("Wrong default Simulator type");
}
}
/**
* Constructs a new bin, initially empty, with
* service policy FIFO, identifier name and linked with simulator sim.
*
* @param sim Simulator associated to the current variable.
*
* @param name name associated to the bin
*
*/
public Bin (ProcessSimulator sim, String name) {
this.sim = sim;
waitingList = new LinkedListStat(sim);
this.name = name;
iter = waitingList.listIterator();
stats = false;
}
/**
* Reinitializes this bin by clearing up its pile of tokens and
* its waiting list.
* The processes in this list (if any) remain in the same states.
*
*/
public void init() {
int oldAvail = available;
waitingList.clear();
available = 0;
if (stats) initStat();
}
/**
* Starts or stops collecting statistics on the list returned
* by {@link #waitList waitList} for this bin.
* If the statistical collection is turned ON, It also constructs (if not yet done)
* and initializes an additional statistical
* collector of class
* {@link umontreal.iro.lecuyer.simevents.Accumulate Accumulate}
* for this bin. This collector will be updated
* automatically. It can be accessed via {@link #statOnAvail statOnAvail}, and
* monitors the evolution of the
* available tokens of the bin
* as a function of time.
*
* @param b true to turn statistical collection ON, false to turn it OFF
*
*
*/
public void setStatCollecting (boolean b) {
if (b) {
if (stats)
throw new IllegalStateException ("Already collecting statistics for this resource");
stats = true;
waitingList.setStatCollecting (true);
if (statAvail != null)
initStat();
else {
statAvail = new Accumulate (sim, "StatOnAvail");
statAvail.update (available);
}
}
else {
if (stats)
throw new IllegalStateException ("Not collecting statistics for this resource");
stats = false;
waitingList.setStatCollecting (false);
}
}
/**
* Reinitializes all the statistical collectors for this
* bin. These collectors must exist, i.e.,
* {@link #setStatCollecting setStatCollecting} (true) must have been invoked earlier for
* this bin.
*
*/
public void initStat() {
if (!stats) throw new IllegalStateException (
"Not collecting statistics for this resource");
statAvail.init();
statAvail.update (available);
waitingList.initStat();
initStatTime = sim.time();
}
/**
* Sets the service policy for ordering processes waiting
* for tokens on the bin to FIFO (first in, first out):
* the processes are placed in the
* list (and served) according to their order of arrival.
*
*/
public void setPolicyFIFO() {
policy = FIFO;
}
/**
* Sets the service policy for ordering processes waiting
* for tokens on the bin to LIFO (last in, first out):
* the processes are placed in the
* list (and served) according to their inverse order of arrival:
* the last arrived are served first.
*
*/
public void setPolicyLIFO() {
policy = LIFO;
}
/**
* Returns the number of available tokens for this bin.
*
* @return the number of available tokens
*
*/
public int getAvailable() {
return available;
}
/**
* The executing process invoking this method requests
* n tokens from this bin. If enough tokens are available,
* the number of tokens in the bin is reduced by n and the
* process continues its execution.
* Otherwise, the executing process is placed into the
* {@link #waitList waitList} list (the queue) for this bin and is suspended
* until it can obtain the requested number of tokens.
*
* @param n number of requested tokens
*
*
*/
public void take (int n) {
SimProcess p = sim.currentProcess();
if (n <= available) {
// The process gets the resource right away.
available -= n;
if (stats) {
statAvail.update (available);
waitingList.statSojourn().add (0.0);
}
}
else {
// Not enough units of the resource are available.
// The process joins the queue waitingList;
UserRecord record = new UserRecord (n, p, sim.time());
switch (policy) {
case FIFO : waitingList.addLast (record); break;
case LIFO : waitingList.addFirst (record); break;
default : throw new IllegalStateException (
"the policy must be LIFO or FIFO");
}
p.suspend();
}
}
/**
* Adds n tokens to this bin.
* If there are processes waiting for tokens from this bin and
* whose requests can now be satisfied, they obtain the tokens and
* resume their execution.
*
* @param n number of tokens to put into the bin
*
*
*/
public void put (int n) {
available+=n;
if (stats) statAvail.update (available);
if (waitingList.size()>0) wakeProcess();
}
private void wakeProcess() {
UserRecord record;
ListIterator iter = waitingList.listIterator();
while (iter.hasNext() && available > 0) {
record=iter.next();
if (!record.process.isAlive())
throw new IllegalStateException(
"process not alive");
// The thread for this process is still alive.
if (record.numUnits <= available) {
// This request can now be satisfied
record.process.resume();
available -= record.numUnits;
if (stats) statAvail.update (available);
iter.remove();
}
}
}
/**
* Returns the list of {@link UserRecord}
* for the processes waiting for tokens from this bin.
*
* @return the list of waiting process user records
*
*/
public LinkedListStat waitList() {
return waitingList;
}
/**
* Returns the statistical collector for the available tokens
* on the bin as a function of time.
* This collector exists only if {@link #setStatCollecting setStatCollecting} (true)
* has been invoked previously.
*
* @return the probe for available tokens
*
*/
public Accumulate statOnAvail() {
return statAvail;
}
/**
* Returns a string containing a complete statistical report on
* this bin. The method
*
{@link #setStatCollecting setStatCollecting} (true) must have
* been invoked previously, otherwise no statistics will have been collected.
* The report contains statistics on the available tokens, queue size and
* waiting time for this bin.
*
* @return a statistical report for this bin, represented as a string
*
*/
public String report() {
if (statAvail == null) throw new IllegalStateException ("Asking a report for a bin "
+"for which setStatCollecting (true) has not been called");
Accumulate sizeWait = waitingList.statSize();
Tally sojWait = waitingList.statSojourn();
PrintfFormat str = new PrintfFormat();
str.append ("REPORT ON BIN : ").append (name).append (PrintfFormat.NEWLINE);
str.append (" From time : ").append (7, 2, 2, initStatTime);
str.append (" to time : ");
str.append (10,2, 2, sim.time());
str.append (PrintfFormat.NEWLINE + " min max average ");
str.append ("standard dev. nb. obs.");
str.append (PrintfFormat.NEWLINE + " Available tokens ");
str.append (8, (int)(statAvail.min()+0.5));
str.append (11, (int)(statAvail.max()+0.5));
str.append (12, 3, 2, statAvail.average());
str.append (PrintfFormat.NEWLINE + " Queue Size ");
str.append (8, (int)(sizeWait.min()+0.5));
str.append (11, (int)(sizeWait.max()+0.5));
str.append (12, 3, 2, sizeWait.average());
str.append (PrintfFormat.NEWLINE + " Wait ");
str.append (12, 3, 2, sojWait.min()).append (' ');
str.append (10, 3, 2, sojWait.max()).append (' ');
str.append (11, 3, 2, sojWait.average()).append (' ');
str.append (10, 3, 2, sojWait.standardDeviation());
str.append (10, sojWait.numberObs());
return str.toString();
}
/**
* Returns the current simulator of this continuous-time variable.
*
* @return the current simulator of the variable
*
*/
public Simulator simulator() {
return sim;
}
/**
* Set the current simulator of this continuous-time variable.
* This method must not be called while a simulator is running.
*
* @param sim the simulator of the current variable
*
*
*/
public void setSimulator (ProcessSimulator sim) {
this.sim = sim;
}
}
Ads help maintain this website.