package umontreal.iro.lecuyer.simprocs;

import java.util.ListIterator;
import umontreal.iro.lecuyer.util.PrintfFormat;
// import umontreal.iro.lecuyer.simevents.Simulator;
import umontreal.iro.lecuyer.simevents.LinkedListStat;
import umontreal.iro.lecuyer.simevents.Accumulate;
import umontreal.iro.lecuyer.simprocs.ProcessSimulator;
import umontreal.iro.lecuyer.stat.Tally;


/**
 * Objects of this class are resources having limited capacity,
 * and which can be requested and released by {@link Process} objects.
 * These resources act indirectly as synchonization devices for
 * processes.
 * 
 * 

* A resource is created with a finite capacity, specified when * invoking the {@link #Resource(int) Resource} constructor, and can be changed * later on. A resource also has an infinite-capacity queue * (waiting line) and a service policy that defaults to FIFO and * can be changed later on. * *

* A process must ask for a certain number of units of the resource * ({@link #request request}), and obtain it before using it. * When it is done with the resource, the process must release * it so that others can use it ({@link #release release}). * A process does not have to request [release] all the resource * units that it needs by a single call to the {@link #request request} * [{@link #release release}] method. It can make several successive requests * or releases, and can also hold different resources simultaneously. * *

* Each resource maintains two lists: one contains the processes * waiting for the resource (the waiting queue) and the other contains * the processes currently holding units of this resource. * The methods {@link #waitList waitList} and {@link #servList servList} permit one to * access these two lists. * These lists actually contain objects of the class {@link UserRecord} * instead of {@link SimProcess} objects. * */ public class Resource { private static final int FIFO = 1; private static final int LIFO = 2; private ProcessSimulator sim; private String name; private int capacity = 0; private int available = 0; private int policy = FIFO; private LinkedListStat serviceList; private LinkedListStat waitingList; private boolean stats = false; private double initStatTime; private Accumulate statUtil; private Accumulate statCapacity; private Tally statSojourn; /** * Constructs a new resource linked with the default simulator, * with initial capacity capacity, and service policy FIFO. * * @param capacity initial capacity of the resource * * */ public Resource (int capacity) { this (capacity, ""); } /** * Constructs a new resource linked with the simulator * sim, with initial capacity capacity, and service policy FIFO. * * @param sim current simulator of the variable * * @param capacity initial capacity of the resource * * */ public Resource (ProcessSimulator sim, int capacity) { this (sim, capacity, ""); } /** * Constructs a new resource linked with the default simulator, with * initial capacity capacity, service policy FIFO, and identifier * name. * * @param capacity initial capacity of the resource * * @param name identifier or name of the resource * * */ public Resource (int capacity, String name) { try { ProcessSimulator.initDefault(); this.sim = (ProcessSimulator)ProcessSimulator.getDefaultSimulator(); this.capacity = available = capacity; this.name = name; serviceList = new LinkedListStat ( sim," Service List for " + name); waitingList = new LinkedListStat ( sim," Waiting List for " + name); } catch (ClassCastException e) { throw new IllegalArgumentException("Wrong default Simulator type"); } } /** * Constructs a new resource linked with the simulator sim, * with initial capacity capacity, * service policy FIFO, and identifier (or name) name. * * @param sim current simulator of the variable * * @param capacity initial capacity of the resource * * @param name identifier or name of the resource * */ public Resource (ProcessSimulator sim, int capacity, String name) { this.capacity = available = capacity; this.name = name; this.sim = sim; serviceList = new LinkedListStat ( sim," Service List for " + name); waitingList = new LinkedListStat ( sim," Waiting List for " + name); } /** * Starts or stops collecting statistics on the lists returned * by {@link #waitList waitList} and {@link #servList servList} for this resource. * If the statistical collection is turned ON, the method * also constructs (if not yet done) * and initializes three additional statistical * collectors for this resource. These collectors will be updated * automatically. They can be accessed via {@link #statOnCapacity statOnCapacity}, * {@link #statOnUtil statOnUtil}, and {@link #statOnSojourn statOnSojourn}, respectively. * The first two, of class * {@link umontreal.iro.lecuyer.simevents.Accumulate Accumulate}, * monitor the evolution of the * capacity and of the utilization (number of units busy) of the resource * as a function of time. * The third one, of class {@link umontreal.iro.lecuyer.stat.Tally Tally}, * collects statistics on the * processes sojourn times (wait + service); it samples a new value * each time a process releases all the units of this resource that it * holds (i.e., when its {@link UserRecord} disappears). * * @param b true to turn statistical collecting 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); serviceList.setStatCollecting (true); if (statUtil != null) initStat(); else { statUtil = new Accumulate (sim, "StatOnUtil"); statUtil.update (capacity - available); statCapacity = new Accumulate (sim, "StatOnCapacity"); statCapacity.update (capacity); statSojourn = new Tally ("StatOnSojourn"); } } else { if (stats) throw new IllegalStateException ("Not collecting statistics for this resource"); stats = false; waitingList.setStatCollecting (false); serviceList.setStatCollecting (false); } } /** * Reinitializes all the statistical collectors for this * resource. These collectors must exist, i.e., * {@link #setStatCollecting setStatCollecting} (true) must have been invoked earlier for * this resource. * */ public void initStat() { if (!stats) throw new IllegalStateException ( "Not collecting statistics for this resource"); statUtil.init(); statUtil.update (capacity - available); statCapacity.init(); statCapacity.update (capacity); statSojourn.init(); serviceList.initStat(); waitingList.initStat(); initStatTime = sim.time(); } /** * Reinitializes this resource by clearing up its waiting list * and service list. The processes that were in these lists (if any) * remain in the same states. If statistical collection is ``on'', * {@link #initStat initStat} is invoked as well. * */ public void init() { serviceList.clear(); waitingList.clear(); available = capacity; if (stats) initStat(); } /** * Returns the current capacity of the resource. * * @return the capacity of the resource * */ public int getCapacity() { return capacity; } /** * Set the service policy 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; } /** * Set the service policy 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; } /** * Modifies by diff units (increases if diff > 0, * decreases if diff < 0) the capacity (i.e., the number of units) * of the resource. * If diff > 0 and there are processes in the waiting list whose * request can now be satisfied, they obtain the resource. * If diff < 0, there must be at least diff units of this * resource available, otherwise an illegal argument exception will be thrown, * printing an error message (this is not a strong limitation, because one * can check first and release some units, if needed, before invoking * changeCapacity). * In particular, the capacity of a resource can never become negative. * * @param diff number of capacity units to add to the actual capacity, * can be negative to reduce the capacity * * @exception IllegalArgumentException if diff is negative and * the capacity cannot be reduced as required * * @exception IllegalStateException if diff is negative and * the capacity could not be reduced due to a lack of available units * * */ public void changeCapacity (int diff) { if (diff > 0) { available += diff; capacity += diff; if (waitingList.size() > 0) startNewCust(); } else { if (-diff > available) throw new IllegalArgumentException("Trying to diminish the capacity " + "of a resource more than its current availability"); available -= -diff; capacity -= -diff; } if (stats) statCapacity.update (capacity); } /** * Sets the capacity to newcap. * Equivalent to {@link #changeCapacity changeCapacity} (newcap - old) if old is the * current capacity. * * @param newcap new capacity of the resource * * @exception IllegalArgumentException if the passed capacity is negative * * @exception IllegalStateException if diff is negative and * the capacity could not be reduced due to a lack of available units * * */ public void setCapacity (int newcap) { if (newcap < 0) throw new IllegalArgumentException ("capacity cannot be negative"); changeCapacity (newcap - capacity); } /** * Returns the number of available units, i.e., the capacity * minus the number of units busy. * * @return the number of available units * */ public int getAvailable() { return available; } /** * The executing process invoking this method requests for * n units of this resource. If there are enough units available * to fill up the request immediately, the process obtains the desired * number of units and holds them until it invokes {@link #release release} * for this same resource. The process is also inserted into the * {@link #servList servList} list for this resource. * On the other hand, if there are less than n units of this * resource available, the executing process is placed into the * {@link #waitList waitList} list (the queue) for this resource and is suspended * until it can obtain the requested number of units of the resource. * * @param n number of required units * */ public void request (int n) { SimProcess p = sim.currentProcess(); UserRecord record = new UserRecord (n, p, sim.time()); if (n <= available) { // The process gets the resource right away. available -= n; serviceList.addLast (record); if (stats) { waitingList.statSojourn().add (0.0); statUtil.update (capacity - available); } } else { // Not enough units of the resource are available. // The process joins the queue waitingList; switch (policy) { case FIFO : waitingList.addLast (record); break; case LIFO : waitingList.addFirst (record); break; default : throw new IllegalStateException( "policy must be FIFO or LIFO"); } p.suspend(); } } // Called by \texttt{release}. private void startNewCust() { UserRecord record; ListIterator iterWait = waitingList.listIterator(); while (iterWait.hasNext() && available > 0) { record = iterWait.next(); if (record.process.getState() == SimProcess.DEAD) iterWait.remove(); // the process was killed, so we remove it from the waiting list. // or maybe we stop the program by throwing IllegalStateException //"Resource.startNewCust: process not alive"); // The thread for this process is still alive. else if (record.numUnits <= available) { // This request can now be satisfied. serviceList.addLast (record); record.process.resume(); available -= record.numUnits; iterWait.remove(); } } } /** * The executing process that invokes this method releases * n units of the resource. If this process is holding exactly * n units, it is removed from the service list of this resource * and its {@link UserRecord} object disappears. * If this process is holding less than n units, * the program throws an illegal argument exception. * If there are other processes waiting for this resource whose requests * can now be satisfied, they obtain the resource. * * @param n number of released units * * @exception IllegalArgumentException if the process did not request n * units before releasing them * * */ public void release (int n) { SimProcess p = sim.currentProcess(); int temp = 0; UserRecord record; ListIterator iterServ = serviceList.listIterator(); while (temp 0) startNewCust(); if (stats) statUtil.update (capacity - available); } /** * Returns the list that contains the * {@link UserRecord} objects * for the processes in the waiting list for this resource. * * @return the list of process user records waiting for the resource * */ public LinkedListStat waitList() { return waitingList; } /** * Returns the list that contains the * {@link UserRecord} objects * for the processes in the service list for this resource. * * @return the list of process user records using this resource * */ public LinkedListStat servList() { return serviceList; } /** * Returns the statistical collector that measures the evolution of * the capacity of the resource as a function of time. * This collector exists only if {@link #setStatCollecting setStatCollecting} (true) has been invoked * previously. * * @return the probe for resource capacity * */ public Accumulate statOnCapacity() { return statCapacity; } /** * Returns the statistical collector for the utilization * of the resource (number of units busy) as a function of time. * This collector exists only if {@link #setStatCollecting setStatCollecting} (true) * has been invoked previously. * The utilization rate of a resource can be obtained as the * time average computed by this collector, divided by the * capacity of the resource. * The collector returned by {@link #servList() servList()}.{@link umontreal.iro.lecuyer.simevents.LinkedListStat#statSize() statSize()} * tracks the number of {@link UserRecord} * in the service list; * it differs from this collector because a process may hold more than one * unit of the resource by given {@link UserRecord}. * * @return the probe for resource utilization * */ public Accumulate statOnUtil() { return statUtil; } /** * Returns the statistical collector for the sojourn times of * the {@link UserRecord} for this resource. * This collector exists only if {@link #setStatCollecting setStatCollecting} (true) has been invoked * previously. * It gets a new observation each time a process releases all the units * of this resource that it had requested by a single {@link #request request} * call. * * @return the probe for the sojourn times * */ public Tally statOnSojourn() { return statSojourn; } /** * Returns the name (or identifier) associated to this * resource. If it was not given upon resource construction, this returns * null. * * @return the name associated to this resource, or null if it was not given * */ public String getName() { return name; } /** * Returns a string containing a complete statistical report on this * resource. The method {@link #setStatCollecting setStatCollecting} (true) must have been invoked * previously, otherwise no statistics have been collected. * The report contains statistics on the waiting times, service * times, and waiting times for this resource, on the capacity, * number of units busy, and size of the queue as a function of time, * and on the utilization rate. * * @return a statistical report for this resource, represented as a string */ public String report() { if (statUtil == null) throw new IllegalStateException ("Asking a report for a resource " +"for which setStatCollecting (true) has not been called"); Accumulate sizeWait = waitingList.statSize(); Tally sojWait = waitingList.statSojourn(); Tally sojServ = serviceList.statSojourn(); PrintfFormat str = new PrintfFormat(); str.append ("REPORT ON RESOURCE : ").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 + " Capacity "); str.append (8, (int)(statCapacity.min()+0.5)); str.append (11, (int)(statCapacity.max()+0.5)); str.append (12, 3, 2, statCapacity.average()); str.append (PrintfFormat.NEWLINE + " Utilization "); str.append (8, (int)(statUtil.min()+0.5)); str.append (11, (int)(statUtil.max()+0.5)); str.append (12, 3, 2, statUtil.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()); str.append (PrintfFormat.NEWLINE + " Service "); str.append (12, 3, 2, sojServ.min()).append (' '); str.append (10, 3, 2, sojServ.max()).append (' '); str.append (11, 3, 2, sojServ.average()).append (' '); str.append (10, 3, 2, sojServ.standardDeviation()); str.append (10, sojServ.numberObs()); str.append (PrintfFormat.NEWLINE + " Sojourn "); str.append (12, 3, 2, statSojourn.min()).append (' '); str.append (10, 3, 2, statSojourn.max()).append (' '); str.append (11, 3, 2, statSojourn.average()).append (' '); str.append (10, 3, 2, statSojourn.standardDeviation()); str.append (10, statSojourn.numberObs()).append (PrintfFormat.NEWLINE); return str.toString(); } }

Ads help maintain this website.