All Downloads are FREE. Search and download functionalities are using the official Maven repository.

umontreal.iro.lecuyer.simprocs.Resource Maven / Gradle / Ivy

Go to download

SSJ is a Java library for stochastic simulation, developed under the direction of Pierre L'Ecuyer, in the Département d'Informatique et de Recherche Opérationnelle (DIRO), at the Université de Montréal. It provides facilities for generating uniform and nonuniform random variates, computing different measures related to probability distributions, performing goodness-of-fit tests, applying quasi-Monte Carlo methods, collecting (elementary) statistics, and programming discrete-event simulations with both events and processes.

The newest version!


/*
 * Class:        Resource
 * Description:  resources with limited capacity which can be requested 
                 and released by Process objects.
 * Environment:  Java
 * Software:     SSJ 
 * Copyright (C) 2001  Pierre L'Ecuyer and Université de Montréal
 * Organization: DIRO, Université de Montréal
 * @author       
 * @since

 * SSJ is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License (GPL) as published by the
 * Free Software Foundation, either version 3 of the License, or
 * any later version.

 * SSJ is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * A copy of the GNU General Public License is available at
   GPL licence site.
 */

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(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy