IMPLEMENTATION MODULE RES; (************************************************************************** Programmeurs : ------------ Pierre L'Ecuyer, Nataly Giroux, Denis Alain, Gaetan Perron, Jean Belanger ***************************************************************************) FROM Storage IMPORT ALLOCATE, DEALLOCATE; FROM MYINOUT IMPORT WriteString, WriteLn, WriteCard, WriteLongCard, Write; FROM NUM IMPORT WriteLR; FROM STAT IMPORT Block, Update, Minimum, Maximum, Average, StandardDev, NumberObs, BlockType; FROM PROCS IMPORT ProcessInstance, Suspend, ResetDelay, CurrentProcess, InstanceSerialNumber; FROM LIST IMPORT List, Insert, InsertOrd, Remove, View, Size, InPosition, OutPosition; IMPORT SIM, LIST, STAT, EVENT; CONST LongName = 32; (* Long. max de la chaine de caracteres*) (* decrivant un bloc stat. *) TYPE Resource = POINTER TO InfoResource; (* Pointeur sur le bloc d'informa- *) (* tions associe a une resource. *) InfoResource = RECORD ResPolicy : ServicePolicy; (* Politique de service. *) ResCapacity : CARDINAL; (* Nombre de serveurs. *) AvailCapacity : CARDINAL; (* Nombre de serveurs disponibles a *) (* l'instant courant. *) Serv : List; (* Liste des clients en service. *) Wait : List; (* Liste des clients en attente. *) Stats : BOOLEAN; (* TRUE si on a fait CollectStat, *) (* FALSE par defaut. *) TInit : LONGREAL; (* Instant de la derniere init. des *) (* blocs stat. *) StatOnUtil : Block; (* Bloc statistique sur les serveurs *) (* occupes d'une ressource. *) StatOnCapacity : Block; (* Bloc statistique sur le nombre de *) (* de serveurs d'une ressource. *) StatOnSojourn : Block; (* Bloc stat. sur les durees de *) (* sejour des dossiers pour cette res.*) Descriptor : ARRAY [0..LongName-1] OF CHAR (* Nom associe a la ressource. *) END; UserDossier = POINTER TO Dossier; (* Pointeur sur le dossier d'un *) (* d'un processus. *) Dossier = RECORD Pro : ProcessInstance; (* Processus associe a ce dossier. *) SerNum : LONGCARD; (* No de serie de ce processus. *) NbRes : CARDINAL; (* Nb de serveurs demandes par le *) (* processus. *) Priority : LONGREAL; (* Indice de priorite de service, *) (* utilise si Policy = Prior *) TArrival : LONGREAL; (* Instant d'arrivee de ce dossier. *) Succ : UserDossier END; VAR FreeDossiers : UserDossier; (* Pile des dossiers libres *) (******************************************************************************) PROCEDURE Error ( Number : CARDINAL ); BEGIN WriteLn; WriteString ('***** ERROR from module RES, at time '); WriteLR (SIM.Time (), 10, 3, 2); WriteString (' ***** ' ); WriteLn; CASE Number OF 0 : WriteString ("Trying to perform an operation on a resource"); WriteString (" which has not been created.") END; WriteLn; WriteLn; HALT END Error; PROCEDURE Error1 ( Number : CARDINAL; R : Resource ); BEGIN WriteLn; WriteString ('***** ERROR from module RES, at time '); WriteLR (SIM.Time (), 10, 3, 2); WriteString (' ***** ' ); WriteLn; CASE Number OF 1 : WriteString ("Trying to diminish the capacity of a resource"); WriteString (" more than its current availability.") | 2 : WriteString ("Calling Request for a resource created"); WriteString (" with priorities.") | 3 : WriteString ("Calling RequestWithPrior for a resource created"); WriteString (" without priorities.") | 4 : WriteString ("Trying to release more units of a resource than"); WriteString (" the process currently holds.") | 5 : WriteString ("Referencing to a stat. block for a resource for which"); WriteString (" CollectStat has not been called.") | 6 : WriteString ("Asking a report for a resource for which"); WriteString (" CollectStat has not been called.") | 7 : WriteString ("Calling InitStat for a resource for which"); WriteString (" CollectStat has not been called.") | 11 : WriteString ("Trying to create a resource already created.") END; WriteLn; WriteString ("Resource name : "); WriteString (R^.Descriptor); Write ("."); WriteLn; WriteLn; HALT END Error1; (******************************************************************************) PROCEDURE Precede ( A, B : ADDRESS ): BOOLEAN; (* Procedure servant a ordonner les dossiers dans les listes associees aux *) (* ressources fonctionnant par priorites. *) (* Utilisee dans : Create. *) VAR C, D : UserDossier; BEGIN C := A; D := B; IF C^.Priority >= D^.Priority THEN RETURN TRUE ELSE RETURN FALSE END END Precede; (******************************************************************************) PROCEDURE StartNewCust ( R : Resource ); (* Sert, si possible, de nouveaux clients apres que de nouvelles unites *) (* de la ressource sont devenues disponibles. Ne met pas a jour les *) (* statistiques sur l'utilisation de la ressource. *) (* Appelee par : ChangeCapacity et Release. *) VAR NewDossier : UserDossier; (* Nouveau dossier a servir. *) BEGIN WITH R^ DO View (NewDossier, First, Wait); WHILE (NewDossier # NIL) AND (AvailCapacity > 0) DO (* Utilise de facon maximum les serveurs disponibles en respectant *) (* la Politique. *) WITH NewDossier^ DO IF SerNum # InstanceSerialNumber (Pro) THEN (* On verifie que le processus n'a pas ete tue *) Remove (NewDossier, Current, Wait); Succ := FreeDossiers; FreeDossiers := NewDossier; View (NewDossier, Current, Wait) ELSIF (NbRes <= AvailCapacity) THEN Remove (NewDossier, Current, Wait); Insert (NewDossier, Last, Serv); ResetDelay (Pro, 0.0); AvailCapacity := AvailCapacity - NbRes; View (NewDossier, Current, Wait) ELSE View (NewDossier, Next, Wait) END END END END END StartNewCust; (******************************************************************************) PROCEDURE Create ( VAR R : Resource; Policy : ServicePolicy; Capac : CARDINAL; Name : ARRAY OF CHAR ); VAR n, i : INTEGER; BEGIN NEW (R); WITH R^ DO ResPolicy := Policy; ResCapacity := Capac; AvailCapacity := Capac; n := HIGH (Name); IF n >= LongName THEN n := LongName - 1 END; FOR i := 0 TO n DO Descriptor [i] := Name [i] END; FOR i := n+1 TO LongName - 1 DO Descriptor [i] := " " END; Stats := FALSE; CASE Policy OF Fifo, Lifo : LIST.Create (Serv, 'ServList'); LIST.Create (Wait, 'WaitList') | Prior : LIST.Create ( Serv, 'ServList' ); LIST.CreateOrd ( Wait, Precede, 'WaitList' ) END END END Create; (******************************************************************************) PROCEDURE CollectStat ( R : Resource ); BEGIN IF R = NIL THEN Error (0) END; WITH R^ DO Stats := TRUE; TInit := SIM.Time (); LIST.CollectStat (Wait); LIST.CollectStat (Serv); STAT.Create (StatOnUtil, Accumulate, 'StatUtil'); Update (StatOnUtil, 0.0); STAT.Create (StatOnCapacity, Accumulate, 'StatCapacity'); Update (StatOnCapacity, VAL (LONGREAL, ResCapacity)); STAT.Create (StatOnSojourn, Tally, 'StatOnSojourn') END END CollectStat; (******************************************************************************) PROCEDURE InitStat ( R : Resource ); BEGIN IF R = NIL THEN Error (0) END; WITH R^ DO IF NOT Stats THEN Error1 (1, R); RETURN END; STAT.Init (StatOnUtil); Update (StatOnUtil, VAL (LONGREAL, ResCapacity - AvailCapacity)); STAT.Init (StatOnCapacity); Update (StatOnCapacity, VAL (LONGREAL, ResCapacity)); STAT.Init (StatOnSojourn); LIST.InitStat (Serv); LIST.InitStat (Wait); TInit := SIM.Time () END END InitStat; (******************************************************************************) PROCEDURE Init ( R : Resource ); VAR E : UserDossier; BEGIN IF R = NIL THEN Error (0) END; WITH R^ DO WHILE Size (Serv) > 0 DO (* Recupere l'espace *) Remove (E, First, Serv); E^.Succ := FreeDossiers; FreeDossiers := E END; WHILE Size (Wait) > 0 DO (* Recupere l'espace *) Remove (E, First, Wait); E^.Succ := FreeDossiers; FreeDossiers := E END; AvailCapacity := ResCapacity; IF Stats THEN InitStat (R) END END END Init; (******************************************************************************) PROCEDURE Capacity (R : Resource) : CARDINAL; BEGIN RETURN R^.ResCapacity END Capacity; (******************************************************************************) PROCEDURE ChangeCapacity ( R : Resource; N : INTEGER ); BEGIN IF R = NIL THEN Error (0) END; WITH R^ DO IF N > 0 THEN AvailCapacity := AvailCapacity + CARDINAL (N); ResCapacity := ResCapacity + CARDINAL (N); IF (Size (Wait) > 0) THEN StartNewCust (R) END; ELSE IF (CARDINAL (-N) > AvailCapacity) THEN Error1 (1, R) END; AvailCapacity := AvailCapacity - CARDINAL (-N); ResCapacity := ResCapacity - CARDINAL (-N); END; IF Stats THEN Update (StatOnCapacity, VAL (LONGREAL, ResCapacity)) END; END END ChangeCapacity; (******************************************************************************) PROCEDURE SetCapacity ( R : Resource; N : CARDINAL ); BEGIN ChangeCapacity (R, INTEGER (N) - INTEGER (R^.ResCapacity)) END SetCapacity; (******************************************************************************) PROCEDURE Avail ( R : Resource ) : CARDINAL; BEGIN RETURN R^.AvailCapacity END Avail; (******************************************************************************) PROCEDURE Request ( N : CARDINAL; R : Resource ); VAR NewDossier : UserDossier; BEGIN IF R = NIL THEN Error (0) END; WITH R^ DO IF FreeDossiers # NIL THEN NewDossier := FreeDossiers; FreeDossiers := NewDossier^.Succ ELSE NEW (NewDossier) END; WITH NewDossier^ DO NbRes := N; Pro := CurrentProcess (); SerNum := InstanceSerialNumber (Pro); TArrival := SIM.Time (); IF N <= AvailCapacity THEN AvailCapacity := AvailCapacity - N; IF Stats THEN Update (LIST.StatSojourn(Wait), 0.0) END; Insert (NewDossier, Last, Serv) ELSE CASE ResPolicy OF Fifo : Insert (NewDossier, Last, Wait) | Lifo : Insert (NewDossier, First, Wait) ELSE Error1 (2, R) (* Doit donner une priorite. *) END; Suspend END END; IF Stats THEN Update (StatOnUtil, VAL (LONGREAL, ResCapacity - AvailCapacity)) END END END Request; (******************************************************************************) PROCEDURE RequestWithPrior ( N : CARDINAL; R : Resource; Pri : LONGREAL ); VAR NewDossier : UserDossier; BEGIN IF R = NIL THEN Error (0) END; WITH R^ DO IF FreeDossiers # NIL THEN NewDossier := FreeDossiers; FreeDossiers := NewDossier^.Succ ELSE NEW (NewDossier) (* Creer un dossier pour le processus *) END; WITH NewDossier^ DO NbRes := N; TArrival := SIM.Time (); Pro := CurrentProcess (); SerNum := InstanceSerialNumber (Pro); Priority := Pri; CASE ResPolicy OF Prior : IF N <= AvailCapacity THEN AvailCapacity := AvailCapacity - N; Insert ( NewDossier, Last, Serv); IF Stats THEN Update (LIST.StatSojourn (Wait), 0.0) END; ELSE InsertOrd (NewDossier, Wait); Suspend END ELSE Error1 (3, R) (* Doit appeler Request. *) END; IF Stats THEN (* Mise a jour des statistiques *) Update (StatOnUtil, VAL (LONGREAL, ResCapacity - AvailCapacity)) END END END END RequestWithPrior; (******************************************************************************) PROCEDURE Release ( N : CARDINAL; R : Resource ); VAR GoingDossier : UserDossier; (* Dossier a enlever de la liste de *) (* service *) Temp : CARDINAL; (* Nombre d'unites liberees *) BEGIN IF R = NIL THEN Error (0) END; WITH R^ DO View (GoingDossier, First, Serv); Temp := 0; WHILE (Temp < N) AND (GoingDossier # NIL) DO (* Trouver toutes les dossiers qui referent au processus courant, *) (* jusqu'a concurrence de N unites de cette ressource. *) IF (InstanceSerialNumber (GoingDossier^.Pro) = InstanceSerialNumber (CurrentProcess ())) THEN Temp := Temp + GoingDossier^.NbRes; IF Temp <= N THEN (* Le service du dossier est termine *) Remove (GoingDossier, Current, Serv); IF Stats THEN Update (StatOnSojourn, SIM.Time () - GoingDossier^.TArrival) END; GoingDossier^.Succ := FreeDossiers; FreeDossiers := GoingDossier; IF Temp < N THEN View (GoingDossier, Current, Serv) END ELSE (* Le service du dossier n'est pas *) (* termine, Temp > N. *) GoingDossier^.NbRes := Temp - N; Temp := N END ELSE View (GoingDossier, Next, Serv) END END; IF Temp < N THEN Error1 (4, R) END; AvailCapacity := AvailCapacity + Temp; IF Size (Wait) > 0 THEN StartNewCust (R) END; IF Stats THEN Update (StatOnUtil, VAL (LONGREAL, ResCapacity - AvailCapacity)) END END END Release; (******************************************************************************) PROCEDURE WaitList ( R : Resource ) : List; BEGIN (* Retourne la liste d'attente *) RETURN R^.Wait END WaitList; (******************************************************************************) PROCEDURE ServList ( R : Resource ) : List; BEGIN (* Retourne la liste de service *) RETURN R^.Serv END ServList; (******************************************************************************) PROCEDURE User ( U : UserDossier ) : ProcessInstance; BEGIN (* Retourne le processus du dossier *) RETURN U^.Pro END User; (******************************************************************************) PROCEDURE NbUnits ( U : UserDossier ) : CARDINAL; BEGIN (* Retourne le nombre d'unites *) RETURN U^.NbRes END NbUnits; (******************************************************************************) PROCEDURE StatCapacity ( R : Resource ) : Block; BEGIN IF R = NIL THEN Error (0) END; WITH R^ DO IF Stats THEN RETURN StatOnCapacity ELSE Error1 (5, R) END END END StatCapacity; (******************************************************************************) PROCEDURE StatUtil ( R : Resource ) : Block; BEGIN IF R = NIL THEN Error (0) END; WITH R^ DO IF Stats THEN RETURN StatOnUtil ELSE Error1 (5, R) END END END StatUtil; (******************************************************************************) PROCEDURE StatSojourn ( R : Resource ) : Block; BEGIN IF R = NIL THEN Error (0) END; WITH R^ DO IF Stats THEN RETURN StatOnSojourn ELSE Error1 (5, R) END END END StatSojourn; (******************************************************************************) PROCEDURE Report ( R : Resource ); VAR SojWait, SojServ, SizeWait : Block; BEGIN IF R = NIL THEN Error (0) END; WITH R^ DO IF Stats THEN SojWait := LIST.StatSojourn (Wait); SojServ := LIST.StatSojourn (Serv); SizeWait := LIST.StatSize (Wait); WriteLn; WriteString ('REPORT ON RESOURCE : '); WriteString (Descriptor); WriteLn; WriteString (' From time '); WriteLR (TInit, 7, 2, 2); WriteString (' to time '); WriteLR (SIM.Time (), 10, 2, 2); WriteLn; WriteString (' min max average '); WriteString ('standard dev. nb. Obs.'); WriteLn; WriteString (' Capacity '); WriteCard (TRUNC (Minimum (StatOnCapacity) + 0.5), 5); WriteCard (TRUNC (0.5 + Maximum (StatOnCapacity)), 11); WriteLR (Average (StatOnCapacity), 14, 3, 2); WriteLn; WriteString(' Utilization '); WriteCard (TRUNC (0.5 + Minimum (StatOnUtil)), 5); WriteCard (TRUNC (0.5 + Maximum (StatOnUtil)), 11); WriteLR (Average (StatOnUtil), 14, 3, 2); WriteLn; WriteString(' Queue Size '); WriteCard (TRUNC (0.5 + Minimum (SizeWait)), 5); WriteCard (TRUNC (0.5 + Maximum (SizeWait)), 11); WriteLR (Average (SizeWait), 14, 3, 2); WriteLn; WriteString(' Wait '); WriteLR (Minimum (SojWait), 12, 3, 2); WriteString(' '); WriteLR (Maximum (SojWait), 10, 3, 2); WriteString(' '); WriteLR (Average (SojWait), 10, 3, 2); WriteString (' '); WriteLR (StandardDev (SojWait), 10, 3, 2); WriteLongCard (NumberObs (SojWait), 11); WriteLn; WriteString (' Service '); WriteLR (Minimum (SojServ), 12, 3, 2); WriteString (' '); WriteLR (Maximum (SojServ), 10, 3, 2); WriteString (' '); WriteLR (Average (SojServ), 10, 3, 2); WriteString (' '); WriteLR (StandardDev (SojServ), 10, 3, 2); WriteLongCard (NumberObs (SojServ), 11); WriteLn; WriteString (' Sojourn '); WriteLR (Minimum (StatOnSojourn), 12, 3, 2); WriteString (' '); WriteLR (Maximum (StatOnSojourn), 10, 3, 2); WriteString (' '); WriteLR (Average (StatOnSojourn), 10, 3, 2); WriteString (' '); WriteLR (StandardDev (StatOnSojourn), 10, 3, 2); WriteLongCard (NumberObs (StatOnSojourn), 11); WriteLn ELSE Error1 (6, R); END END END Report; PROCEDURE Delete (VAR R : Resource); VAR U : UserDossier; BEGIN WITH R^ DO WHILE Size (Serv) > 0 DO Remove (U, Current, Serv); U^.Succ := FreeDossiers; FreeDossiers := U END; WHILE Size (Wait) > 0 DO Remove (U, Current, Wait); U^.Succ := FreeDossiers; FreeDossiers := U END; LIST.Delete (Serv); LIST.Delete (Wait); IF Stats THEN STAT.Delete (StatOnUtil); STAT.Delete (StatOnCapacity); STAT.Delete (StatOnSojourn) END; DISPOSE (R); R := NIL END END Delete; BEGIN FreeDossiers := NIL END RES.