/**********************************************************************/ /* */ /* File "smpl.c" */ /* smpl Simulation Subsystem */ /* */ /**********************************************************************/ #include #include #include typedef double real; #define then #define nl 256 /* element pool length */ #define ns 256 /* namespace length */ #define pl 58 /* printer page length (lines used */ #define sl 23 /* screen page length by 'smpl') */ #define FF 12 /* form feed */ static int rept_page(int); static int save_name(char*,int); static void end_line(); static void enlist(int*,int); static void enqueue(int,int,int,int,real); static void msg(int,int,char*,int,int); static void resetf(); int stream(int); void endpage(); void error(int,char*); void newpage(); void report(); void reportf(); void smpl_pause(); static FILE *display, /* screen display file */ *opf; /* current output destination */ static int event, /* current simulation event */ token, /* last token dispatched */ blk, /* next available block index */ avl, /* available element list header */ evl, /* event list header */ fchn, /* facility descriptor chain header */ avn, /* next available namespace position */ tr, /* event trace flag */ mr, /* monitor activation flag */ lft=sl; /* lines left on current page/screen */ static real clock, /* current simulation time */ start, /* simulation interval start time */ tl; /* last trace message issue time */ static int l1[nl], l2[nl], /* facility descriptor, */ l3[nl]; /* queue, & */ static real /* event list */ l4[nl], /* element pool */ l5[nl]; static char name[ns]; /* model and facility name space */ /*--------------- INITIALIZE SIMULATION SUBSYSTEM ------------------*/ void smpl(m,s) int m; char *s; { int i; static int rns=1; display=opf=stdout; blk=1; avl=-1; avn=0; /* element pool & namespace headers */ evl=fchn=0; /* event list & descriptor chain headers */ clock=start=tl=0.0; /* sim., interval start, last trace times */ event=tr=0; /* current event no. & trace flags */ for (i=0; i namespace */ rns=stream(rns); rns=++rns>15? 1:rns; /* set random no. stream */ mr=(m>0)? 1:0; /* set monitor flag */ /* if (mr) then {opf=display; init_mtr(1);} */ } /*----------------------- RESET MEASUREMENTS -----------------------*/ void reset() { resetf(); start=clock; } /*--------------------------- SAVE NAME ----------------------------*/ static int save_name(s,m) char *s; int m; { int i,n; n=strlen(s); if (n>m) then n=m; if (avn+n>ns) then error(2,NULL); /* namespace exhausted */ i=avn; avn+=n+1; strncpy(&name[i],s,n); if (n==m) then name[avn++]='\0'; return(i); } /*------------------------- GET MODEL NAME -------------------------*/ char *mname() { return(name); } /*------------------------ GET FACILITY NAME -----------------------*/ char *fname(f) int f; { return(&name[l3[f+1]]); } /*--------------------------- GET BLOCK ----------------------------*/ static int get_blk(n) int n; { int i; if (blk==0) then error(3,NULL); /* block request after schedule */ i=blk; blk+=n; if (blk>=nl) then error(1,NULL); /* element pool exhausted */ return(i); } /*-------------------------- GET ELEMENT ---------------------------*/ static int get_elm() { int i; if (avl<=0) then { if (avl==0) then error(1,NULL); /* empty element list */ /* if (mr && !tr) then init_mtr(2); */ /* build the free element list from the block of elements */ /* remaining after all facilities have been defined */ for (i=blk; i<(nl-1); i++) l1[i]=i+1; avl=blk; blk=0; } i=avl; avl=l1[i]; return(i); } /*------------------------- RETURN ELEMENT -------------------------*/ static void put_elm(i) int i; { l1[i]=avl; avl=i; } /*------------------------- SCHEDULE EVENT -------------------------*/ void schedule(ev,te,tkn) int ev,tkn; real te; { int i; if (te<0.0) then error(4,NULL); /* negative event time */ i=get_elm(); l2[i]=tkn; l3[i]=ev; l4[i]=0.0; l5[i]=clock+te; enlist(&evl,i); if (tr) then msg(1,tkn,"",ev,0); } /*--------------------------- CAUSE EVENT --------------------------*/ void cause(ev,tkn) int *ev,*tkn; { int i; if (evl==0) then error(5,NULL); /* empty event list */ i=evl; *tkn=token=l2[i]; *ev=event=l3[i]; clock=l5[i]; evl=l1[i]; put_elm(i); /* delink element & return to pool */ if (tr) then msg(2,*tkn,"",event,0); /* if (mr && (tr!=3)) then mtr(tr,0); */ } /*-------------------------- RETURN TIME ---------------------------*/ real time() { return(clock); } /*-------------------------- CANCEL EVENT --------------------------*/ int cancel(ev) int ev; { int pred,succ=evl,tkn; while((succ!=0) && (l3[succ]!=ev)) {pred=succ; succ=l1[pred];} if (succ==0) then return(-1); tkn=l2[succ]; if (tr) then msg(3,tkn,"",l3[succ],0); if (succ==evl) then evl=l1[succ]; /* unlink event */ else l1[pred]=l1[succ]; /* list entry & */ put_elm(succ); /* deallocate it */ return(tkn); } /*------------------------- SUSPEND EVENT --------------------------*/ static int suspend(tkn) int tkn; { int pred,succ=evl; while((succ!=0) && (l2[succ]!=tkn)) {pred=succ; succ=l1[pred];} if (succ==0) then error(6,NULL); /* no event scheduled for tkn */ if (succ==evl) then evl=l1[succ]; /* unlink event */ else l1[pred]=l1[succ]; /* list entry */ if (tr) then msg(6,-1,"",l3[succ],0); return(succ); } /*-------------- ENTER ELEMENT IN QUEUE OR EVENT LIST --------------*/ static void enlist(head,elm) int *head,elm; { /* 'head' points to head of queue/event list */ int pred,succ; real arg,v; arg=l5[elm]; succ=*head; while(1) { /* scan for position to insert entry: event list is order- */ /* ed in ascending 'arg' values, queues in descending order */ if (succ==0) then break; /* end of list */ else { v=l5[succ]; if (*head==evl) then { /* event list */ if (v>arg) then break; } else { /* queue: if entry is for a preempted token- */ /* (l4, the remaining event time, >0), insert */ /* entry at beginning of its priority class; */ /* otherwise, insert it at the end */ if ((v0.0))) then break; } } pred=succ; succ=l1[pred]; } l1[elm]=succ; if (succ!=*head) then l1[pred]=elm; else *head=elm; } /*------------------------- DEFINE FACILITY ------------------------*/ int facility(s,n) char *s; int n; { int f,i; f=get_blk(n+2); l1[f]=n; l3[f+1]=save_name(s,(n>1 ? 14:17)); if (fchn==0) then fchn=f; else {i=fchn; while(l2[i+1]) i=l2[i+1]; l2[i+1]=f;} l2[f+1]=0; if (tr) then msg(13,-1,fname(f),f,0); return(f); } /*--------------- RESET FACILITY & QUEUE MEASUREMENTS --------------*/ static void resetf() { int i=fchn,j; while(i) { l4[i]=l4[i+1]=l5[i+1]=0.0; for (j=i+2; j<=(i+l1[i]+1); j++) {l3[j]=0; l4[j]=0.0;} i=l2[i+1]; /* advance to next facility */ } } /*------------------------ REQUEST FACILITY ------------------------*/ int request(f,tkn,pri) int f,tkn,pri; { int i,r; if (l2[f] 0 for proper enqueueing */ /* (see 'enlist'). Update facility & server stati- */ /* stics for the preempted token, and set r = 0 to */ /* reserve the facility for the preempting token. */ if (tr) then msg(8,tkn,fname(f),2,0); j=l1[k]; i=suspend(j); ev=l3[i]; te=l5[i]-clock; if (te==0.0) then te=1.0e-99; put_elm(i); enqueue(f,j,l2[k],ev,te); if (tr) then {msg(10,-1,"",j,l3[f]); msg(12,-1,fname(f),tkn,0);} l3[k]++; l4[k]+=clock-l5[k]; l2[f]--; l4[f+1]++; r=0; } } if (r==0) then { /* reserve server k of facility */ l1[k]=tkn; l2[k]=pri; l5[k]=clock; l2[f]++; } return(r); } /*------------------------ RELEASE FACILITY ------------------------*/ void release(f,tkn) int f,tkn; { int i,j=0,k,m; real te; /* locate server (j) reserved by releasing token */ k=f+1+l1[f]; /* index of last server element */ for (i=f+2; i<=k; i++) if (l1[i]==tkn) then {j=i; break;} if (j==0) then error(7,NULL); /* no server reserved */ l1[j]=0; l3[j]++; l4[j]+=clock-l5[j]; l2[f]--; if (tr) then msg(9,tkn,fname(f),0,0); if (l3[f]>0) then { /* queue not empty: dequeue request ('k' = */ /* index of element) & update queue measures */ k=l1[f+1]; l1[f+1]=l1[k]; te=l4[k]; l5[f+1]+=l3[f]*(clock-l5[f]); l3[f]--; l4[f]++; l5[f]=clock; if (tr) then msg(11,-1,"",l2[k],l3[f]); if (te==0.0) then then { /* blocked request: place request at head of event */ /* list (so its facility request can be re-initiated */ /* before any other requests scheduled for this time) */ l5[k]=clock; l1[k]=evl; evl=k; m=4; } else { /* return after preemption: reserve facility for de- */ /* queued request & reschedule remaining event time */ l1[j]=l2[k]; l2[j]=(int)l5[k]; l5[j]=clock; l2[f]++; if (tr) then msg(12,-1,fname(f),l2[k],0); l5[k]=clock+te; enlist(&evl,k); m=5; } if (tr) then msg(m,-1,"",l3[k],0); } } /*----------------------- GET FACILITY STATUS ----------------------*/ int status(f) int f; { return(l1[f]==l2[f]? 1:0); } /*-------------------- GET CURRENT QUEUE LENGTH --------------------*/ int inq(f) int f; { return(l3[f]); } /*-------------------- GET FACILITY UTILIZATION --------------------*/ real U(f) int f; { int i; real b=0.0,t=clock-start; if (t>0.0) then { for (i=f+2; i<=f+l1[f]+1; i++) b+=l4[i]; b/=t; } return(b); } /*---------------------- GET MEAN BUSY PERIOD ----------------------*/ real B(f) int f; { int i,n=0; real b=0.0; for (i=f+2; i<=f+l1[f]+1; i++) {b+=l4[i]; n+=l3[i];} return((n>0)? b/n:b); } /*-------------------- GET AVERAGE QUEUE LENGTH --------------------*/ real Lq(f) int f; { real t=clock-start; return((t>0.0)? (l5[f+1]/t):0.0); } /*----------------------- TURN TRACE ON/OFF ------------------------*/ void trace(n) int n; { switch(n) { case 0: tr=0; break; case 1: case 2: case 3: tr=n; tl=-1.0; newpage(); break; case 4: end_line(); break; default: break; } } /*-------------------- GENERATE TRACE MESSAGE ----------------------*/ static void msg(n,i,s,q1,q2) int n,i,q1,q2; char *s; { static char *m[14] = {"", "SCHEDULE", "CAUSE", "CANCEL", " RESCHEDULE"," RESUME", " SUSPEND", "REQUEST", "PREEMPT", "RELEASE", " QUEUE", " DEQUEUE", " RESERVE", "FACILITY" }; if (clock>tl) /* print time stamp (if time has advanced) */ then {tl=clock; fprintf(opf," time %-12.3f ",clock);} else fprintf(opf,"%21s",m[0]); if (i>=0) /* print token number if specified */ then fprintf(opf,"-- token %-4d -- ",i); else fprintf(opf,"-- -- "); fprintf(opf,"%s %s",m[n],s); /* print basic message */ switch(n) { /* append qualifier */ case 1: case 2: case 3: case 4: case 5: case 6: fprintf(opf," EVENT %d",q1); break; case 7: case 8: switch(q1) { case 0: fprintf(opf,": RESERVED"); break; case 1: fprintf(opf,": QUEUED (inq = %d)",q2); break; case 2: fprintf(opf,": INTERRUPT"); break; default: break; } break; case 9: break; case 10: case 11: fprintf(opf," token %d (inq = %d)",q1,q2); break; case 12: fprintf(opf," for token %d",q1); break; case 13: fprintf(opf,": f = %d",q1); break; default: break; } fprintf(opf,"\n"); end_line(); } /*------------------------- TRACE LINE END -------------------------*/ static void end_line() { if ((--lft)==0) then { /* end of page/screen. for trace 1, advance page if print- */ /* er output; screen output is free-running. for trace 2, */ /* pause on full screen; for trace 3, pause after line. */ switch(tr) { case 1: if (opf==display) then lft=sl; else endpage(); break; case 2: if (mr) then {putchar('\n'); lft=sl; smpl_pause();} else endpage(); break; case 3: lft=sl; break; } } if (tr==3) then smpl_pause(); } /*----------------------------- PAUSE ------------------------------*/ void smpl_pause() { /* pause execution via 'mtr' call (if active) */ /* if (mr) then mtr(tr,1); else */ getchar(); } /*------------------ DISPLAY ERROR MESSAGE & EXIT ------------------*/ void error(n,s) int n; char *s; { FILE *dest; static char *m[8]= { "Simulation Error at Time ", "Empty Element Pool", "Empty Name Space", "Facility Defined After Queue/Schedule", "Negative Event Time", "Empty Event List", "Preempted Token Not in Event List", "Release of Idle/Unowned Facility" }; dest=opf; while(1) { /* send messages to both printer and screen */ fprintf(dest,"\n**** %s%.3f\n",m[0],clock); if (n) then fprintf(dest," %s\n",m[n]); if (s!=NULL) then fprintf(dest," %s\n",s); if (dest==display) then break; else dest=display; } if (opf!=display) then report(); /* if (mr) then mtr(0,1); */ exit(0); } /*------------------------ GENERATE REPORT -------------------------*/ void report() { newpage(); reportf(); endpage(); } /*-------------------- GENERATE FACILITY REPORT --------------------*/ void reportf() { int f; if ((f=fchn)==0) then fprintf(opf,"\nno facilities defined: report abandoned\n"); else { /* f = 0 at end of facility chain */ while(f) {f=rept_page(f); if (f>0) then endpage();} } } /*---------------------- GENERATE REPORT PAGE ----------------------*/ static int rept_page(fnxt) int fnxt; { int f,i,n; char fn[19]; static char *s[7]= { "smpl SIMULATION REPORT", " MODEL: ", "TIME: ", "INTERVAL: ", "MEAN BUSY MEAN QUEUE OPERATION COUNTS", " FACILITY UTIL. ", " PERIOD LENGTH RELEASE PREEMPT QUEUE" }; fprintf(opf,"\n%51s\n\n\n",s[0]); fprintf(opf,"%-s%-54s%-s%11.3f\n",s[1],mname(),s[2],clock); fprintf(opf,"%68s%11.3f\n\n",s[3],clock-start); fprintf(opf,"%75s\n",s[4]); fprintf(opf,"%s%s\n",s[5],s[6]); f=fnxt; lft-=8; while(f && lft--) { n=0; for (i=f+2; i<=f+l1[f]+1; i++) n+=l3[i]; if (l1[f]==1) then sprintf(fn,"%s",fname(f)); else sprintf(fn,"%s[%d]",fname(f),l1[f]); fprintf(opf," %-17s%6.4f %10.3f %13.3f %11d %9d %7d\n", fn,U(f),B(f),Lq(f),n,(int)l4[f+1],(int)l4[f]); f=l2[f+1]; } return(f); } /*--------------------------- COUNT LINES --------------------------*/ int lns(i) int i; { lft-=i; if (lft<=0) then endpage(); return(lft); } /*---------------------------- END PAGE ----------------------------*/ void endpage() { if (opf==display) then { /* screen output: push to top of screen & pause */ while(lft>0) {putc('\n',opf); lft--;} printf("\n[ENTER] to continue:"); getchar(); /* if (mr) then clr_scr(); else */ printf("\n\n"); } else if (lft