/* --------------------------------------------------------------------- Prosim4: Version 2.0 --------------------------------------------------------------------- */ #include #include #define EXTERN /* Define variable declared in prosim4.h */ #include "prosim4.h" #include "sys4.h" /* ----------------------------------------------------------------------- File: prosim4.c for SUN-4 Core functions for the process simulation system Written by Shun Yan Cheung, August 1989 Version 1.0: August 1989 Version 2.0: January 1991 Dedication: Once upon a time, I attended the Delft University of Technology in The Netherlands where I learn, among other things, the Prosim78 simulation system. Although the lesson was a dry swim, I was impressed by the features of Prosim78. I have hereby implemented and extended the Prosim78 system and called it ProSim4. ----------------------------------------------------------------------- */ /* ---------------------------------------------------------------- StartSimulate(EndTime): Let the system run until TimeHeap is empty or NOW > EndTime This routine is also the scheduler... All it does is: 1. get the first process of the TimeHeap. 2. pass control to it and suspend itself in the SchedulerPCB ---------------------------------------------------------------- */ void StartSimulate(double EndTime) { double NextMileStoneTime = 100.0; while ( (NTimeHeap > 0) && (Now <= EndTime) ) { CurrentPtr = HeapDelete(TimeHeap, &NTimeHeap); Now = CurrentPtr->EventTime; Transfer(&SchedulerPCB, &(CurrentPtr->PCB)); if (Now > NextMileStoneTime) { /* printf("**** Simulation time is now: %lf...\n", Now); */ NextMileStoneTime += 100.0; } }; if (Now < EndTime) { printf("\n**** WARNING: Simulation ended at %f with no active entity\n", Now); } /* ------------------------------------------------ By terminating the procedure, execution returns to the MAIN user program.... ------------------------------------------------ */ } /* StartSimulate */ #define MAXPROSIMNAME 100 static int NProsimProcName = 0; static char ProsimProcName[MAXPROSIMNAME][8]; static int ProsimProcIndex[MAXPROSIMNAME]; int LookupProsimProcName(char *s) { int i; for (i = 0; i < NProsimProcName; i++) if (strcmp(ProsimProcName[i], s) == 0) return(i); /* ---------------------- Not found, store it. ---------------------- */ if (NProsimProcName < MAXPROSIMNAME) { strcpy(ProsimProcName[NProsimProcName], s); ProsimProcIndex[NProsimProcName] = 0; return(NProsimProcName++); } else return(-1); } /* ---------------------------------------------------------------------- Activate(StringName, ProcName, PCBPtr, param): prepare "ProcName" to be executed Input: ProcName = void name PCBPtr = pointer to a PCB record Effect: 1. initialize some local variables of the process 2. create a process using NEWPROCESS 3. put newly created process at head of TimeHeap Usage: 1. as a HELP function for Generate() for activating a client that has just been generated. 2. to activate well-known processes (servers, generators, etc) that have identifiable PCBs ---------------------------------------------------------------------- */ void Activate(char *StringName,void (*ProcName)(), PCBType *PCBPtr, void *param) { int N; if ( (N = LookupProsimProcName(StringName)) == -1 ) { printf("PROSIM Error: Too many StringName... increase MAXPROSIMNAME !\n"); exit(1); } sprintf(PCBPtr->Name, "%s#%d", StringName, ProsimProcIndex[N]++); PCBPtr->ArrivalTime = Now; PCBPtr->EventTime = Now; PCBPtr->State = ACTIVE; /* Set process to be active */ PCBPtr->NQueue = 0; PCBPtr->Queue = NULL; NewProcess(ProcName, PCBPtr->Mem, sizeof(PCBPtr->Mem), &(PCBPtr->PCB), param); HeapInsert(TimeHeap, &NTimeHeap, PCBPtr); } /* Activate */ /* ---------------------------------------------------------------- Generate(ProcName): Generate arrivals of clients Make a process && put it in the TimeHeap Modification: Generate() now returns the PCB pointer of the generated process. This enables the generator to stick an ID in the generated client for unique identification purposes. ---------------------------------------------------------------- */ PCBType * Generate(char *StringName, void (*ProcName)(), void *param) { PCBType * PCBPtr; if ( FreePCBPtr == NULL ) /* Free PCB list is empty */ { printf("\n**** SYSTEM ERROR: No more Free PCB's. Sorry.\n"); OnAbort(); /* Allow user to print data structures to debug before exit... */ exit(1); } PCBPtr = FreePCBPtr; /* Get a free PCB off FreePCBList */ FreePCBPtr = FreePCBPtr->Next; Activate(StringName, ProcName, PCBPtr, param); /* Put process in TimeHeap */ return(PCBPtr); /* Return pointer to the new process */ } /* Generate */ /* ---------------------------------------------------- Terminate: end the existence of the current entity ---------------------------------------------------- */ void Terminate() { CurrentPtr->Next = FreePCBPtr; /* return PCB */ FreePCBPtr = CurrentPtr; Transfer(&(CurrentPtr->PCB), &SchedulerPCB); /* Transfer control */ } /* Terminate */ /* ------------------------------------------------------------------ Passivate(TimeOut): make current entity retire from being active input: TimeOut = timeout period that you want to passivate. If TimeOut <= 0, you passivate until you get ReActivated by another entity. Usage: You may only execute Passivate(0) if you will be waken by someone else. For instance: 1. A server that is a 'well-known' entity (it's PCB can be refered to by name (i.e. a global variable) can execute Passivate(0) and an arriving client can wake him up by an ReActivate(&ServerPCB); 2. A Client (without the convenience of accessing it's PCB by name) must enqueue itself in some queue-variable which will be subsequently dequeued by another entity and get ReActivated. Any entity can execute Passivate(>0) without worrying about getting ReActivated... ---------------------------------------------------------------- */ int Passivate(double TimeOut, long *Param) { CurrentPtr->Param = Param; /* Set up parameter for ReActivate */ if (TimeOut <= 0.0) { /* Total Passivate */ CurrentPtr->State = PASSIVE; /* Indicate PASSIVE */ Transfer(&(CurrentPtr->PCB), &SchedulerPCB); /* Transfer control */ } else { /* Process Passivate with timeout */ CurrentPtr->State = WAITING; /* Indicate WAITING */ CurrentPtr->EventTime = Now + TimeOut; HeapInsert(TimeHeap, &NTimeHeap, CurrentPtr); /* Back into runheap ! */ Transfer(&(CurrentPtr->PCB), &SchedulerPCB); } if (CurrentPtr->State == WAITING) return(TIMEOUT); else return(CurrentPtr->Result); } /* Passivate */ int PassivateUntil(double Time, long *Param) { CurrentPtr->Param = Param; /* Set up parameter for ReActivate */ if (Time <= 0.0) { /* Total Passivate */ CurrentPtr->State = PASSIVE; /* Indicate PASSIVE */ Transfer(&(CurrentPtr->PCB), &SchedulerPCB); /* Transfer control */ } else { /* Process Passivate with timeout */ CurrentPtr->State = WAITING; /* Indicate WAITING */ CurrentPtr->EventTime = Time; HeapInsert(TimeHeap, &NTimeHeap, CurrentPtr); Transfer(&(CurrentPtr->PCB), &SchedulerPCB); } if (CurrentPtr->State == WAITING) return(TIMEOUT); else return(CurrentPtr->Result); } /* PassivateUntil */ int Sleep(double T) { return( Passivate(T, NULL) ); } int SleepUntil(double T) { return( PassivateUntil(T, NULL) ); } /* ----------------------------------------------------------------- ReActivate(PCBPtr, Result, Data): make entity PCBPtr active (i.e., put it back to the TimeHeap[] if necessary) and change the Clients PCB LocalBlockPtr (to allow clients to find the local block data). Input: PCBPtr = Pointer to the PCB of the process that need to be re-activated, i.e. be put in the TimeHeap Result = Return value from server to client (e.g. REJECT, OK, DONE,..). This value will be the return value of Passivate() and PassivateUntil() Data = value returned into the long *Param parameter of Passivate() and PassivateUntil(). Effect: If state is PASSIVE or WAITING, then change state of ProSim4 process "PCBPtr" to ACTIVE and put it into the TimeHeap (this ProSim4 process will then be the first to run !!!) ProSim4 processes can be: PASSIVE = out of the TimeHeap (Passivate(COMPLETELY)) WAITING = in the TimeHeap, and can be awaken (Passivate(Time)) ACTIVE = in the TimeHeap waiting (Hold(Time)) This is a smart procedure: the process being ReActivated will only be put in the TimeHeap if it is PASSIVE or WAITING. Any other state inplies that the process is already in the TimeHeap[] and the ReActivate() call has NO effect (i.e., the ProSim4 process will stay in the same place in the TimeHeap[]. Note: ReActivate() will NOT transfer control to another process. the current process will still be executing after this call. Transfer control happens only when the current process executes Hold(), Passivate() or Terminate() ----------------------------------------------------------------- */ void ReActivate(PCBType *PCBPtr, int Result, long Data) { if ( PCBPtr->State == WAITING ) { /* -------------------------------------------------- The entity is timing out and await service, we are re-activating him BEFORE his timeout period ! -------------------------------------------------- */ PCBPtr->EventTime = Now; /* For HeapSort() */ HeapSpeedUp(TimeHeap, PCBPtr->pos); /* Put him as root of TimeHeap */ } else if ( PCBPtr->State == PASSIVE ) { /* ------------------------------------------------------ The entity has passivated COMPLETELY and await service or arrival of client (in case of server) ------------------------------------------------------ */ PCBPtr->EventTime = Now; /* For HeapSort() */ HeapInsert(TimeHeap, &NTimeHeap, PCBPtr); /* Insert in TimeHeap[] */ } /* ---------------------------------------------------------------- Update information in the PCB record of the Reactivated process ---------------------------------------------------------------- */ PCBPtr->State = ACTIVE; /* Change the state to ACTIVE */ PCBPtr->Result = Result; /* Tell him why he is waken up */ if (PCBPtr->Param) *(PCBPtr->Param) = Data; /* Pass data to the Param place */ } /* ReActivate */ void WakeUp(PCBType *PCBPtr, int Result) { ReActivate(PCBPtr, Result, NULL); } /* ------------------------------------------------------------------ Hold(Time): sleep Time time-units, i.e. put yourself in the right position in the TimeHeap to get scheduled ------------------------------------------------------------------ */ void Hold(double Time) { /* if (Now + Time == Now) fprintf(stderr, "Hold warning: Now = %lf Delta = %lf, not advancing !\n", Now, Time); */ CurrentPtr->EventTime = Now + Time; CurrentPtr->State = ACTIVE; /* Overwrite possible WAITING value */ HeapInsert(TimeHeap, &NTimeHeap, CurrentPtr); Transfer(&(CurrentPtr->PCB), &SchedulerPCB); } /* Hold */ void HoldUntil(double Time) { /* if (Now == Time) fprintf(stderr, "HoldUntil warning: Now = %lf, Time = %lf !\n", Now, Time); */ CurrentPtr->EventTime = Time; CurrentPtr->State = ACTIVE; /* Overwrite possible WAITING value */ HeapInsert(TimeHeap, &NTimeHeap, CurrentPtr); Transfer(&(CurrentPtr->PCB), &SchedulerPCB); } /* HoldUntil */ /* ---------------------- System Initialization ---------------------- */ void InitProsim(unsigned int MaxNProcess, unsigned int MaxNQElems, unsigned int MaxNTimeHeap) { int i; PCBType *PCB; QElemType *QQQ; printf("=======================================================\n"); printf("ProSim4: Process Simulation.\n"); printf("=======================================================\n"); printf("System Parameters:\n"); printf(" %6d processes / %d words stack space\n", MaxNProcess, MAXWSSIZE); printf(" %6d queueing elements for processes.\n", MaxNQElems); printf(" %6d spaces in TimeHeap.\n", MaxNTimeHeap); printf(" (Queueing elements for message are dynamic...)\n"); printf("Initializing system parameters....\n"); printf("=======================================================\n"); /* -------------------------------- Link the PCB[] elements -------------------------------- */ PCB = calloc(MaxNProcess, sizeof(PCBType) ); if ( PCB == NULL) { printf("Can't allocate %d PCB's (%d bytes each)...\n", MaxNProcess, sizeof(PCBType) ); exit(1); } for ( i = 0; i <= MaxNProcess-2; i++ ) PCB[i].Next = &PCB[i+1]; PCB[MaxNProcess-1].Next = NULL; FreePCBPtr = &PCB[0]; /* -------------------------------- Link the QElem[] elements -------------------------------- */ QQQ = calloc(MaxNQElems, sizeof(PCBType) ); if ( QQQ == NULL) { printf("Can't allocate %d PCB's (%d bytes each)...\n", MaxNQElems, sizeof(PCBType) ); exit(1); } for ( i = 0; i <= MaxNQElems-2; i++ ) QQQ[i].HNext = &QQQ[i+1]; QQQ[MaxNQElems-1].HNext = NULL; FreeQElemPtr = &QQQ[0]; /* -------------------------------- Allocate space for the TimeHeap -------------------------------- */ TimeHeap = (PCBType **) calloc(MaxNTimeHeap, sizeof(PCBType *) ); MAXNHEAP = MaxNTimeHeap; NTimeHeap = 0; Now = 0.0; } /* SystInit */ void PrintReason(int R) { switch (R) { case DONE: printf("DONE\n"); break; case ERROR: printf("ERROR\n"); break; case REJECT: printf("REJECT\n"); break; case ARRIVAL: printf("ARRIVAL\n"); break; default: printf("User specific reason, value = %d\n", R); } } double GetSimTime() { return(Now); } double GetMyArrivalTime() { return(CurrentPtr->ArrivalTime); }