PRGM SMFEX; BEGIN # ** SMFEX - SCREEN MANAGEMENT FACILITY EXECUTIVE. * * COPYRIGHT CONTROL DATA SYSTEMS INC. 1992. * * THE SMFEX PROGRAM IS THE EXECUTIVE OR MONITOR FOR THE SMFEX * SUBSYSTEM. THE FUNCTION PROVIDED BY THE SMFEX SUBSYSTEM IS * AN ALTERNATE IMPLEMENTATION OF THE FULL SCREEN EDITOR (FSE) * TO BE MEMORY RESIDENT, SERVICING LARGE USER COMMUNITIES * WITH A REDUCTION IN SYSTEM SCHEDULING OVERHEAD COMPARED TO * THE CONVENTIONAL EXECUTION OF A UNIQUE JOB FOR EACH USER. * * THE SMFEX PROGRAM IS RESPONSIBLE TO MANAGE THE * MULTI-TASKING TECHNIQUE WHICH ALLOWS THE EDITOR CODE TO BE * SHARED BY MULTIPLE USERS. THE EDITOR CODE RESIDES IN * SEVERAL SOURCE MODULES OUTSIDE OF THIS PROGRAM. SMFEX AND * THE EDITING MODULES ARE COMBINED BY THE LOADER. EDITING * MODULES REQUIRE SEPARATE COMPILATION WITH CERTAIN * MODIFY-TIME DEFINITIONS CHANGED TO YIELD TWO SETS OF * RELOCATABLE MODULES, ONE SUITABLE FOR LOADER PROCESSING TO * YIELD FSE AND THE OTHER SUITABLE FOR LOADER PROCESSING TO * YIELD SMFEX. * # DEF LISTCON #1#; CONTROL PRESET; # UNIVERSAL DEFINITIONS # *CALL COMFMLT *CALL COMFFSE # SMFEX'S OWN DEFS # *CALL COMFSMF # SIC CODES. IAFEX TALKS TO OUR BUFFER 0, SINGLE-USER FSE TALKS # # TO OUR BUFFER 1 WITH A DIFFERENT SET OF CODES. # STATUS IAF2SMF INIT, MSGIN, OUTDONE, BREAK, HUNGUP, CONNECT; STATUS SMF2IAF INIT, MSGOUT, PROMPT, LOGOUT; STATUS TASKST EMPTY, DELAY, RECALLFET, START, SWAP, RUN, PRELIM; # TYPES OF STATISTICS COLLECTED # STATUS INSTST THNK, RSPNS, NOTTI, NOTASK, SWPE, SWPD, CLOCKTICK, PREEMPT, SWPTIM, EDTCIO, SESSION, TRANS, XSTTO, BREAKS, HANGUPS, BGNEDT, ENDEDT, INPUT, XMIT, KWIKTOC, TASKUTIL, WORKIO, DROPCPU; DEF MOD4(ZZ) #((ZZ) LAN 3)#; DEF MOD8(ZZ) #((ZZ) LAN 7)#; DEF MOD32(ZZ) #((ZZ) LAN 31)#; PAGE # EXTERNAL REF'S AND DEF'S # XDEF BEGIN PROC SMFRCL; PROC SMFDLY; PROC CLEARINT; PROC VOLUNTEER; PROC TTITRAP; PROC TTOTRAP; PROC FATALTRAP; PROC CLEARQUE; CONTROL IFEQ METERING,1; PROC BGNMETER; PROC WIOSTAT; CONTROL FI; ARRAY RENTSTK [1:MAXREENT]; # SUBROUTINE STACK # BEGIN ITEM RSTK; END ITEM RSTKPTR; END XREF PROC MOVEWD; XREF BEGIN # FIRST COME COMPASS ROUTINES, FOR SPECIAL/OPTIMAL TRICKS # FUNC FINDBIT; FUNC NOSWDSZ; PROC SPREAD; PROC GATHER; PROC FLDLEN; PROC FLDLENE; PROC SFMSSTF; PROC SFMCSTF; PROC TESTECS; PROC READECS; PROC WRITECS; PROC GETUTAB; PROC SAVUTAB; # NOW COME THE STANDARD UOI SYMPLIB ROUTINES WE NEED # PROC WRITEO; PROC READO; PROC RECALL; PROC SYSREQ; PROC READ; PROC READC; PROC REWIND; PROC STATUSP; PROC WRITER; PROC REWRITR; PROC WRITEC; PROC WRITEW; PROC MESSAGE; PROC DISTCON; PROC RETERN; PROC GETFLCE; PROC ONSW; PROC RTIME; PROC SETRFLE; PROC PROTEON; PROC EVICT; PROC FILINFO; PROC REQUEST; PROC EESET; PROC ENDRUN; PROC ABORT; # NOW COME ENTRIES TO FSE EDITOR # FUNC MIN; FUNC MAX; PROC PROCESS; PROC EDTINIT; PROC MAKEFET; PROC EDTTERM; PROC PAUSEIO; PROC RESUMIO; PROC CHECKIO; PROC TTSYNC; PROC FATAL; PROC VDSYNCH; END PAGE # COMMON DATA AREAS # *CALL COMFDS1 *CALL COMFVD2 *CALL COMFDS2 PAGE # EDITOR TABLES # # COMPILED HERE ONLY FOR CONTROL PRESET # *CALL COMFTAB PAGE # SMFEX'S OWN DATA # ITEM TIMEOFDAY=0; ITEM SLICECOUNT=0; ITEM OLDTIMEDAY=0; ITEM NEWTIMEDAY=0; ITEM IDLEDOWN B=FALSE; ITEM STOPNOW B=FALSE; ITEM ACCEPTNONE B=FALSE; ITEM ECSDOWN B=FALSE; ITEM SHUTDOWN=0; # CONTROLS OPERATOR SHUTDON # ITEM COMPLETE B=FALSE; # USED TO SHUT WHOLE THING DOWN # ITEM CURUSER; # UTILITY VARIABLE # ITEM CURTASK; # CURRENT TASK REFERENCE # ITEM LASTTASK=0; # PREVIOUS TASK TO RUN # ITEM TASKSAVAIL=3; # TASKS CURRENTLY AVAILABLE/IN USE # ITEM TASKSBUSY=0; # NUMBER OF TASKS IN USE NOW # ITEM CHNGSTATUS; # CURRENT TASKS CHANGE OF STATUS # ITEM REMAINTTI=NUMTTIBUF; # HOW MANY BUFFERS AVAIL # ITEM DISPATLEN; # LENGTH OF DISPATCHED DATA SEGMENT # ITEM SWAPLEN; # LENGTH OF SWAPPED SEGMENT # ITEM SWAPPRUS; # LENGTH SWAPPED IN PRU COUNT # ITEM SWAPBUFLEN; # LENGTH SWAP CIRCULAR BUFFS # ITEM MAXSWPPAG=0; # HIGHEST (DISK ONLY) SWAP PAGE USED # ITEM MODELPTR; # POINTS TO COPY OF VIRGIN DATA # ITEM MODELLEN; # LENGTH OF VIRGIN COMMON COPY # ITEM FIELDLEN; ITEM LOGGEDIN=0; ITEM WRKFILLOCK B=FALSE; ITEM ENABLEFLAG=0; CONTROL IFEQ METERING,1; ITEM MAXLOGGED=0; ITEM STATTIME=0; CONTROL FI; CONTROL IFEQ ECSCODE,1; ITEM ECSFIELD=0; ITEM ECSERRORS=0; CONTROL FI; ITEM STATUSMSG C(30)="0000 USERS 00 TASKS"; BASED ARRAY ANYFET;; BASED ARRAY MOVFROM;; BASED ARRAY MOVTO;; ARRAY TTICONTROL [1:NUMTTIBUF]; BEGIN ITEM TTICTLWORD = [NUMTTIBUF(0)]; ITEM TTIBUFLEN U(0,0,12); ITEM TTIBUFLNK U(0,12,12); ITEM TTIBUFADDR U(0,42,18); END ARRAY SWAPBITS (NUMSWPBIT);; ARRAY TTIBITS (NUMTTIBIT);; ARRAY SWAPGOOD (NUMSWPBIT);; ARRAY SWAPBAD (NUMSWPBIT);; ARRAY TTIGOOD (NUMTTIBIT);; BASED ARRAY SWAPMASK;; # NOTE THAT CURUSERTAB MUST MATCH USERTAB, WORD BY FIELD. # # NOTE ALSO THAT THE FORMATS OF THESE ARRAYS ARE HARDCODED # # IN THE COMPASS SUBROUTINES GETUTAB AND SAVUTAB. # ARRAY CURUSERTAB (22); BEGIN ITEM CURUSLOGGED B(0)=[FALSE]; ITEM CURUSINQUE B(1)=[FALSE]; ITEM CURUSDROP B(02)=[FALSE]; # CURRENT USER DROP FLAG # ITEM CURUSDONE B(3)=[FALSE]; ITEM CURUSINTASK B(4)=[FALSE]; ITEM CURUSSTART B(5)=[FALSE]; ITEM CURUSINTRPT B(6)=[FALSE]; ITEM CURUSCRUNCH B(7)=[FALSE]; ITEM CURUSLOSTDT B(8)=[FALSE]; ITEM CURUSRCVRY B(9)=[FALSE]; ITEM CURUSOLDSND B(10)=[FALSE]; ITEM CURUSVICTIM B(11)=[FALSE]; ITEM CURUSSTMWRK B(12)=[FALSE]; ITEM CURUSOKOUT B(13)=[FALSE]; ITEM CURUSSTKPTR (14)=[0]; ITEM CURUSTASK (15)=[0]; ITEM CURUSTTIBUF (16)=[0]; ITEM CURUSQUELNK (17)=[0]; ITEM CURUSIAFNUM (18)=[0]; ITEM CURUSFNTPTR (19)=[0]; ITEM CURUSSWPPAG (20)=[0]; ITEM CURUSLASTIM (21)=[0]; END # NOTE COMMENTS ABOVE ON RESTRICTIONS AGAINST REDEFINITION # # OF EITHER CURUSERTAB OR USERTAB. # ARRAY USERTAB [1:NUMSMFUSR] S(3); BEGIN ITEM USERWORD0 (0)=[NUMSMFUSR(0)]; ITEM USERWORD1 (1)=[NUMSMFUSR(0)]; ITEM USERWORD2 (2)=[NUMSMFUSR(0)]; ITEM USERFLAGS U(0,0,18); ITEM USERLOGGED B(0,0,1); # IS LOGGED FULLY IN # ITEM USERINQUE B(0,1,1); # ALREADY IN SWAPIN QUEUE # ITEM USERDROP B(00,02,01); # USER DROPPED PHONE CONNECT # ITEM USERDONE B(0,3,1); # EDIT HAS COMPLETED # ITEM USERINTASK B(0,4,1); # ASSIGNED TO SOME TASK # ITEM USERSTART B(0,5,1); # ONLY FOR RIGHT AFTER LOGGED # ITEM USERINTRPT B(0,6,1); # WE MUST SOON SERVICE BREAK KEY # ITEM USERCRUNCH B(0,7,1); # USER IS FILE-CRUNCHING,LOW PRIO # ITEM USERLOSTDT B(0,8,1); # USER LOST EXCESS INPUT DATA # ITEM USERRCVRY B(0,9,1); # SWAPPING IN ONLY TO CHKPT # ITEM USEROLDSND B(0,10,1); # MIDDLE OF SERIES OF SENDS # ITEM USERVICTIM B(0,11,1); # VICTIMIZED BY SMFEX ECS ERROR # ITEM USERSTMWRK B(0,12,1); # USE STIMULATION WRKFIL NAME # ITEM USEROKOUT B(0,13,1); # O.K. TO PRODUCE TTY OUTPUT # ITEM USERSTKPTR U(0,18,6); # REENTRANCY STACK LEVEL # ITEM USERTASK U(0,24,6); # WHICH TASK ARE WE IN # ITEM USERTTIBUF U(0,30,6); # WHICH INPUT BUFFER WE OWN # ITEM USERQUELNK U(0,36,12); # LINKAGE IN SWAP QUEUE # ITEM USERIAFNUM U(0,48,12); # WHICH IAFEX PORT # ITEM USERFNTPTR U(1,0,12); # TO SPEED UP CIO'S # ITEM USERSWPPAG U(1,12,12); # WHICH SWAP PAGE # ITEM USERLASTIM U(1,24,30); # LAST TIME STAMP # ITEM USERJSN C(2,0,4); # JOB SEQUENCE NUMBER # ITEM USEREJT U(2,24,12); # JOB EJT ORDINAL # END ARRAY TASKTAB [1:NUMTASKS] P(13); BEGIN ITEM TASKSTATUS (0)=[0]; # CURRENT TASK STATUS # ITEM TASKFLAGS (1)=[0]; # ALL BITS OFF # ITEM TASKREQUE B(1,0,1); # USER NEEDS REPEATED SERVICE # ITEM TASKABORT B(1,2,1); # TASK IS ABORTING # ITEM TASKSWPNOW B(1,3,1); # CURRENTLY EXECING SWAP CODE # ITEM TASKSWPIN B(1,4,1); # DIRECTION OF SWAP IS IN # ITEM TASKUSER (2)=[0]; # WHICH USER IN THIS TASK # ITEM TASKNXTTTO (3)=[0]; # WHERE TO APPEND NEXT OUTPUT # ITEM TASKTYPTTO (4)=[0]; # TRANSMIT CODE DESIRED # ITEM TASKRCLADR (5)=[0]; # CURRENT RECALL FET # ITEM TASKADDR (6)=[0]; # ADDRESS OF TASK MEMORY # ITEM TASKSTKPTR (7)=[0]; # DISPATCHING RSTKPTR # ITEM TASKDSPADR (8)=[0]; # DISPATCH DATA START # ITEM TASKTIME (9)=[0]; # TIMEOFDAY SWAPPED IN # ITEM TASKSWPFIL (10)=[0]; # WHICH SWAP FILE IN USE # ITEM TASKSWPPRU (11)=[0]; # WHICH PRU IN THAT FILE # ITEM TASKPULSE (12)=[0]; # FAKE FET FOR CLOCK EVENTS # END ARRAY SWAPQ (3); BEGIN ITEM SWPQHEAD I(0)=[0]; ITEM SWPQTAIL I(1)=[0]; ITEM SWPQSIZE I(2)=[0]; END ARRAY SSCINPUT [-1:O"100"]; ITEM SSCINP (0)=[O"102"(0)]; ARRAY IAFMSGWORD; BEGIN ITEM IAFHEADER; # WHOLE WORD # ITEM IAFFUNC U(0,0,12)=[SMF2IAF"INIT"]; ITEM IAFTERM U(0,12,12)=[0]; ITEM IAFPTR U(0,24,18)=[0]; ITEM IAFLEN U(0,42,18)=[1]; END ARRAY INITSSC [0:1]; ITEM INITSSCWORD=[0,0]; BASED ARRAY SSCBUFFER; BEGIN ITEM SSCWORD; ITEM SSCCOUNT U(0,36,6); ITEM SSCTYPE U(0,46,2); ITEM SSCSTATUS U(0,48,12); END ARRAY NULLFET(FETSIZ); ITEM NULLFETDT C(1,0,2); ARRAY SWPFETS [1:NUMSWPFET] S(FETSIZ); BEGIN ITEM SWPFET; ITEM SWPFETDONE B(0,59,1); ITEM SWPFETDT C(1,0,2); ITEM SWPFETR B(1,12,1); ITEM SWPFETL U(1,36,6); ITEM SWPFETIN U(2,42,18); ITEM SWPFETOUT U(3,42,18); ITEM SWPFETCRI U(6,0,30); ITEM SWPFETW B(6,30,1); ITEM SWPFETRR U(6,31,29); END ARRAY DMBFET(FETSIZ);; ARRAY FILINFPARMS (5); BEGIN ITEM FILINFNAME C(0,0,7); ITEM FILINFLENC U(0,42,18)=[O"050001"]; ITEM FILINFSTAT U(1,12,42); ITEM FILINFTTY B(1,43,1); ITEM FILINFRMS B(1,44,1); ITEM FILINFWRIT B(1,52,1); ITEM FILINFREAD B(1,53,1); ITEM FILINFFT U(1,54,6); END PAGE # MINOR UTILITY ROUTINES # PROC INITBITMAP(MAP,NUMWORD,NUMBIT); BEGIN # ** INITBITMAP - INITIALIZE A BIT MAP ARRAY. * * INITBITMAP PREPARES BITMAPS FOR USAGE. A BIT MAP CONSISTS * OF ANY NUMBER OF WORDS, EACH CONTAINING A 32-BIT MASK. * THE MASK IS POSITIONED 12 BITS RIGHT FROM THE SIGN BIT OF * THE WORD. THIS FORMAT ALLOWS SEARCHING FOR BITS WHICH ARE * STILL ON BY INSERTING A FLOATING POINT EXPONENT AND * EXECUTING THE NORMALIZE INSTRUCTION. * * ENTRY MAP - THE MAP TO INITIALIZE. * NUMWORD - HOW MANY WORDS IN THE MAP. * NUMBIT - HOW MANY TOTAL BITS IN THE MAP. * * EXIT MAP - SETUP. * * CALLS FORCEALLOC. # ARRAY MAP[0:999]; ITEM MAPWORD; ITEM NUMWORD, NUMBIT; ITEM TMP1; FOR TMP1=0 STEP 1 UNTIL NUMWORD-1 DO MAPWORD[TMP1] =O"00007777777777600000"; FOR TMP1=NUMBIT+1 STEP 1 UNTIL NUMWORD*32 DO FORCEALLOC(MAP,TMP1,NUMWORD); END # OF INITBITMAP # PROC BUILDTASK(WHICH); BEGIN # ** BUILDTASK - ALLOCATE FIELD LENGTH AND INIT POINTERS. * * BUILDTASK ALLOCATES FIELD LENGTH FOR PER-TASK ARRAYS, AND * MAPS OUT THE BASE ADDRESSES FOR THE ARRAYS. BUILDTASK THEN * COPIES THE SET OF POINTERS INTO THE TASK'S DISPATCHING AREA. * * ENTRY WHICH - TASK NUMBER. * FIELDLEN - CURRENT TOP OF FIELD LENGTH. * * EXIT FIELDLEN - INCREASED. * TASKSPADR[WHICH] - ADDRESS OF TASK DISPATCH AREA. * TASK DISPATCH AREA - SETUP. * * CALLS FLDLEN, MOVEWD. * * USES POINTER WORDS FOR FOLLOWING ARRAYS - * ARRAYEND, ARRAYSTART, AUDITSTAGE, BACKSTACK, * BACKSTORE, BFPRU, CHGSTRING1, DISK, DISPRSTK, * FET, FKEYDEFS, LINEBUF, LOCSTRING1, LOCSTRING2, * MOVTO, OBF, READLIST, ROWCONTROL, SPLITCONTROL, * SWAPRSTK, TITLE1LIN, TITLE2LIN, TTOBUFFER. * * NOTE THIS CODE AND THE ARRAY LAYOUT IN COMFDAT ARE * EXTREMELY INTERDEPENDENT. # ITEM WHICH; TASKADDR[WHICH]=FIELDLEN; # FIRST ALLOCATE EDITOR ARRAYS, ORDER AND SIZE MATCHING COMFDAT # P=FIELDLEN; FIELDLEN=FIELDLEN+1; P = FIELDLEN; FIELDLEN = FIELDLEN + 6; # *TDU* TABLE LENGTH # P = FIELDLEN; FIELDLEN = FIELDLEN + O"272"; # *TDU* TABLE LENGTH # P=FIELDLEN; FIELDLEN=FIELDLEN+2*SPLTCTLSIZ; P=FIELDLEN; FIELDLEN=FIELDLEN+2*(MAXROWS+1); P=FIELDLEN; FIELDLEN=FIELDLEN+(BACKMAX+1); P=FIELDLEN; FIELDLEN=FIELDLEN+(2*(TEMPIND+1)); P=FIELDLEN; FIELDLEN=FIELDLEN+NUMFKEYS; P=FIELDLEN; FIELDLEN=FIELDLEN+AUDITSIZE+1; P=FIELDLEN; FIELDLEN=FIELDLEN+STRWID; P=FIELDLEN; FIELDLEN=FIELDLEN+STRWID; P=FIELDLEN; FIELDLEN=FIELDLEN+STRWID; P=FIELDLEN; FIELDLEN=FIELDLEN+(2*NUMFKEYS); P=FIELDLEN; FIELDLEN=FIELDLEN+POSFKEYS; P=FIELDLEN; FIELDLEN=FIELDLEN+TTLLNLEN+1; P=FIELDLEN; FIELDLEN=FIELDLEN+TTLLNLEN+1; P=FIELDLEN; FIELDLEN=FIELDLEN+1; # END OF ALLOCATED EDITOR ARRAYS # # ALLOCATE BUFFERS AND CONTROL VECTORS # P=FIELDLEN; FIELDLEN=FIELDLEN+BUFSIZE; P=FIELDLEN; FIELDLEN=FIELDLEN+MAXREENT; TASKDSPADR[WHICH]=FIELDLEN; FIELDLEN=FIELDLEN+DISPATLEN; P=FIELDLEN; FIELDLEN=FIELDLEN+MAXREENT; P=FIELDLEN; FIELDLEN=FIELDLEN+SIZTTOBUF+2; P=FIELDLEN; FIELDLEN=FIELDLEN+FETSIZ; P=FIELDLEN; FIELDLEN=FIELDLEN+OBFSIZE; P=FIELDLEN; FIELDLEN=FIELDLEN+DSKSIZ; P=FIELDLEN; FIELDLEN=FIELDLEN+LSTSIZE; FLDLEN(FIELDLEN+4); P=LOC(LIN); P=TASKDSPADR[WHICH]; MOVEWD(DISPATLEN,DATASTART,MOVTO); END # OF BUILDTASK # PROC GENWRKNAM; BEGIN # ** GENWRKNAM - GENERATE WORKFILE NAME FOR THIS USER. * * GENWRKNAM DETERMINES THE CORRECT FILENAME FOR THE WORK- * FILE FOR THE CURRENT USER. THE FILE NAME IS UNIQUE FOR * EACH USER AND IS GENERATED BY THE USER'S TLX CALL TO * CONNECT TO SMFEX, AND IS EQUAL TO "WK" PLUS JSN. * * ENTRY CURUSIAFNUM - IAF TERMINAL NUMBER. * USERJSN[CURUSER] - JSN OF THE USER'S JOB. * * EXIT WORKNAM - CORRECT NAME. # C<0,2>WORKNAM="WK"; C<2,4>WORKNAM=USERJSN[CURUSER]; C<6,1>WORKNAM=0; END # OF GENWRKNAM # CONTROL IFEQ METERING,1; PROC ADDVECT(VECTOR,DATA); BEGIN # ** ADDVECT - ADD DATA INTO A STATISTICS VECTOR. * * ADDVECT IS PART OF THE INSTRUMENTATION FEATURE OF SMFEX * AND SERVES TO INCREMENT THE TOTALS AND ONE VALUE-DEPENDENT * COUNTER IN A SELECTED VECTOR. THE VECTOR HAS ONE WORD FOR * TOTAL VALUE AND TOTAL EVENT COUNT, THEN FOUR WORDS EACH * CONTAINING FOUR 15-BIT COUNTERS. THE COUNTER TO BE * INCREMENTED IS SELECTED BY THE DATA VALUE, WITH THE FIRST * COUNTER FOR VALUES OF ZERO, THE SECOND FOR VALUES OF ONE, * AND THE THIRD THRU SIXTEENTH COUNTER CORRESPONDING TO * VALUES IN THE RANGE (2**(N-2)) .LE. VALUE .LT. (2**(N-1)). * * ENTRY DATA - VALUE TO RECORD. * * EXIT VECTOR - UPDATED. # ITEM TMP1, TMP2, TMP3 R; ARRAY VECTOR[0:4]; ITEM WORD; ITEM DATA; B<0,30>WORD[0]=B<0,30>WORD[0]+DATA; B<30,30>WORD[0]=B<30,30>WORD[0]+1; IF DATA LQ 0 THEN TMP1=0; ELSE BEGIN TMP3=DATA*1.0; TMP1=B<6,6>TMP3-15; END TMP1=MAX(TMP1,0); TMP1=MIN(TMP1,15); TMP2=MOD4(TMP1)*15; TMP1=(TMP1/4)+1; IF BWORD[TMP1] NQ O"77777" THEN BWORD[TMP1]=BWORD[TMP1]+1; END # OF ADDVECT # CONTROL FI; PAGE # TRACE ROUTINES FOR DEBUG # DEF TCTL # CONTROL IFEQ TRACEFLAG,1; #; DEF TRCOUT(AA) # TCTL TRACEOUT(AA); CONTROL FI #; DEF TRCSTR(AA) # TCTL TRACESTR(AA); CONTROL FI #; DEF TRCDEC(AA) # TCTL TRACEDEC(AA); CONTROL FI #; DEF TRCFRC # TCTL TRACEFRC; CONTROL FI #; DEF TRCWORDS(AA,BB) # TCTL TRACEWORDS(AA,BB); CONTROL FI #; DEF TRCBOTH(AA,BB) # TCTL TRACEBOTH(AA,BB); CONTROL FI #; CONTROL IFEQ TRACEFLAG,1; PROC TRACEOUT(TEXT); BEGIN ARRAY TEXT[0:99]; ITEM TXTWORD; ITEM INITED B=FALSE; ARRAY TRCFET (FETSIZ);; ARRAY TRCLIN[0:20]; ITEM TRCLINE; ITEM TRCPTR,TXTPTR; ARRAY TRCBUFF [0:O"2000"];; IF NOT INITED THEN BEGIN INITED=TRUE; TRCPTR=0; MAKEFET(TRCFET,"TRACE",TRCBUFF,O"2001"); END TXTPTR=0; TRCLINE[TRCPTR]=TXTWORD[TXTPTR]; WHYLE B<48,12>TXTWORD[TXTPTR] NQ 0 DO BEGIN TXTPTR=TXTPTR+1; TRCPTR=TRCPTR+1; TRCLINE[TRCPTR]=TXTWORD[TXTPTR]; END FOR TXTPTR=9 STEP -1 WHILE TXTPTR GQ 0 AND CTRCLINE[TRCPTR] EQ ":" DO CTRCLINE[TRCPTR]=" "; TRCPTR=TRCPTR+1; TRCLINE[TRCPTR]=0; IF TRCPTR GQ 07 THEN BEGIN WRITEC(TRCFET,TRCLIN); TRCPTR=0; END RETURN; ENTRY PROC TRACEWORDS(TEXT,NWORDS); ITEM NWORDS; IF NOT INITED THEN BEGIN INITED=TRUE; TRCPTR=0; MAKEFET(TRCFET,"TRACE",TRCBUFF,O"2001"); END IF TRCPTR NQ 0 THEN BEGIN WRITEC(TRCFET,TRCLIN); TRCPTR=0; END WRITEW(TRCFET,TEXT,NWORDS); IF B<48,12>TXTWORD[NWORDS-1] NQ 0 THEN WRITEW(TRCFET,0,1); RETURN; ENTRY PROC TRACEFRC; IF TRCPTR NQ 0 THEN BEGIN WRITEC(TRCFET,TRCLIN); TRCPTR=0; END WRITER(TRCFET,1); END # OF TRCOUT # PROC TRACESTR(STR); BEGIN ITEM STR C(40); ARRAY TEXT [0:4]; ITEM TXTWORD; ITEM TMP1; TXTWORD[0]=0; FOR TMP1=0 STEP 1 WHILE TMP1 LQ 39 AND CSTR NQ "$" DO BEGIN CTXTWORD[TMP1/10]=CSTR; TXTWORD[1+TMP1/10]=0; END TRCOUT(TEXT); END # OF TRCSTR # PROC TRACEDEC(NUM); BEGIN ITEM NUM; ITEM TMP1,TMP2,TMP3; TMP1=NUM; TMP2=O"55555555555555330000"; TMP3=7; IF TMP1 LS 0 THEN BEGIN C<0,1>TMP2="-"; TMP1=-TMP1; END WHYLE TMP1 NQ 0 DO BEGIN CTMP2=O"33"+MOD(TMP1,10); TMP3=TMP3-1; TMP1=TMP1/10; END TRCOUT(TMP2); END # OF TRCDEC # PROC TRACEBOTH(AA,BB); BEGIN ARRAY AA;; ARRAY BB;; TRCSTR(AA); TRCDEC(BB); END # OF TRCBOTH # CONTROL FI; PAGE # UTIITIES FOR QUEUES, CHAINS, TABLES # PROC GETUSER; BEGIN # ** GETUSER - MAKE TABLE ENTRIES ACCESSIBLE FOR CURRENT USER. * * GETUSER UNPACKS THE DENSELY FORMATTED, INDEXABLE USER * TABLE INTO THE SPARSE, NONINDEXABLE USER TABLE. THE DENSE * TABLE PROVIDES CONSERVATION OF MEMORY CAPACITY, WHILE * THE SPARSE TABLE PROVIDES CONSERVATION OF CODE SIZE AND * TIMING FOR FREQUENT REFERENCES. * * ENTRY CURUSER - INDEX FOR WHICH USER. * USERTAB[CURUSER] - "DENSE PACK" FORMATTED TABLE. * * EXIT CURUSERTAB - EXPANDED COPY OF USERTAB. * * CALLS GETUTAB. # BASED ARRAY PARM;; P=LOC(USERTAB[CURUSER]); GETUTAB(PARM,CURUSERTAB); END # OF GETUSER # PROC PUTUSER; BEGIN # ** PUTUSER - PRESERVE TABLE ENTRIES IN DENSE TABLE. * * PUTUSER IS CALLED TO COPY AND PACK THE SPARSELY FORMATTED, * NON-INDEXABLE USER TABLE INTO THE DENSELY FORMATTED, * INDEXABLE TABLE. PUTUSER IS THUS THE OPPOSITE OF * GETUSER. NOTE THAT THE SPARSE TABLE IS TEMPORARY AND * THE DENSE TABLE IS PERMANENT. * * ENTRY CURUSER - CURRENT USER. * CURUSERTAB - SPARSE TABLE. * * EXIT USERTAB[CURUSER] - PACKED VERSION OF TABLE. * * CALLS SAVUTAB. # BASED ARRAY PARM;; P=LOC(USERTAB[CURUSER]); SAVUTAB(PARM,CURUSERTAB); END # OF PUTUSER # PROC GETCOMMON; BEGIN # ** GETCOMMON - GET STORAGE FOR RIGHT TASK INTO COMMON. * * TO CONSERVE CPU TIME REQUIRED FOR SHUFFLING OF THE COMMON * BLOCK IN MULTI-TASKING, THE COMMON BLOCK IS LEFT VOLATILE * WHEN ONE TASK GIVES UP THE CPU IN THE HOPE THAT THE SAME * TASK WILL BE THE NEXT TO USE THE CPU. WHEN A DIFFERENT * TASK IS NEXT ON THE CPU, THEN WE MUST PERFORM THE DEFERRED * COPYING OF COMMON TO/FROM TASK DISPATCHING AREAS. * * ENTRY LASTTASK - TASK WHICH MOST RECENTLY USED COMMON. * ZERO INDICATES COMMON IS NOT VOLATILE. * CURTASK - TASK WHICH NEEDS COMMON. * TASKSWPNOW[LASTTASK], TASKSWPIN[LASTTASK] - * INDICATE IF VOLATILITY SUPPRESSED BY * CURRENTLY ACTIVE SWAPIN. * * EXIT LASTTASK - EQUALS CURTASK. * COMMON BLOCK - SAVED IN LASTTASK IF NEEDED, * UPDATED FOR CURTASK IF NEEDED. * * CALLS MOVEWD. * * USES P, P. # IF LASTTASK NQ 0 AND LASTTASK NQ CURTASK AND NOT (TASKSWPNOW[LASTTASK] AND TASKSWPIN[LASTTASK]) THEN BEGIN P=TASKDSPADR[LASTTASK]; MOVEWD(DISPATLEN,DATASTART,MOVTO); END IF LASTTASK NQ CURTASK THEN BEGIN P=TASKDSPADR[CURTASK]; MOVEWD(DISPATLEN,MOVFROM,DATASTART); END LASTTASK=CURTASK; END # OF GETCOMMON # PROC PUTCOMMON; BEGIN # ** PUTCOMMON - COPY COMMON INTO DISPATCHING AREA. * * PUTCOMMON IS CALLED WHEN THE LIVE COMMON BLOCK IS CONSIDERED * MORE CURRENT THAN THE COMMON IMAGE IN THE TASK DISPATCHING * AREA, AND REQUIRES IMMEDIATE COPYING. * * ENTRY CURTASK - CURRENT TASK. * TASKDSPADR[CURTASK] - ADDRESS OF DISPATCH AREA. * * EXIT DISPATCH AREA - UPDATED FROM DATASTART. * * CALLS MOVEWD. * * USES P. # # COPY DISPATCHABLE STORAGE FOR THIS TASK OUT OF COMMON # P=TASKDSPADR[CURTASK]; MOVEWD(DISPATLEN,DATASTART,MOVTO); END # OF PUTCOMMON # PROC KILL; BEGIN # ** KILL - ERASE USE FROM TABLES. * * KILL IS CALLED WHEN DISCONNECTING A USER. IT IS ASSUMED * THE USER IS NOT IN THE SWAPIN QUEUE. THE USER MAY * HAVE TERMINAL INPUT BUFFERS, WHICH ARE RELEASED. THEN * THE TABLE ENTRIES ARE ZEROED. * * ENTRY CURUSER - WHO TO KILL. * CURUSIAFNUM - IAF TERMINAL NUMBER. * * EXIT USERTAB[CURUSER], ORDWORD[CURUSIAFNUM] - ZERO. * CURUSERTAB - ZERO ALSO. * * CALLS PURGETTI, PUTUSER, GETUSER. # PURGETTI; USERWORD0[CURUSER]=0; USERWORD1[CURUSER]=0; USERWORD2[CURUSER]=0; GETUSER; END # OF KILL # PROC SCHEDULE; BEGIN # ** SCHEDULE - ASSURE USER WILL RUN IN A TASK SOON. * * SCHEDULE IS USED WHENEVER WE RECEIVE A CODE FROM IAFEX, * ASSURES THE USER WILL BE SCHEDULED FOR TASK SERVICE OR WILL * BE ADEQUATELY SERVICED IF ALREADY IN A TASK. * * ENTRY CURUSER - USER TO BE QUEUED. * CURUSERTAB - ALL ENTRIES SETUP. * CURUSINTASK - WHETHER ALREADY SCHEDULED. * CURUSINQUE - WHETHER ALREADY QUEUED. * CURUSTASK - WHICH TASK IF CURUSINTASK TRUE. * * EXIT USER QUEUED IF NOT ALREADY IN TASK. * TASKREQUE[] - SET IF IN TASK TO SIGNAL EVENT. * TASKPULSE[] - SET TO QUICKLY END ANY DELAY. * * CALLS PUTINQ. # IF CURUSINTASK THEN BEGIN TASKREQUE[CURUSTASK]=TRUE; # ASSURE KEEPS RUNNING # TASKPULSE[CURUSTASK]=3; # ASSURE NO CLOCK DELAY LOOP # END ELSE IF NOT CURUSINQUE THEN PUTINQ; END # OF SCHEDULE # PROC PUTINQ; BEGIN # ** PUTINQ - PUT CURRENT USER IN SCHEDULE QUEUE. * * PUTINQ LINKS THE CURRENT USER ONTO THE QUEUE OF JOBS * WHICH HAVE BEEN SCHEDULED FOR FUTURE TASK ASSIGNMENT. * A NEW QUEUE LINKAGE IS STARTED IF THERE IS NO QUEUE * ALREADY. * * ENTRY CURUSER - CURRENT USER. * CURUSERTAB - ALL ENTRIES SETUP. * CURUSINQUE - MUST BE FALSE. * SWPQHEAD - ZERO IF NO QUEUE EXISTS, OR LINK TO HEAD * OF QUEUE IF OTHER USERS ALREADY QUEUED. * SWPQTAIL - IF SWPQHEAD NON-TRIVIAL, LAST USER QUEUED. * SWPQSIZE - NUMBER OF USERS IN QUEUE. * * EXIT VIA MELT IF INVALID CONDITIONS. * SWPQHEAD - HEAD OF QUEUE. (CURUSER IF NEW QUEUE) * SWPQTAIL - TAIL OF QUEUE NOW EQUALS CURUSER. * USERQUELNK[OLD SWPQTAIL] - LINKS TO CURUSER IF THERE * WAS A PREVIOUS QUEUE. * CURUSINQUE - TRUE. * SWPQSIZE - INCREMENTED. * * CALLS MELT. # CONTROL IFGQ PARANOIA,5; IF CURUSINQUE THEN MELT("PUTINQ 1$"); CONTROL FI; IF SWPQHEAD EQ 0 THEN # NEW QUEUE # BEGIN SWPQHEAD=CURUSER; SWPQTAIL=CURUSER; CURUSQUELNK=0; END ELSE # APPEND TO QUEUE # BEGIN USERQUELNK[SWPQTAIL]=CURUSER; CURUSQUELNK=0; SWPQTAIL=CURUSER; END CURUSINQUE=TRUE; SWPQSIZE=SWPQSIZE+1; END # OF PUTINQ # PROC TAKEOFFQ; BEGIN # ** TAKEOFFQ - TAKE CURRENT USER OFF QUEUE. * * TAKEOFFQ REMOVES THE CURRENT USER FROM THE QUEUE OF USERS * SCHEDULED FOR FUTURE TASK ASSIGNMENT. THE CURRENT USER IS * REQUIRED TO BE IN THE QUEUE, BUT IS ALLOWED TO BE AT THE * HEAD, OR AT THE TAIL, OR ANYWHERE IN BETWEEN. TAKEOFFQ * THEREFORE CONTAINS LOGIC TO CLOSE QUEUE LINKAGE UP AROUND * THE EXTRACTED USER. * * ENTRY CURUSER - CURRENT USER. * CURUSERTAB - ALL ENTRIES SETUP. * CURUSINQUE - MUST BE TRUE. * SWPQHEAD - HEAD OF QUEUE. * SWPQTAIL - TAIL OF QUEUE. * SWPQSIZE - NUMBER OF USERS IN QUEUE. * USERQUELNK[ALL IN QUEUE] - LINKAGE. * * EXIT CURUSINQUE - FALSE. * CURUSQUELNK - ZEROED OUT. * SWPQTAIL - BACKED TO PREVIOUSLY NEXT-TO-LAST * USER IF CURUSER MATCHED SWPQTAIL. * SWPQHEAD - ADVANCED TO PREVIOUSLY SECOND USER IF * CURUSER MATCHED SWPQHEAD. ZERO IF CURUSER WAS * ONLY USER IN QUEUE. * SWPQSIZE - DECREMENTED. * * CALLS MELT. # ITEM TMP1,TMP2; CONTROL IFGQ PARANOIA,5; IF NOT CURUSINQUE THEN MELT("TAKEOFFQ 1$"); CONTROL FI; IF SWPQHEAD EQ CURUSER THEN # TAKE FROM FRONT # BEGIN IF SWPQHEAD EQ SWPQTAIL THEN SWPQTAIL=0; SWPQHEAD=CURUSQUELNK; END ELSE # FIND THEN EXTRACT # BEGIN TMP1=SWPQHEAD; WHYLE TMP1 NQ CURUSER DO BEGIN CONTROL IFGQ PARANOIA,5; IF TMP1 EQ 0 THEN MELT("TAKEOFFQ 2$"); CONTROL FI; TMP2=TMP1; TMP1=USERQUELNK[TMP1]; END IF TMP1 EQ SWPQTAIL THEN SWPQTAIL=TMP2; USERQUELNK[TMP2]=USERQUELNK[TMP1]; END CURUSINQUE=FALSE; CURUSQUELNK=0; SWPQSIZE=SWPQSIZE-1; END # OF TAKEOFFQ # PROC ALLOC(BITMAP,BITMASK,INDEX,LIMIT); BEGIN # ** ALLOC - ALLOCATE RESOURCE VIA BITMAP. * * SEARCH THE BITMAP FOR AN AVAILABLE ENTITY, RETURN THE INDEX * FOR IT. SET THE BIT AS UNAVAILABLE. BITMAPS ARE 32-BITS * PER WORD, CENTERED, SO THAT THE FINDBIT ROUTINE CAN USE * OPTIMAL INSTRUCTIONS (E.G., NORMALIZE) TO FIND A BIT. A * BIT IS ON TO SHOW AVAILABLE ENTITY. * * ENTRY BITMAP - THE BITMAP ARRAY TO SEARCH AND UPDATE. * BITMASK - ARRAY WITH "DONT USE" FLAGS. * LIMIT - SIZE OF ARRAYS. * * EXIT INDEX - ORDINAL OF ALLOCATED RESOURCE. * BITMAP - UPDATED TO REFLECT ALLOCATION. * * CALLS FINDBIT. # ARRAY BITMAP[0:99]; ITEM BITWORD; ARRAY BITMASK;; ITEM INDEX, LIMIT; INDEX=FINDBIT(LIMIT,BITMAP,BITMASK); IF INDEX NQ -1 THEN B<12+MOD32(INDEX),1>BITWORD[INDEX/32]=0; INDEX=INDEX+1; # ADJUST TO ORIGIN OF ONE # END # OF ALLOC # PROC DEALLOC(BITMAP,INDEX,LIMIT); BEGIN # ** DEALLOC - UPDATED BITMAP TO MAKE RESOURCE AVAILABLE. * * DEALLOC IS THE COUNTERPART TO ALLOC. * * ENTRY BITMAP - ARRAY CONTAINING ALLOCATION FLAGS. * INDEX - WHICH RESOURCE TO FREE UP. * LIMIT - SIZE OF BITMAP. * * EXIT BITMAP - UPDATED. * * CALLS MELT. # ITEM INDEX, LIMIT; ARRAY BITMAP [0:99]; ITEM BITWORD; ITEM TMP1; TMP1=INDEX-1; CONTROL IFGQ PARANOIA,5; IF TMP1 GQ LIMIT*32 OR TMP1 LS 0 THEN MELT("DEALLOC 1$"); IF B<12+MOD32(TMP1),1>BITWORD[TMP1/32] EQ 1 THEN MELT("DEALLOC 2$"); CONTROL FI; B<12+MOD32(TMP1),1>BITWORD[TMP1/32]=1; END # OF DEALLOC # PROC FORCEALLOC(BITMAP,INDEX,LIMIT); BEGIN # ** FORCEALLOC - ALLOCATE SPECIFIC RESOURCE WITHOUT SEARCH. * * FORCES A BIT AS UNAVAIL. USED TO CUT OFF EXCESS BITS AT * END OF A BITMAP AND TO SHUT OFF ENTITIES THAT HAVE SUFFERED * HARDWARE FAILURES. (SUCH AS SEGMENTS OF ECS) * * ENTRY BITMAP - ARRAY OF ALLOCATION FLAGS. * INDEX - WHICH RESOURCE TO ALLOCATE. * LIMIT - SIZE OF BITMAP. * * EXIT BITMAP - UPDATED. * * CALLS MELT. # ITEM INDEX,LIMIT; ARRAY BITMAP[0:99]; ITEM BITWORD; ITEM TMP1; TMP1=INDEX-1; CONTROL IFGQ PARANOIA,5; IF TMP1 GQ LIMIT*32 OR TMP1 LS 0 THEN MELT("FORCEALLOC 1$"); CONTROL FI; B<12+MOD32(TMP1),1>BITWORD[TMP1/32]=0; END # OF FORCEALLOC # PROC KILLECSBITS(BITMAP); BEGIN # ** KILLECSBITS - FORCEALLOC ALL ECS SWAP PAGES. * * ENTRY BITMAP - ARRAY OF ALLOCATION FLAGS TO UPDATE. * * BITMAP - UPDATED. * * CALLS FORCEALLOC. # ARRAY BITMAP;; ITEM TMP1; FOR TMP1=1 STEP 1 UNTIL NUMSWPECS DO FORCEALLOC(BITMAP,TMP1,NUMSWPBIT); END # OF KILLECSBITS # PROC PURGETTI; BEGIN # ** PURGETTI - GET RID OF ANY TERMINAL INPUT CHAINS. * * ENTRY CURUSER - CURRENT USER. * CURUSTTIBUF - FIRST TTI BUFFER OR NULL. * REMAINTTI - HOW MANY BUFFERS STILL AVAIL. * TTIBUFLNK[ALL] - LINKAGES. * TTIBITS - ALLOCATION BITMASK. * * EXIT CURUSTTIBUF - NULL. * REMAINTTI - INCREMENTED. * TTIBITS - UPDATED. * * CALLS DEALLOC. # ITEM TMP1; TRCSTR("PURGETTI$"); TMP1=CURUSTTIBUF; WHYLE TMP1 NQ 0 DO BEGIN CURUSTTIBUF=0; DEALLOC(TTIBITS,TMP1,NUMTTIBIT); REMAINTTI=REMAINTTI+1; TMP1=TTIBUFLNK[TMP1]; END END # OF PURGETTI # PAGE # MAJOR SYSTEM STATE ROUTINES # PROC DROPSLICE; BEGIN # ** DROPSLICE - RECALL CPU FOR A LITTLE WHILE. * * ENTRY NO CONDITIONS. * * EXIT REAL TIME HAS BEEN DELAYED. * * CALLS RECALL, INSTRMNT1. # RECALL(0); CONTROL IFEQ METERING,1; INSTRMNT1(INSTST"CLOCKTICK"); CONTROL FI; END # OF DROPSLICE # PROC DOTIMER; BEGIN # ** DOTIMER - SUPERVISE TIME DEPENDENCIES. * * DOTIMER IS INTENDED TO BE CALLED ONCE PER MAIN LOOP. THE * CPU IS RECALLED ONCE TO ACHIEVE A SMALL DELAY IN REAL TIME, * ABOUT 24 MILLISECONDS IF THE INSTALLATION RUNS WITH DEFAULT * OPERATOR PARAMETERS, THE TIME OF DAY IS NOTED, AND CERTAIN * FUNCTIONS ARE PERFORMED IF THE TIME OF DAY HAS ADVANCED * SUFFICIENTLY SINCE THE LAST TIME SUCH FUNCTIONS WERE * PERFORMED. * * ON A CYCLE OF APPROX FIVE TIMES PER SECOND, WE LOOK FOR * OPERATOR COMMANDS AND SIGNAL RESTART OF ANY TASKS WHICH ARE * DELAYING FOR THE PURPOSE OF ACHIEVING A QUICK RECYCLE OF * THE SAME TASK FOR THE SAME USER. * * ON A CYCLE OF APPROX TWO SECONDS, THE B-DISPLAY MESSAGE IS * UPDATED TO SHOW CURRENT LOAD PARAMETERS - NUMBER OF USERS * IN THE SUBSYSTEM AND NUMBER OF TASKS CURRENTLY ASSIGNED TO * EXECUTE. * * ENTRY TIMEOFDAY - TIME AT WHICH PREVIOUS CPU SLICE STARTED. * NEWTIMEDAY - EQUALS TIMEOFDAY OR TIME AT WHICH A TASK * WAS CONSIDERED FOR PREEMPTIVE SWAP, WHICHEVER LARGER. * SLICECOUNT - NUMBER OF CYCLES EXECUTED SO FAR. * LOGGEDIN - NUMBER OF USERS. * TASKSBUSY - NUMBER OF TASKS UTILIZED. * ENABLEFLAG - ENABLE/DISABLE NEW USERS OR NO-OP. * * EXIT OLDTIMEDAY - PREVIOUS VALUE FOR TIMEOFDAY. * TIMEOFDAY, NEWTIMEDAY - CURRENT TIME. * SLICECOUNT - INCREMENTED. * SHUTDOWN, SENSE SWITCHES - REFER TO DOOPER ROUTINE. * TASKPULSE[ALL] - 1 IF 5-HERTZ CYCLE OCCURRED. * B-DISPLAY UPDATED IF 2-SECOND CYCLE. * * CALLS DROPSLICE, DOOPER, RTIME, SMFSSTF, SFMCSTF, * CHECKTIME(INTERNAL), MESSAGE. # # HANDLES TIME-OF-DAY DEPENDENT STUFF # ITEM LAST5HZ=0, LAST2SEC=0; ITEM TMP1, TMP2, BOOL B; PROC CHECKTIME(LAST,INTERVAL,FLAG); BEGIN ITEM LAST, INTERVAL, FLAG B; FLAG=FALSE; IF TIMEOFDAY-LAST GQ INTERVAL THEN BEGIN LAST=TIMEOFDAY; FLAG=TRUE; END END # OF CHECKTIME # # DOTIMER MAIN CODE STARTS HERE # DROPSLICE; # RECALL FOR A WHILE # OLDTIMEDAY=NEWTIMEDAY; RTIME(TIMEOFDAY); B<0,30>TIMEOFDAY=0; # DONT NEED UPPER PART # NEWTIMEDAY=TIMEOFDAY; SLICECOUNT=SLICECOUNT+1; IF ENABLEFLAG NQ 0 THEN BEGIN IF ENABLEFLAG GQ 0 AND SHUTDOWN EQ 0 THEN SFMSSTF; IF ENABLEFLAG LS 0 THEN SFMCSTF; ENABLEFLAG=0; END CHECKTIME(LAST5HZ,200,BOOL); # 5 TIMES PER SECOND # IF BOOL THEN BEGIN DOOPER; # LOOK FOR OPERATOR COMMANDS # # ACTIVATE ANY TASKS THAT ARE SPINNING WHEELS # FOR CURTASK=1 STEP 1 UNTIL NUMTASKS DO TASKPULSE[CURTASK]=3; END CHECKTIME(LAST2SEC,2000,BOOL); # ONCE EVERY TWO SECONDS # IF BOOL THEN BEGIN # DISPLAY LOAD FACTORS # TMP1=LOGGEDIN; FOR TMP2=3 STEP -1 UNTIL 0 DO BEGIN CSTATUSMSG=MOD(TMP1,10)+O"33"; TMP1=TMP1/10; END C<12,1>STATUSMSG=O"33"+MOD(TASKSBUSY,10); C<11,1>STATUSMSG=O"33"+MOD(TASKSBUSY/10,10); C<28,2>STATUSMSG=0; MESSAGE(STATUSMSG,1,1); END END # OF DOTIMER # PROC DOOPER; BEGIN # ** DOOPER - PERFORM OPERATOR FUNCTIONS. * * DOOPER SHOULD BE CALLED FROM TIME TO TIME TO SEE IF THE * ENVIRONMENT INDICATES AN OPERATIONAL CHANGE IS OCCURRING. * * THE FIRST ISSUANCE OF "IDLE,SMF." INDICATES THAT SMFEX * SHOULD ENROLL NO NEW USERS AND SHOULD TRANSITION EXISTING * USERS BACK TO THEIR SINGLE-USER EDITORS WITH COMPLETE * TRANSPARENCY. THIS CAN BE DONE AS THE USERS CYCLE THROUGH * THE MAIN LOOP OF THE EDITOR. WHEN THE USER COUNT REACHES * ZERO, SMFEX WILL SHUT DOWN. * * A SECOND ISSUE OF THE IDLE COMMAND REQUESTS THAT SMFEX GET * OUT OF THE SYSTEM MORE RAPIDLY, POSSIBLY AT THE EXPENSE OF * SOME USERS RECEIVING INTERRUPT WARNING MESSAGES BUT STILL * WITH NO LOSS OF DATA. SMFEX CAN STOP AS QUICKLY AS IT CAN * GET ANY CURRENT TASKS TO LEAVE THEIR INTERNAL CONTROL POINTS. * * A THIRD ISSUE OF THE IDLE COMMAND REQUESTS EVEN MORE RAPID * SHUTDOWN. ANY CURRENT TASKS WILL BE PAUSED AT THEIR NEXT * CIO RECALL. THUS SOME USER COMMANDS MAY NOT COMPLETE, BUT * NO EDIT SESSIONS WILL SUFFER REGRESSIVE DAMAGE. * * ANY TIME DOOPER DETECTS EITHER THE SLOWDOWN MODE OR THE * QUICK IDLE MODE, IT SETS THE ENABLEFLAG SO THAT DOTIMER * WILL DISABLE THE ACCESSABILITY OF THE SUBSYSTEM. * * ENTRY SHUTDOWN - PREVIOUS SHUTDOWN LEVEL IF ANY. * 0 = NORMAL PROCESSING. * 1 = ACCEPT NO USERS, KEEP RUNNING. * 2 = SPECIAL FOR RECOVERY FUNCTION. * 3 = SHUTDOWN WHEN TASKS BECOME EMPTY. * 4 = SHUTDOWN WHEN CIO CALLS COMPLETE. * SWAPGOOD, SWAPBAD - EXTENDED MEMORY CONTROLS. * * EXIT SHUTDOWN - UPDATED. * MEM[0] - UPDATED TO ACKNOWLEDGE IDLE COMMAND. * ENABLEFLAG - FORCED NEGATIVE IF DETECT NEW NON-TRIVIAL * VALUE FOR SHUTDOWN FLAG. # ITEM TMP1; IF SHUTDOWN EQ 2 THEN RETURN; IF MEM[0] LAN O"100000" NQ 0 THEN # IDLE,SMF. # BEGIN IF IDLEDOWN THEN STOPNOW=TRUE; ELSE IF ACCEPTNONE THEN IDLEDOWN=TRUE; ELSE ACCEPTNONE=TRUE; MEM[0]=MEM[0] LXR O"100000"; # ACKNOWLEDGE IDLE COMMAND # ONSW(O"40"); # SIGNAL SUBSYSTEM TERMINATION # END TMP1=0; IF ACCEPTNONE THEN TMP1=1; IF IDLEDOWN THEN TMP1=3; IF STOPNOW THEN TMP1=4; IF SHUTDOWN NQ TMP1 THEN BEGIN IF TMP1 NQ 0 THEN ENABLEFLAG=-1; ELSE ENABLEFLAG=1; END SHUTDOWN=TMP1; CONTROL IFEQ ECSCODE,1; IF ECSDOWN THEN P=LOC(SWAPBAD); ELSE P=LOC(SWAPGOOD); CONTROL FI; END # OF DOOPER # PAGE # DOINPUT -- RECEIVE SIC MSG AND QUEUE # PROC DOINPUT; BEGIN # ** DOINPUT - ACCEPT MESSAGES FROM IAFEX. * * DOINPUT SUPERVISES ALL MESSAGE RECEIVED FROM IAF, AND * SHOULD BE CALLED BY THE MAIN LOOP ONCE PER RECALL CYCLE. * IAF COMMUNICATES WITH SMF BY USING THE SCP/UCP FACILITY * (IAF IS THE SCP AND SMF IS THE UCP) TO PLACE MESSAGES * INSIDE THE SMF FIELD LENGTH. THE ADDRESS OF THE SMF WINDOW * FOR MESSAGES WAS ESTABLISHED BY SMF WHEN IT FIRST SENT A * MESSAGE TO IAF. IAF CANNOT SEND ITS NEXT MESSAGE UNTIL SMF * ACKNOWLEDGES THE CURRENT MESSAGE BY CLEARING THE HEADER * WORD. * * NOTE THAT THE DOINPUT ROUTINE PROCESSES RECEIPT OF * MESSAGES, BUT HAS NOTHING TO DO WITH SENDING MESSAGES TO * IAFEX. THAT FUNCTION IS PROVIDED AS TASKS EXECUTE VIA * EXECUTIVE ROUTINES CALLED BY THE EDITOR MODULE. DOINPUT * CAN TRANSMIT A MESSAGE TO IAFEX, BUT ONLY UNDER THE * EMERGENCY SITUATION WHERE THE USER TABLE HAS OVERFLOWED, IN * WHICH CASE THE USER BEING CONNECTED MUST BE IMMEDIATELY * DISCONNECTED BEFORE ANY OTHER SMFEX PROCESSING CAN PROCEED. * THIS SITUATION IS NORMALLY DISCOURAGED BY LOGIC TO DISABLE * SMFEX SUBSYSTEM ACCESSABILITY WHEN THE USER TABLE GETS * CLOSE TO THE FULL CONDITION. * * MESSAGES FROM IAF ARE OF TWO BASIC FORMS - FIRST, A * QUICK-SHUTDOWN IS INDICATED BY NON-ZERO CONTENT APPEARING * IN SSCINP[-1], AND SECOND, A PRODUCTION MESSAGE IS * INDICATED BY NON-ZERO CONTENT IN ONE OR MORE WORDS STARTING * WITH SSCINP[0]. * * PRODUCTION MESSAGES CAN APPEAR IN SIX VARIATIONS - (1) * INITIALIZATION OF IAF, (2) COMMAND TEXT, (3) * ACKNOWLEDGEMENT THAT OUTPUT HAS BEEN RECEIVED AND MORE CAN * BE ACCEPTED, (4) USER BREAK, (5) DETACHMENT OF TERMINAL * FROM JOB, AND (6) CONNECTION OF A NEW USER INTO SMFEX. * * THE SECOND THRU FIFTH MESSAGE TYPES REQUIRE REFERENCE TO AN * EXISTING USER WITHIN SMFEX. THE SECOND MESSAGE TYPE * CARRIES ARBITRARY TEXT ALONG WITH THE HEADER WORD, AND THE * SIXTH MESSAGE TYPE CARRIES ONE WORD OF TEXT WITH THE HEADER * WORD. THE OTHER MESSAGE TYPES CONSIST SOLELY OF THE HEADER * WORD. * * COMMAND MESSAGES CAUSE SCHEDULING OF THE USER FOR TASK * PROCESSING, AND TO ENABLE THE RECEIPT OF ADDITIONAL * MESSAGES FROM IAF WHILE THE TASK SWAPS IN, THE MESSAGE TEXT * IS QUEUED IN ONE OF THE TERMINAL INPUT BUFFERS. SINCE ONE * COMMAND MESSAGE MAY BE JUST A FRAGMENT OF THE ENTIRE * COMMAND TEXT, INPUT BUFFERS ARE LINKED TO THE USER AND TO * EACH OTHER AS NEEDED. * * SMFEX ATTEMPTS TO AVOID RUNNING OUT OF INPUT BUFFERS WITH * "TRY HARDER" TASK SCHEDULING RULES, BUT IF BUFFERS DO GET * EXHAUSTED, THEN DOINPUT SIMPLY NEGLECTS TO CLEAR AWAY THE * IAF WINDOW AT SSCINP[0], THUS PREVENTING IAF FROM SENDING * MORE MESSAGES UNTIL WE CATCH UP AND CAUSING FUTURE DOINPUT * CYCLES TO ONCE AGAIN ATTEMPT TO QUEUE THE INPUT. IN THE * EXTREME CASE WHERE SMFEX FALLS BEHIND WITH IAFEX HOLDING 64 * MESSAGES IN ITS OWN QUEUE, THEN IAFEX WILL TREAT SMFEX AS A * DERELICT AND REVERT ALL USERS BACK TO SINGLE-USER MODE. * * USER BREAKS AND DETACHMENTS CAUSE TASK SCHEDULING WITH * APPROPRIATE FLAGS SET SO THE TASK WILL INTERRUPT NORMAL * PROCESSING AND REACT ACCORDINGLY. FOR DETACHMENT, IAF * DELAYS DETACH COMPLETION UNTIL SMFEX FINALLY TRANSMITS AN * END-OF-EDIT MESSAGE. * * OUTPUT ACCEPTANCE MESSAGES CAUSE TASK SCHEDULING FOR * CONTINUED EXECUTION. * * CONNECTION MESSAGES CAUSE SMFEX TO ALLOCATE A NEW TABLE * ENTRY FOR THE USER, SAVING THE JSN AND EJT ORDINAL PROVIDED * IN THE SECOND WORD OF THE MESSAGE, WITH THE NEW USER * SCHEDULED FOR TASK EXECUTION SO THAT THE TRANSFERRED * WORKFILE CAN BE QUICKLY VALIDATED. * * ENTRY SSCINP[-1] - AS CLEARED BY PREVIOUS DOINPUT, OR * NEW SHUTDOWN MESSAGE FROM IAFEX. * SSCINP[0] - AS CLEARED BY PREVIOUS DOINPUT, OR * HEADER OF NEW MESSAGE FROM IAFEX. * SHUTDOWN - WHETHER TO PROCESS INPUT NORMALLY. * USER AND TASK TABLES SETUP. * REMAINTTI - NUMBER OF INPUT QUEUE BUFFERS LEFT. * TTIBITS - ALLOCATION BITMASK FOR INPUT BUFFERS. * TTIBUFADDR[ALL] - WHERE THE INPUT BUFFERS ARE. * TTIBUFLNK[ALL] - EXISTING LINKAGES OF GROUPED BUFFERS. * LOGGEDIN - NUMBER OF USERS IN SMFEX. * MAXLOGGED - HIGH WATER MARK OF LOGGEDIN. * SWAP QUEUE - SETUP. * * EXIT SSCINP[-1] - CLEARED. * SSCINP[0] - CLEARED UNLESS REMAINTTI WAS ZERO WITH * A COMMAND MESSAGE AWAITING QUEUING. * LOGGEDIN - INCREMENTED IF CONNECTION MESSAGE. * MAXLOGGED - HIGH WATER MARK. * REMAINTTI - DECREMENTED IF COMMAND MESSAGE ARRIVED * AND REMAINTTI WAS NOT ALREADY ZERO. * SWAP QUEUE - UPDATED AS NEEDED. * TERMINAL INPUT BUFFERS - IF ALLOCATED, THEN LINKED TO * USER TABLE ENTRY OR TO EXISTING BUFFERS. * USER TABLE ENTRY - UPDATED AS NEEDED. * MEM[0] - SENSE SWITCH 6 SET AS NEEDED. * ENABLEFLAG - FORCED NEGATIVE IF NEARING FULL TABLE. * * CALLS MELT, INSTRMNT2, INSTRMNT1, ALLOC, MOVEWD, * SCHEDULE, PURGETTI, SYSTEM, RECALL, GETUSER, PUTINQ, * PUTUSER, MAX. * * USES IAFHEADER, CURUSER. # ITEM TMP1,TMP2,TMP3; SWITCH RCVFNCSW RCVINIT,RCVMSGIN,RCVOUTDONE,RCVBREAK, RCVHUNGUP,RCVCONNECT; ARRAY VALIDATE[0:5]; ITEM VLDTUSR B=[FALSE,4(TRUE),FALSE]; ARRAY REJECTION [0:1]; ITEM REJECTMSG; ITEM EJT; IF SSCINP[-1] NQ 0 THEN BEGIN STOPNOW=TRUE; SSCINP[-1]=0; ONSW(O"40"); # SIGNAL SUBSYSTEM TERMINATION # END IF SHUTDOWN GR 1 THEN RETURN; IAFHEADER=SSCINP[0]; IF IAFHEADER NQ 0 THEN # WE HAVE RECEIVED SOMETHING # BEGIN IF VLDTUSR[IAFFUNC] THEN BEGIN CURUSER=0; # SEARCH FOR USER AMONG RECENT TASK ASSIGNMENTS # FOR TMP1=1 STEP 1 UNTIL TASKSAVAIL DO BEGIN IF TASKUSER[TMP1] NQ 0 AND USERIAFNUM[TASKUSER[TMP1]] EQ IAFTERM THEN BEGIN CURUSER=TASKUSER[TMP1]; GOTO RCVFOUND; END END # SEARCH FOR USER BY BRUTE FORCE # FOR TMP1=1 STEP 1 UNTIL NUMSMFUSR DO BEGIN IF USERIAFNUM[TMP1] EQ IAFTERM THEN BEGIN CURUSER=TMP1; GOTO RCVFOUND; END END RCVFOUND: IF CURUSER LQ 0 OR CURUSER GR NUMSMFUSR THEN BEGIN SSCINP[0]=0; GOTO AFTERRCV; END GETUSER; END GOTO RCVFNCSW[IAFFUNC]; RCVMSGIN: CONTROL IFEQ TRACEFLAG,1; P=LOC(SSCINP[1]); TMP1=IAFLEN-1; CONTROL FI; TRCBOTH("MSGIN$",CURUSER); TRCWORDS(MOVFROM,TMP1); CONTROL IFGQ PARANOIA,2; IF NOT CURUSLOGGED THEN MELT("DOIN 2$"); IF IAFLEN GR SIZTTIBUF+1 THEN MELT("DOIN 22$"); CONTROL FI; IF IAFLEN LS 2 THEN BEGIN SSCINP[0]=0; GOTO AFTERRCV; END CONTROL IFEQ METERING,1; INSTRMNT2(INSTST"INPUT",IAFLEN); CONTROL FI; IF REMAINTTI LQ 0 THEN # TRY ANOTHER DAY # BEGIN CONTROL IFEQ METERING,1; INSTRMNT1(INSTST"NOTTI"); CONTROL FI; GOTO PASTRCV; END ALLOC(TTIBITS,TTIGOOD,TMP2,NUMTTIBIT); CONTROL IFGQ PARANOIA,2; IF TMP2 EQ 0 THEN MELT("DOIN 4$"); # MUST SUCCEED # CONTROL FI; REMAINTTI=REMAINTTI-1; # KEEP TRACK RESOURCE # P=LOC(SSCINP[1]); P=TTIBUFADDR[TMP2]; TMP3=IAFLEN-1; MOVEWD(TMP3,MOVFROM,MOVTO); TTIBUFLEN[TMP2]=TMP3; TTIBUFLNK[TMP2]=0; IF CURUSTTIBUF EQ 0 THEN # START NEW CHAIN # BEGIN CURUSTTIBUF=TMP2; CONTROL IFEQ METERING,1; TMP1=TIMEOFDAY-CURUSLASTIM; INSTRMNT2(INSTST"THNK",TMP1); CONTROL FI; END ELSE # ADD TO CHAIN # BEGIN TMP1=CURUSTTIBUF; WHYLE TMP1 NQ 0 DO BEGIN TMP3=TMP1; TMP1=TTIBUFLNK[TMP1]; END TTIBUFLNK[TMP3]=TMP2; END SCHEDULE; GOTO ENDRCV; RCVBREAK: TRCBOTH("BREAK$",CURUSER); CONTROL IFEQ METERING,1; INSTRMNT1(INSTST"BREAKS"); CONTROL FI; IF NOT CURUSLOGGED THEN GOTO ENDRCV; CURUSINTRPT=TRUE; CURUSCRUNCH=FALSE; # IMPROVE SCHEDULEING PRIORITY # PURGETTI; SCHEDULE; GOTO ENDRCV; RCVHUNGUP: TRCBOTH("HUNGUP$",CURUSER); CONTROL IFEQ METERING,1; INSTRMNT1(INSTST"HANGUPS"); CONTROL FI; CURUSDROP=TRUE; CURUSRCVRY=TRUE; GOTO RCVBREAK; RCVINIT: STOPNOW=TRUE; GOTO AFTERRCV; RCVOUTDONE: # TTY OUTPUT COMPLETE # TRCBOTH("TOCIN $",CURUSER); CONTROL IFGQ PARANOIA,3; IF NOT CURUSLOGGED THEN MELT("DOIN 43$"); CONTROL FI; SCHEDULE; GOTO ENDRCV; RCVCONNECT: TRCBOTH("CONNECT$",IAFTERM); IF IAFTERM GR MAXCONNECT THEN MELT("TABLE LIMIT$"); CURUSER=0; FOR TMP1=1 STEP 1 UNTIL NUMSMFUSR DO BEGIN IF USERIAFNUM[TMP1] EQ IAFTERM THEN CURUSER=TMP1; IF CURUSER EQ 0 THEN BEGIN IF NOT USERLOGGED[TMP1] THEN CURUSER=TMP1; END END TRCBOTH("PICK USER$",CURUSER); IF CURUSER EQ 0 THEN BEGIN # CRITICAL TABLE SPACE SHORTAGE - SEND LOGOUT FUNCTION TO # # IAF IMMEDIATELY AND SERIALIZE UNTIL THIS IS ACCOMPLISHED # EJT=B<48,12>SSCINP[1]; TRYREJECT: IAFFUNC=SMF2IAF"LOGOUT"; IAFLEN=1; IAFPTR=0; REJECTMSG[0]=0; REJECTMSG[1]=IAFHEADER; P=LOC(REJECTION); SSCTYPE=O"3"; SSCCOUNT=1; SYSREQ("SSC",0,LOC(SSCBUFFER),IAFSSID); IF SSCSTATUS LAN O"7776" EQ 4 THEN BEGIN RECALL(0); GOTO TRYREJECT; END EESET((TTEQ*4096)+EJT); GOTO AFTERRCV; END USERFLAGS[CURUSER]=0; GETUSER; CURUSLOGGED=TRUE; CURUSSTART=TRUE; CURUSIAFNUM=IAFTERM; USERJSN[CURUSER]=C<0,4>SSCINP[1]; USEREJT[CURUSER]=B<48,12>SSCINP[1]; LOGGEDIN=LOGGEDIN+1; IF LOGGEDIN GQ NUMSMFUSR-2 THEN ENABLEFLAG=-1; CONTROL IFEQ METERING,1; MAXLOGGED=MAX(MAXLOGGED,LOGGEDIN); CONTROL FI; PUTINQ; GOTO ENDRCV; ENDRCV: CURUSLASTIM=TIMEOFDAY; SSCINP[0]=0; PASTRCV: PUTUSER; AFTERRCV: END # OF BUFFER0 HANDLER # END # OF DOINPUT # PAGE # RUNJOBS -- TRY TO DO SOME USER CODE # PROC RUNJOBS; BEGIN # ** RUNJOBS - SUPERVISE TASK EXECUTION. * * RUNJOBS CONTROLS TASK EXECUTION. TASKS GET THE CPU ONLY * FROM RUNJOBS, AND ARE REQUIRED TO EXIT EACH TIME SLICE TO * THE APPROPRIATE LABEL WITHIN RUNJOBS. SINCE RUNJOBS IS THE * SOLE PATH FOR TASKS TO RECEIVE CONTROL, IT IS NOT NECESSARY * FOR RUNJOBS ITSELF TO USE REENTRANT ENTRY AND EXIT EVEN * THOUGH ITS SUBROUTINES ARE THEMSELVES REENTRANT. * * NOTE THAT THERE IS NO ARCHITECTURAL PROTECTION AGAINST * RENEGADE TASKS. IF A TASK LOOPS OR OVERWRITES MEMORY, THE * ENTIRE SUBSYSTEM WILL BE IMPACTED. THEREFORE, MULTI-USER * EDITING CODE MUST BE BUG-FREE. EDITING CODE MUST ALSO BE * SENSITIVE AND REASONABLE IN RESOURCE CONSUMPTION SINCE THIS * SUBSYSTEM EXECUTES AT A VERY HIGH PRIORITY. * * RUNJOBS PERFORMS TWO PRELIMINARY PHASES BEFORE INITIATING * TASKS. THE FIRST PRELIMINARY PHASE ACCOUNTS FOR THE LEVEL * OF UTILIZATION, BOTH FOR STATISTICAL REASONS AND TO ENABLE * SCHEDULING RULES TO "TRY HARDER". THE SECOND PRELIMINARY * PHASE CHECKS FOR SHUTDOWN CONDITIONS. * * THE PRINCIPAL FUNCTION OF RUNJOBS IS TO ADVANCE THE STATUS * OF EACH AVAILABLE TASK. WHILE THE SUBSYSTEM IS COMPILED * WITH A LARGE NUMBER OF TASKS IN EXISTENCE, THE NUMBER OF * TASKS AVAILABLE FOR EXECUTION IS DETERMINED AT SUBSYSTEM * STARTUP AND IS TYPICALLY MUCH SMALLER. * * EACH TASK CAN HAVE ANY OF THE FOLLOWING STATUSES -- * * 1. IT MAY BE EMPTY. AN EMPTY TASK WILL BE ASSIGNED TO A * USER IF THERE ARE USERS IN THE SCHEDULING (SWAPIN) QUEUE * AND NO SHUTDOWN CONDITION EXISTS. IN ASSIGNING A USER TO A * PREVIOUSLY EMPTY TASK, THE TASK STATUS IS CHANGED TO * "START" OR "SWAPIN" BASED ON THE USER'S PROGRESS. * * 2. IT MAY BE DELAYING. A DELAYING TASK MISSES THE CPU FOR * PRECISELY ONE SMFEX CPU RECALL CYCLE, AND TYPICALLY IS * WASTING REAL TIME WHILE POLLING ON COMPLETION OF AN I/O * FUNCTION. IN VIEW OF THE BRUTE FORCE REQUIRED TO RESTART A * DELAYED TASK, THIS STYLE OF WAITING FOR I/O IS NOT USED * OFTEN WITHIN THE EDITING CODE, BUT IT IS FULLY SUPPORTED BY * THIS EXECUTIVE. RUNJOBS RESTARTS A DELAYED TASK AS SOON AS * IT IS DISCOVERED, BY CHANGING THE TASK STATUS TO "RUN". * * 3. A TASK MAY BE IN AUTO RECALL. THIS TASK WISHES TO MISS * THE CPU UNTIL SMFEX DETECTS THAT THE SPECIFIED COMPLETION * BIT HAS BEEN TOGGLED BY ANYBODY. THIS IS THE METHOD * GENERALLY USED WITHIN THE EDITOR TO WAIT FOR I/O, SINCE IT * AVOIDS WASTING CPU TIME TO START UP A DELAYED TASK WHICH IS * LIKELY TO DROP RIGHT BACK INTO DELAY. ONCE SMFEX VERIFIES * THAT THE DESIRED EVENT IS COMPLETE, IT RESTARTS A RECALLED * TASK BY CHANGING THE TASK STATUS TO "RUN". * * 4. A TASK MAY BE IN "START" STATUS. THIS TASK IS READY * FOR THE CPU, FOR A USER WHO HAS NOT YET EXECUTED ANY * MULTI-USER EDITING CODE. THE TASK WILL GET THE CPU AT THE * EDITOR'S MAIN ENTRY POINT. NO SWAP PAGE EXISTS YET, AND * DISPATCHING DATA IS IN MODEL FORMAT. * * 5. A TASK MAY BE IN "SWAPIN" STATUS. THIS TASK HAS * PREVIOUSLY EXECUTED AND SWAPPED OUT. THE TASK THEREFORE * WANTS THE CPU AT THE ENTRY POINT OF THE SWAPPING CODE. * SINCE THE SWAPOUT AND SWAPIN CODE ARE ONE AND THE SAME, * TASK CPU DISPATCHING IS EFFECTED BY RETURNING TO THE SWAP * CODE WHERE CONTROL WAS PREVIOUSLY SURRENDERED. THAT SWAP * CODE WILL READ IN THE SWAP CODE AND ALTER THE MEMORY IMAGE * FOR THE TASK ON THE FLY, THEN RETURN OUT THE THE EDITING * CODE WHICH HAD CAUSED THE PREVIOUS SWAPOUT. * * 6. A TASK MAY BE IN "RUN" STATUS. THIS TASK HAS BEEN * SWAPPED IN FOR SOME TIME, AND IS PRESENTLY COMPLETING A * DELAY OR RECALL. SINCE THE MEMORY IMAGE FOR THE TASK IS * INTACT, WE ARE REQUIRED ONLY TO MANIPULATE THOSE ARRAYS * CRITICAL TO TASK DISPATCHING, AND RETURN TO THE POINT IN * THE EDITING CODE WHERE THE TASK HAD PREVIOUSLY PAUSED. * * EACH TIME A TASK GETS THE CPU, IT CAN FINISH ITS TIME SLICE * IT THREE WAYS -- (1) THE ENTIRE EDIT SESSION MAY END, IN * WHICH CASE THE TASK EXITS VIA LABEL "AFTEREDIT" FOR CLEANUP * PROCESSING, OR (2) THE TASK MAY COMPLETE A TRANSACTION, * I.E., IT MAY FINISH SWAPPING OUT FOR TERMINAL I/O OR * RESOURCE BALANCING, IN WHICH CASE IT EXITS VIA LABEL * "AFTERTRANS", OR (3) IT MAY NEED TO DELAY/RECALL WITHIN ITS * MEMORY IMAGE (I.E., WHILE STILL SWAPPED IN) FOR SOME DISK * I/O OR OTHER SHORT-TERM EVENT, IN WHICH CASE IT EXITS VIA * LABEL "AFTERSLICE". * * IN SUMMARY, THERE ARE THREE WAYS FOR A TASK TO BE ASSIGNED * THE CPU (START OF SESSION, START OF TRANSACTION, OR RESTART * AFTER DELAY) AND THREE WAYS FOR A TASK TO FORFEIT THE CPU * (END OF SESSION, END OF TRANSACTION, DELAY), AND THESE * ENTRIES AND EXITS CAN BE MIXED IN ANY FASHION. AS A * RESULT, RUNJOBS MUST VIOLATE A RULE OF CLEAN PROGRAMMING * PRACTISE, IN ALLOWING JUMPS INTO BLOCKS OF CODE (IF-THEN, * NOT LOOPS) WHICH ARE NOT NECESSARILY ACTIVE. * * * ENTRY SHUTDOWN - CONTROLS WHETHER TO EXECUTE AT ALL. * SWPQHEAD - START OF SWAPIN QUEUE. * TASKSAVAIL - NUMBER OF TASKS WE CAN USE. * TASK AND USER TABLES - SETUP. * MODELPTR, MODELLEN - MODEL FORMAT DISPATCH DATA. * TIMEOFDAY - MILLISECOND CLOCK. * LOGGEDIN - NUMBER OF USERS. * * EXIT TASKSBUSY - NUMBER OF NON-EMPTY TASKS. * COMPLETE - FORCED TRUE BASED ON SHUTDOWN. * LOGGEDIN - DECREMENTED FOR ANY SESSION COMPLETIONS. * ENABLEFLAG - NON-ZERO POSITIVE IF LOGGEDIN * DECREMENTS OUT OF NEAR-FULL CONDITION. * USER AND TASK TABLES - UPDATED AS NEEDED. * * CALLS INSTRMNT1, GETUSER, TAKEOFFQ, PUTUSER, GETCOMMON, * MOVEWD, GENWRKNAM, EDTINIT, PROCESS, EDTTERM, * MELT, ABTKILL, ABTPAUSE, RTRNWRKFIL, SENDLOGOUT, * KILL, TRANSACT, PUTINQ, SPREAD, GATHER. * * USES CURUSER, CURTASK, RSTKPTR, RENTSTK. * * NOTE WHENEVER RUNJOBS INVOKES A TASK, BY CALLING * PROCESS, TRANSACT, OR RESUME, THE FOLLOWING * CONVENTIONS APPLY FOR PARAMETERS PASSED TO AND * FROM THE EDITOR CODE -- * * PASSED CURTASK, CURUSER, RSTKPTR, RENTSTK, * WORKNAM, P. * * RETURNED ABORTED, RSTKPTR, RENTSTK, * CHNGSTATUS, TASKRCLADR[CURTASK]. # ITEM TMP1,TMP2; XDEF LABEL AFTEREDIT; XDEF LABEL AFTERTRANS; XDEF LABEL AFTERSLICE; XREF LABEL RESUME; TASKSBUSY=0; # COUNT UP TASK UTILIZATION # FOR TMP1=1 STEP 1 UNTIL NUMTASKS DO IF TASKSTATUS[TMP1] NQ TASKST"EMPTY" THEN TASKSBUSY=TASKSBUSY+1; CONTROL IFEQ METERING,1; IF TASKSBUSY GQ TASKSAVAIL AND SWPQHEAD NQ 0 THEN INSTRMNT1(INSTST"NOTASK"); CONTROL FI; IF SHUTDOWN GR 1 THEN # SEE IF SINUSES CLEARED # BEGIN IF TASKSBUSY EQ 0 THEN # ALL ACTIVITY CEASED # BEGIN IF (SHUTDOWN GQ 2 AND SWPQHEAD EQ 0) OR SHUTDOWN EQ 4 THEN COMPLETE=TRUE; END END IF SHUTDOWN EQ 1 AND LOGGEDIN EQ 0 THEN COMPLETE=TRUE; FOR CURTASK=1 STEP 1 UNTIL TASKSAVAIL DO BEGIN IF SWPQHEAD NQ 0 AND SHUTDOWN LQ 3 AND TASKSTATUS[CURTASK] EQ TASKST"EMPTY" THEN BEGIN CURUSER=SWPQHEAD; WHYLE CURUSER NQ 0 AND USERTTIBUF[CURUSER] EQ 0 DO CURUSER=USERQUELNK[CURUSER]; IF CURUSER EQ 0 THEN BEGIN CURUSER=SWPQHEAD; WHYLE CURUSER NQ 0 AND USERCRUNCH[CURUSER] DO CURUSER=USERQUELNK[CURUSER]; IF CURUSER EQ 0 THEN CURUSER=SWPQHEAD; END TRCBOTH("ASGN SLT$",CURTASK); TRCDEC(CURUSER); GETUSER; CURUSCRUNCH=FALSE; CURUSINTASK=TRUE; CURUSTASK=CURTASK; TAKEOFFQ; TASKUSER[CURTASK]=CURUSER; TASKFLAGS[CURTASK]=0; # CLEAR ALL # TASKNXTTTO[CURTASK]=0; TASKTYPTTO[CURTASK]=0; IF CURUSSTART THEN TASKSTATUS[CURTASK]=TASKST"START"; ELSE TASKSTATUS[CURTASK]=TASKST"SWAP"; PUTUSER; END IF TASKSTATUS[CURTASK] EQ TASKST"EMPTY" THEN TEST CURTASK; IF TASKSTATUS[CURTASK] EQ TASKST"DELAY" THEN BEGIN TASKSTATUS[CURTASK]=TASKST"RUN"; END IF TASKSTATUS[CURTASK] EQ TASKST"RECALLFET" THEN BEGIN IF B<59,1>MEM[TASKRCLADR[CURTASK]] EQ 1 THEN BEGIN TASKSTATUS[CURTASK]=TASKST"RUN"; END END IF TASKSTATUS[CURTASK] EQ TASKST"START" THEN BEGIN CURUSER=TASKUSER[CURTASK]; TRCBOTH("TASK STRT$",CURTASK); TRCDEC(CURUSER); GETUSER; CONTROL IFEQ METERING,1; INSTRMNT1(INSTST"SESSION"); CONTROL FI; CURUSSTART=FALSE; RSTKPTR=1; # MAKE A NEW STACK # GETCOMMON; # SET ARRAY BASES # P=MODELPTR; MOVEWD(MODELLEN,MOVFROM,DATAEND); # VIRGIN EDIT DATA # GENWRKNAM; TASKTIME[CURTASK]=TIMEOFDAY; # SINCE NO SWAPIN # IF NOT CURUSRCVRY THEN BEGIN EDTINIT; PROCESS; EDTTERM; END AFTEREDIT: # CONTROL FORCE HERE IF RCVRY # CONTROL IFEQ METERING,1; INSTRMNT1(INSTST"ENDEDT"); CONTROL FI; CURUSDONE=TRUE; CONTROL IFEQ TRACEFLAG,1; IF ABORTED THEN TRACESTR("ABORTEDIT$"); ELSE TRACESTR("ENDEDIT$"); CONTROL FI; CONTROL IFEQ MELTONABT,1; IF ABORTED THEN MELT("SUBTASK ABORT$"); CONTROL FI; RSTKPTR=1; IF ABORTED THEN BEGIN IF TASKABORT[CURTASK] THEN ABTKILL; ELSE BEGIN TASKABORT[CURTASK]=TRUE; ABTPAUSE; TASKABORT[CURTASK]=FALSE; END END RTRNWRKFIL; # RETURN THE WORK FILE # SENDLOGOUT; # TRANSMIT LOGOUT FUNCTION # LOGGEDIN=LOGGEDIN-1; IF LOGGEDIN EQ NUMSMFUSR-5 THEN ENABLEFLAG=1; TASKSTATUS[CURTASK]=TASKST"EMPTY"; LASTTASK=0; KILL; END IF TASKSTATUS[CURTASK] EQ TASKST"SWAP" THEN BEGIN CURUSER=TASKUSER[CURTASK]; TRCBOTH("TASK TRNS$",CURTASK); TRCDEC(CURUSER); GETUSER; RSTKPTR=CURUSSTKPTR ; # HE REMEMBERS HIS PLACE # GETCOMMON; FETFNT=CURUSFNTPTR; GENWRKNAM; TRANSACT; # RUNS AS SUBROUTINE OF USER # AFTERTRANS: # CONTROL COMES HERE MANUALLY FROM TRANSACT/ENDTRANS # CURUSSTKPTR=RSTKPTR; CURUSFNTPTR=FETFNT; CURUSINTASK=FALSE; IF TASKREQUE[CURTASK] THEN PUTINQ; TASKSTATUS[CURTASK]=TASKST"EMPTY"; LASTTASK=0; PUTUSER; END IF TASKSTATUS[CURTASK] EQ TASKST"RUN" THEN BEGIN CURUSER=TASKUSER[CURTASK]; TRCBOTH("TASK RUN$",CURTASK); TRCDEC(CURUSER); GETUSER; IF SHUTDOWN EQ 4 THEN # SELF DESTRUCT # BEGIN TASKSTATUS[CURTASK]=TASKST"EMPTY"; RETURN; END RSTKPTR=TASKSTKPTR[CURTASK]; # HE REMEMBERS PLACE # GETCOMMON; MOVEWD(RSTKPTR-1,DISPRSTK,RENTSTK); SPREAD(RSTKPTR,RENTSTK); IF CURUSINTRPT THEN USRBRK=1; IF SHUTDOWN GR 2 THEN BEGIN USRBRK=1; FORCENULL=TRUE; END GENWRKNAM; GOTO RESUME; # TO RESUME JOB WHERE IT WAS # AFTERSLICE: # CONTROL MANUALLY SENT HERE BY PAUSE # GATHER(RSTKPTR,RENTSTK); # SAVE ALL SUBROUTINES # MOVEWD(RSTKPTR-1,RENTSTK,DISPRSTK); TASKSTATUS[CURTASK]=CHNGSTATUS; TASKSTKPTR[CURTASK]=RSTKPTR; PUTUSER; END END # OF POLLING LOOP # END # OF RUNJOBS # PAGE # INSTRUMENT -- COLLECT STATS # CONTROL IFEQ METERING,1; PROC INSTRUMENT(TYPE,DATA,DATA2); BEGIN # ** INSTRUMENT - TAKE STATISTICS. * * INSTRUMENT, TOGETHER WITH ENTRY POINTS INSTRMNT1, * INSTRMNT2, AND INSTRMNT3, COLLECTS STATISTICS REGARDING * INTERNAL PERFORMANCE AND WORKLOAD CHARACTERISTICS. * * ENTRY TYPE - SELECTS STATISTICAL CASE. * DATA - ONE DATA ITEM, WHERE APPLICABLE. * DATA2 - ADDITIONAL DATA WHERE REQUIRED. * MAXLOGGED, LOGGEDIN, TIMEOFDAY, SLICECOUNT, * BUILDCIO, ACCTTRANS, PALAST[0] - ADDITIONAL * PARAMETERS FOR CERTAIN FUNCTION TYPES. * * EXIT STATWORD[ALL] - INCREMENTED OR SET. * DISTRSTM, DISTTKTM, DISTCICT, DISTTRCT, DISTFLSZ, * DISTMGSZ - FREQUENCY DISTRIBUTIONS. * * CALLS ADDVECT. # ITEM DATA,DATA2,TYPE; ENTRY PROC INSTRMNT1(TYPE); ENTRY PROC INSTRMNT2(TYPE,DATA); ENTRY PROC INSTRMNT3(TYPE,DATA,DATA2); SWITCH INSTSW INSTTHNK,INSTRSPNS,INSTNOTTI, INSTNOTASK,INSTSWPE,INSTSWPD,INSTCLOCK, INSTPREEMPT,INSTSWPTIM,INSTEDTCIO, INSTSESSION,INSTTRANS,INSTXSTTO, INSTBREAK,INSTHANG,INSTBGNEDT, INSTENDEDT,INSTINPUT,INSTXMIT, INSTKWIK,INSTUTIL,INSTWORK,INSTDROP; GOTO INSTSW[TYPE]; INSTNOTTI: STATWORD[1]=STATWORD[1]+1; RETURN; INSTNOTASK: STATWORD[2]=STATWORD[2]+1; RETURN; INSTSESSION: STATWORD[3]=STATWORD[3]+1; RETURN; INSTTRANS: STATWORD[4]=STATWORD[4]+1; RETURN; INSTRSPNS: STATWORD[5]=STATWORD[5]+DATA; ADDVECT(DISTRSTM,DATA/16); RETURN; INSTTHNK: STATWORD[6]=STATWORD[6]+DATA; ADDVECT(DISTTKTM,DATA/1024); RETURN; INSTPREEMPT: # WORDS 7, 8, 9 # STATWORD[6+DATA]=STATWORD[6+DATA]+1; RETURN; INSTXSTTO: STATWORD[10]=STATWORD[10]+1; RETURN; INSTBREAK: STATWORD[11]=STATWORD[11]+1; RETURN; INSTHANG: STATWORD[12]=STATWORD[12]+1; RETURN; INSTCLOCK: STATWORD[13]=MAXLOGGED; STATWORD[14]=LOGGEDIN; STATWORD[15]=TIMEOFDAY; STATWORD[16]=SLICECOUNT; RETURN; INSTSWPE: STATWORD[17]=STATWORD[17]+1; RETURN; INSTSWPD: STATWORD[18]=STATWORD[18]+1; RETURN; INSTSWPTIM: STATWORD[19]=STATWORD[19]+DATA; RETURN; INSTEDTCIO: STATWORD[20]=STATWORD[20]+DATA; # CIO MSECS # STATWORD[21]=STATWORD[21]+DATA2; # CIO COUNT # IF DATA2 EQ 0 THEN STATWORD[22]=STATWORD[22]+1; # NUM ZERO CIO # IF DATA2 GQ 16 THEN BEGIN STATWORD[23]=STATWORD[23]+1; # NUM CRUNCHERS # STATWORD[24]=STATWORD[24]+DATA2; # CRUNCHY EFFORT # END ADDVECT(DISTCICT,DATA2); RETURN; INSTBGNEDT: STATWORD[25]=STATWORD[25]+BUILDCIO; # FILEBUILD CIOS # RETURN; INSTENDEDT: ADDVECT(DISTTRCT,ACCTTRANS); ADDVECT(DISTFLSZ,PALAST[0]); RETURN; INSTINPUT: STATWORD[26]=STATWORD[26]+DATA; # NUM WORDS # RETURN; INSTXMIT: STATWORD[27]=STATWORD[27]+DATA; # NUM WORDS # STATWORD[28]=STATWORD[28]+DATA2; # NUM BLOCKS # ADDVECT(DISTMGSZ,DATA); RETURN; INSTKWIK: STATWORD[29]=STATWORD[29]+1; RETURN; INSTUTIL: STATWORD[30]=STATWORD[30]+DATA; RETURN; INSTWORK: STATWORD[31+DATA]=STATWORD[31+DATA]+1; # WORDS 31 THRU 41 # RETURN; INSTDROP: STATWORD[42]=STATWORD[42]+1; RETURN; END # OF INSTRUMENT # CONTROL FI; PAGE # USER-STATE CODE # # BASIC WAIT/INTERRUPT HOOKS # PROC SMFDLY; IOBEGIN(SMFDLY) # ** SMFDLY - INTERFACE FOR TASKS TO DELAY. * * EDITING CODE USE COMPILE-TIME MACROS TO REDEFINE THE * KEYWORD "DELAY" INTO A CALL TO "SMFDLY" FOR MULTI-USER * CODE. SMFDLY INTERFACES TO THE PAUSE ROUTINE, WHICH IN * TURN WORKS WITH RUNJOBS TO CONTROL THE TASK. * * ENTRY NO CONDITIONS. * * EXIT AFTER DELAYING. * * CALLS PAUSE. * * USES CHNGSTATUS. # CHNGSTATUS=TASKST"DELAY"; PAUSE; IOEND # OF SMFDLY # PROC SMFRCL(AFET); IOBEGIN(SMFRCL) # ** SMFRCL - INTERFACE FOR TASKS TO AUTO-RECALL ON AN EVENT. * * EDITING CODE USE COMPILE-TIME MACROS TO REDEFINE THE * KEYWORD "RECALL" INTO A CALL TO "SMFRCL" FOR MULTI-USER * CODE. SMFDLY INTERFACES TO THE PAUSE ROUTINE, WHICH IN * TURN WORKS WITH RUNJOBS TO CONTROL THE TASK. * * NOTE THAT EDITING CODE IS NOT ALLOWED TO SPECIFY AUTO-RECALL * ON ANY RA+1 CALL. INSTEAD, EDITING CODE MUST ISSUE * RA+1 AND THEN CALL THIS ROUTINE TO RECALL UNTIL THE SYSTEM * FUNCTION IS COMPLETE. * * ENTRY AFET - COMPLETION WORD. * * EXIT AFTER COMPLETION BIT IS FOUND TO BE ON. * * CALLS PAUSE. * * USES CHNGSTATUS, TASKRCLADR[CURTASK]. # ARRAY AFET;; TASKRCLADR[CURTASK]=LOC(AFET); CHNGSTATUS=TASKST"RECALLFET"; PAUSE; IOEND # OF SMFRCL # PROC CLEARINT; IOBEGIN(CLEARINT) # ** CLEARINT - ACKNOWLEGE USER BREAK. * * EDITING CODE IS REQUIRED TO CALL THIS ROUTINE AS SOON AS * A USER BREAK HAS BEEN NOTICED, AND ALL EDITOR-SPECIFIC * RESPONSES HAVE BEEN PERFORMED. CLEARINT CLEARS FLAGS * INDICATING UNACKNOWLEGED INTERRUPT, SINCE ONLY EXECUTIVE * CODE CAN ACCESS SUCH FLAGS. * * ENTRY NO CONDITIONS. * * EXIT CURUSINTRPT - FALSE. * BUFFERED OUTPUT PURGED. * TASKREQUE - FALSE UNLESS TASK RECOVERY IN EFFECT. * * CALLS CLEARQUE. # IF CURUSINTRPT THEN TASKNXTTTO[CURTASK]=0; CLEARQUE; CURUSINTRPT=FALSE; IOEND # OF CLEARINT # PROC CLEARQUE; BEGIN # ** CLEARQUE - ATTEMPT TO CLEAR TASKREQUE FLAG. * * CLEARQUE CLEARS THE TASKREQUE FLAG UNLESS TASK RECOVERY IS * IN EFFECT. TASKREQUE IS SET BY DOINPUT WHEN A USER NEEDS * TO BE SCEDULED TO A TASK BUT IS ALREADY IN A TASK. DOINPUT * HAS NO WAY TO KNOW IF THE TASK ALREADY RUNNING WILL DETECT * OR IGNORE THE INPUT EVENT. THUS, THE TASKREQUE FLAG IS * SET. IF THE EDITING CODE NEVER USES CLEARQUE TO SHOW * ACKNOWLEGEMENT, THEN WHEN THE TASK SWAPS OUT THE RUNJOBS * ROUTINE WILL IMMEDIATELY PUT THE USER BACK IN THE SWAPIN * QUEUE. IF THE EDITING CODE ACKNOWLEGES THE INPUT EVENT, * WHICH IS USUALLY A USER BREAK, THEN CLEARQUE GETS RID OF * THE RESCHEDULING AT END OF TRANSACTION. * * THE TASKREQUE FLAG IS PRESERVED IF RECOVERY IS ON. * RECOVERY DEALS WITH JOB DETACHMENT AND SUBSYSTEM EXITS. * * ENTRY CURUSRCVRY - WHETHER RECOVERY IN EFFECT. * TASKREQUE[CURTASK] - WHETHER RESCHEDULING PLANNED. * * EXIT TASKREQUE[CURTASK] - UPDATED. # TASKREQUE[CURTASK]=TASKREQUE[CURTASK] AND CURUSRCVRY; END # OF CLEARQUE # PROC SPINWHEELS; IOBEGIN(SPINWHEELS) # ** SPINWHEELS - DELAY TASK UNTIL INPUT ARRIVES. * * SPINWHEELS ALLOWS A TASK TO HOLD ONTO ITS RESOURCES FOR * BRIEF TIME INTERVALS IN EXPECTATION OF A USEFUL EXTERNAL * ARRIVING PROBABLY WITHIN A FRACTION OF A SECOND. IF * RESOURCES ARE TIGHT WE TERMINATE THE WAIT SO THE CALLER CAN * SWAP OUT. * * ENTRY TIMEOFDAY - CURRENT TIME. * TASKREQUE[CURTASK], TASKTIME[CURTASK], REMAINTTI, * TASKAVAIL, TASKSBUSY - EVENT/RESOURCE DATA. * SPINTIME - TIME ALREADY SPENT SPINNING. * * EXIT TIMEOFDAY - CURRENT TIME MAY HAVE ADVANCED. * SPINTIME - POSSIBLY INCREMENTED. * * CALLS OKTOWAIT(INTERNAL), SMFRCL. * * USES SPINSTART, TASKPULSE[CURTASK]. # FUNC OKTOWAIT B; BEGIN # ** OKTOWAIT - COMPUTE RULES FOR SPINWHEELS. * * OKTOWAIT DETERMINES THE EVENT/RESOURCE THRESHOLDS FOR THE * SPINWHEELS ROUTINES. A TASK MAY SPIN ITS WHEELS, HOLDING * RESOURCES, UNTIL/UNLESS ANY OF THE FOLLOWING -- * * 1. THE DESIRED INPUT ARRIVES FROM IAFEX (TASKREQUE) * * 2. TWO SECONDS EXPIRE. * * 3. ONE-HALF SECOND EXPIRES AND THERE ARE MORE USERS * SCHEDULED FOR TASKS THAN TASKS LEFT TO TAKE THEM. * * 4. REGARDLESS OF TIMING, LESS THAN HALF OF THE TERMINAL * INPUT BUFFERS ARE LEFT OVER. (MOST CRITICAL RESOURCE) * * ENTRY AND EXIT - SEE OUTER PROCEDURE. # OKTOWAIT=TRUE; IF TASKREQUE[CURTASK] OR TIMEOFDAY-TASKTIME[CURTASK] GR 2000 THEN OKTOWAIT=FALSE; IF NUMTTIBUF-REMAINTTI GR TASKSAVAIL-TASKSBUSY AND TIMEOFDAY-TASKTIME[CURTASK] GR 500 THEN OKTOWAIT=FALSE; IF REMAINTTI LQ NUMTTIBUF/2 THEN OKTOWAIT=FALSE; END # SPINWHEELS MAIN CODE STARTS HERE # SPINSTART=TIMEOFDAY; WHYLE OKTOWAIT DO BEGIN TASKPULSE[CURTASK]=2; # SIMULATE BUSY FET # P=LOC(TASKPULSE[CURTASK]); SMFRCL(MOVTO); # APPROX 05-HZ DELAY LOOP # END SPINTIME=SPINTIME+TIMEOFDAY-SPINSTART; IOEND # OF SPINWHEELS # PAGE # RELIABILITY/METERING HOOKS # PROC FATALTRAP; IOBEGIN(FATALTRAP) # ** FATALTRAP - INTERFACE FOR FAILING TASKS. * * FATALTRAP IS CALLED BY EDITING CODE WHICH DETECTS ITS OWN * UNCORRECTABLE ALGORITHMIC INCONSISTENCY. THIS INTERFACE * WAITS FOR ANY CIO CALL TO FINISH THEN BRANCHES DIRECTLY * TO THE RUNJOBS EXECUTIVE. * * ENTRY FET - MAYBE ACTIVE. * * EXIT FET - COMPLETE. * VIA AFTEREDIT LABEL. * * CALLS SMFRCL, AFTEREDIT. # XREF LABEL AFTEREDIT; SMFRCL(FET); # WAIT IO DONE # GOTO AFTEREDIT; # TERMINATE TASK # END # OF FATALTRAP # PROC ABTPAUSE; IOBEGIN(ABTPAUSE) # ** ABTPAUSE - TRY TO CHECKPOINT WORKFILE AFTER ABORT. * * ABTPAUSE IS CALLED AS EDITING CODE BY THE RUNJOBS EXECUTIVE * WHEN A TASK HAS ABORTED ITSELF. THE INTENT IS TO ATTEMPT * TO CHECKPOINT THE WORKFILE BEFORE TRANSITIONING BACK TO * SINGLE-USER. IF THIS CAN BE DONE, THEN THE SINGLE EDITOR * WILL DETECT THE ABORTED FLAG IN THE DATA SEGMENT OF THE * WORKFILE AND WILL KNOW THAT IT CANNOT PROCEED. ABTPAUSE * MAY NOT SUCCEED, AND RUNJOBS IS RESPONSIBLE TO KEEP TRACK * OF ABTPAUSE FAILURE AND INSTEAD CALL ABTKILL. * * CALLS PAUSEIO. # PAUSEIO; IOEND # OF ABTPAUSE # PROC ABTKILL; IOBEGIN(ABTKILL) # ** ABTKILL - KILL OFF TASK AFTER DOUBLE ABORT. * * THE RUNJOBS EXECUTIVE CALLS ABTKILL AS A LAST RESORT WHEN A * TASK HAS DELIBERATELY ABORTED ITSELF, THEN HAS ABORTED * ITSELF A SECOND TIME WHEN ABTPAUSE ATTEMPTED TO CHECKPOINT * THE WORKFILE. ABTKILL ATTEMPTS TO ZAP THE WORKFILE TO * CONTAIN EXACTLY ONE WORD OF ZEROES, WHICH WOULD NOT BE * ACCEPTED AS A VALID TRANSITION ONCE THE SINGLE-USER EDITOR * GETS BACK IN CONTROL. THE INTENT IS TO BE AS CERTAIN AS * POSSIBLE THAT THE SINGLE EDITOR WILL NOT PICK UP AND * CONTINUE A HOPELESS EDIT SESSION. * * CALLS SMFRCL, WRITEO, REWRITR, SMFRCL. * * USES FET. # SMFRCL(FET); FETRR=1; WRITEO(FET,0); REWRITR(FET,0); SMFRCL(FET); IOEND # OF ABTKILL # CONTROL IFEQ METERING,1; PROC BGNMETER; BEGIN # ** BGNMETER - INSTRUMENT BEGINNING OF EDIT SESSION. * * BGNMETER ALLOWS THE SINGLE-USER EDITOR TO PASS DATA FROM * ITS FILE-BUILD PROCESS, INTO THE CORRESPONDING TASK IN * THE MULTI-USER EDITOR, WHICH CAN USE THIS INTERFACE TO * GLOBALLY RECORD THE DATA. * * CALLS INSTRMNT1. # INSTRMNT1(INSTST"BGNEDT"); END PROC WIOSTAT(ORDINAL); BEGIN # ** WIOSTAT - WORKIO STATISTICS INTERFACE. * * WIOSTAT IS CALLED BY THE WORKFILE MANAGER AT NOTEWORTHY * EVENTS SO THAT STATISTICS CAN BE INCREMENTED. * * ENTRY ORDINAL - WHICH ACCUMULATOR TO INCREMENT. * * CALLS INSTRMNT2. # ITEM ORDINAL; INSTRMNT2(INSTST"WORKIO",ORDINAL); END CONTROL FI; PAGE # FNT MANAGEMENT # PROC RTRNWRKFIL; IOBEGIN(RTRNWRKFIL) # ** RTRNWRKFIL - RETURN THE WORKFILE FNT. * * BEFORE SMFEX CAN TRANSITION A USER BACK TO THE SINGLE * USER EDITOR, IT MUST GET RID OF ITS LOCAL FNT FOR THE * USERS WORKFILE. RTRNWRKFIL DOES THIS AND IS * DESIGNED TO BE CALLED BY RUNJOBS IN USER STATE JUST AFTER * THE TASK HAS TERMINATED AT THE AFTEREDIT LABEL. * * ENTRY FET - SETUP. * * EXIT CURUSDONE - TRUE. * * CALLS RETERN, SMFRCL. # RETERN(FET,0); CURUSDONE=TRUE; # ASSURE NO RECOVERY # SMFRCL(FET); IOEND # OF RTRNWRKFIL # PAGE # LOGIN/LOGOUT CONTROL # PROC SENDLOGOUT; IOBEGIN(SENDLOGOUT) # ** SENDLOGOUT - TRANSMIT LAST OUTPUT AND ROLLIN SINGLE. * * SENDLOGOUT PERFORMS THE LAST PROCESSING WHEN A TASK * COMPLETES ALL MULTI-USER PROCESSING. SENDLOGOUT SHOULD * BE CALLED ONLY BY RUNJOBS AFTER THE TASK HAS EXITED * AT LABEL AFTEREDIT AND AFTER THE WORKFILE LOCAL FNT HAS * BEEN RETURNED. SENDLOGOUT ASSURES THAT ANY OUTPUT LEFT * IN OUR BUFFERS IS SEND ON TO IAF, THEN TELLS IAF THE * USER IS LEAVING MULTI-USER MODE, THEN ISSUES AN EESET * FUNCTION TO CAUSE ROLLIN OF THE SINGLE USER JOB. * * THE TLX FUNCTION USED BY THE SINGLE USER JOB TO ROLLOUT * WILL BE RECALLED TO VERIFY THAT SMFEX HAS PROPERLY * DISPENSED WITH THE WORKFILE. TLX THEN CONFORMS TO IAF * THAT THE MULTI-USER SESISON IS OVER. * * ENTRY TASKNXTTTO[CURTASK] - INDICATES RESIDUAL OUTPUT. * * EXIT TASKNXTTTO[CURTASK] - ZERO. * * CALLS TTOTRAP, TRANSMIT, EESET. * * USES TTOADDR, TTOLEN, TTOTYPE. # IF TASKNXTTTO[CURTASK] NQ 0 THEN BEGIN TTOADDR=0; TTOLEN=0; TTOTYPE=TTOST"FORCE"; TTOTRAP; END TASKTYPTTO[CURTASK]=SMF2IAF"LOGOUT"; TRANSMIT; EESET((TTEQ*4096)+USEREJT[CURUSER]); IOEND # OF SENDLOGOUT # PAGE # TRANSMIT TO IAFEX # PROC TRANSMIT; IOBEGIN(TRANSMIT) # ** TRANSMIT - TRANSMIT A FUNCTION CODE/MESSAGE TO IAFEX. * * TRANSMIT IS USED FOR THE ISSUANCE OF ALL CODES TO IAFEX * EXCEPT FOR THE SPECIAL CASE WHERE DOINPUT NEEDS TO * DISCONNECT A TERMINAL IN SERIALIZED OPERATION FOR * OVERFLOWED TABLES. TRANSMIT EXECUTES IN USER MODE, THUS * THE EXECUTIVE HAS NO WAY TO SEND MESSAGES TO IAFEX EXCEPT * FOR THE CASE ALREADY MENTIONED. * * EACH TASK HAS ITS OWN OUTPUT BUFFER WHICH IS USED TO * ACCUMULATE AND BATCH OUTPUT TEXT TOGETHER TO MINIMIZE CALLS * TO TRANSMIT. ONCE TRANSMIT IS CALLED, IT USES THE SCP/UCP * FACILITY TO SEND THE TASK'S BUFFER TO IAF. IF THE SCP/UCP * FUNCTIONS INDICATE THAT THE MESSAGE COULD NOT BE SENT TO * IAF ON THIS RECALL CYCLE, THEN THE TASK IS DELAYED * (ALLOWING OTHER TASKS TO EXECUTE IN PARALLEL) AND THE * SCP/UCP OPERATION WILL BE TRIED AGAIN. * * ENTRY TASKTYPTTO[CURTASK] - FUNCTION CODE FOR IAFEX. * TASKNXTTO[CURTASK] - AMOUNT OF TEXT TO GO WITH * FUNCTION CODE. * CURUSIAFNUM - IAFEX TERMINAL NUMBER. * TTOWORDS, TTOBLOCKS - STATISTICAL ACCUMULATORS. * CURUSLASTIM, TIMEOFDAY - ALSO FOR STATISTICS. * CURUSRCVRY, CURUSINTRPT - INDICATE PURGE BUFFER. * * EXIT TTOWORDS, TTOBLOCKS - INCREMENTED. * CURUSLASTIM, TIMEOFDAY - UP TO DATE. * * CALLS MELT, SYSTEM, INSTRMNT2, SMFDLY. * * USES IAFHEADER, P. # ITEM TMP1; TRCSTR("NTR TRSMT$"); CONTROL IFEQ TRACEFLAG,1; TMP1=TASKNXTTTO[CURTASK]; TRACEBOTH("NXTTTO$",TMP1); TMP1=TASKTYPTTO[CURTASK]; TRACEBOTH("TYPTTO$",TMP1); CONTROL FI; CONTROL IFGQ PARANOIA,2; IF TASKNXTTTO[CURTASK] GR SIZTTOBUF THEN MELT("TRANSMIT 1$"); IF CURUSIAFNUM GR MAXCONNECT THEN MELT("TRANSMIT 2$"); CONTROL FI; IF CURUSINTRPT OR CURUSRCVRY THEN BEGIN TASKNXTTTO[CURTASK]=0; IF SHUTDOWN EQ 2 OR TASKTYPTTO[CURTASK] EQ SMF2IAF"MSGOUT" OR TASKTYPTTO[CURTASK] EQ SMF2IAF"PROMPT" THEN BEGIN TASKTYPTTO[CURTASK]=0; IORET END END IAFFUNC=TASKTYPTTO[CURTASK]; IAFLEN=TASKNXTTTO[CURTASK]+1; IAFPTR=0; IAFTERM=CURUSIAFNUM; TTOBUFF[1]=IAFHEADER; CONTROL IFEQ METERING,1; TTOWORDS=TTOWORDS+IAFLEN; TTOBLOCKS=TTOBLOCKS+1; CONTROL FI; TRYTOSEND: # BACK HERE IF RETRY NEEDED # TTOBUFF[0]=0; P=LOC(TTOBUFFER); SSCTYPE=O"3"; SSCCOUNT=IAFLEN; SYSREQ("SSC",0,LOC(SSCBUFFER),IAFSSID); IF SSCSTATUS LAN O"7776" EQ 0 THEN # NO ERROR # BEGIN CONTROL IFEQ TRACEFLAG,1; ITEM TMP2,TMP3; IAFHEADER=TTOBUFF[1]; # SINCE MAY HAVE LOST CTL # TMP1=IAFFUNC; TMP2=IAFTERM; TMP3=IAFLEN-1; P=LOC(TTOBUFF[2]); TRACEBOTH("FUNCOUT$",TMP1); TRACEDEC(TMP2); IF IAFLEN GR 1 THEN TRACEWORDS(MOVFROM,TMP3); CONTROL FI; CONTROL IFEQ METERING,1; IF NOT CURUSOLDSND THEN BEGIN TMP1=TIMEOFDAY-CURUSLASTIM; INSTRMNT2(INSTST"RSPNS",TMP1); END CONTROL FI; IF TASKTYPTTO[CURTASK] EQ SMF2IAF"MSGOUT" THEN CURUSOLDSND=TRUE; ELSE CURUSOLDSND=FALSE; CURUSLASTIM=TIMEOFDAY; IORET END ELSE IF SSCSTATUS LAN O"7776" EQ 4 THEN # IAF BUSY NOW # BEGIN SMFDLY; GOTO TRYTOSEND; END ELSE MELT("CANT TALK TO IAFEX$"); IOEND # OF TRANSMIT # PAGE # VOLUNTEER TO SURRENDER TASK # PROC VOLUNTEER; IOBEGIN(VOLUNTEER) # ** VOLUNTEER - SEE IF THIS TASK SHOULD SLOW DOWN. * * VOLUNTEER IS USED TO PREVENT A TASK FROM EXCESSIVE RESOURCE * USAGE. EDITING CODE SHOULD CALL VOLUNTEER PERIODICALLY * INSIDE ANY PROCESS WHICH IS POTENTIALLY LONG-RUNNING. NOTE * THAT ANY CODE IN THE WORKFILE MANAGER (THE WORKIO MODULE) * IS A "MUST COMPLETE" FUNCTION - THAT MEANS THAT VOLUNTEER * CAN BE CALLED BY ACTUAL EDITING CODE BUT CANNOT BE CALLED * FROM WORKIO ITSELF. * * VOLUNTEER FIRST CONSIDERS WHETHER THE TASK IS USING * EXCESSIVE CPU TIME ON AN INSTANTANEOUS BASIS. THE * OPERATING SYSTEM REAL TIME MILLISECOND CLOCK IS FRESHLY * ACCESSED, AND THE CURRENT TIME IS COMPARED TO THE TIME * TAKEN AT THE START OF THE RECALL CYCLE TO SEE IF MORE THAN * A FEW MILLISECONDS OF CPU HAVE BEEN USED. A CALCULATION * ATTEMPTS TO SELECT A THRESHOLD SUCH THAT SMFEX WILL NEVER * USE MORE THAN ABOUT HALF OF THE CPU. THIS MEANS THAT FOR * AN INSTALLATION WHICH INTENDS THAT SUBSTANTIALLY ALL OF THE * CPU SHOULD BE USED FOR TEXT EDITING, SMFEX MAY BE * UNSUITABLE AND THE SINGLE-USER EDITORS MAY BE BETTER. * * IF THE TASK IS CURRENTLY USING MORE THAN A FEW MILLISECONDS * OF CPU, IT DELAYS, AND THE REMAINDER OF THE VOLUNTEER * ALGORITHM WILL CONTINUE ON A LATER RECALL CYCLE. * * THE REMAINDER OF THE VOLUNTEER ALGORITHM CONSIDERS WHETHER * A TASK HAS RESIDED IN ITS SWAPPED-IN MEMORY IMAGE FOR TOO * MUCH REAL TIME. WE CALCULATE THE REAL TIME FROM THE TIME * AT WHICH THE TASK SWAPPED IN UNTIL THE PRESENT TIME. IF * THE TIME DELTA EXCEEDS THE "VOLTIME" THRESHOLD AND THERE IS * QUEUED TERMINAL OUTPUT, THE TASK WILL SWAP OUT SO THE * OUTPUT CAN BE SENT. IF THE TIME DELTA EXCEEDS THE * THRESHOLD AND THERE ARE MORE USERS AWAITING SWAPIN THAN THE * NUMBER OF TASKS FREE TO SWAP THEM IN, THEN THIS TASK WILL * SWAPOUT SO ANOTHER USER CAN SWAPIN. IF THE NUMBER OF * TERMINAL INPUT BUFFERS IS DOWN TO APPROXIMATELY ONE FOURTH * OF THE TOTAL, THEN THIS TASK WILL SWAP OUT REGARDLESS OF * WHETHER WE HAVE EXCEEDED THE THRESHOLD. * * THUS, THE "VOLTIME" THRESHOLD REPRESENTS THE AMOUNT OF TIME * A TASK IS ASSURED THAT IT CAN REMAIN SWAPPED IN. IF THE * TASK IS NOT PRODUCING OUTPUT AND THERE IS NO EXCESS QUEUE * FOR SWAPIN, THE TASK CAN EXCEED THE TRESHOLD. BUT IF THERE * IS A CRITICAL RESOURCE SHORTAGE (INPUT BUFFERS) THEN ALL * BETS ARE OFF AND WE SWAP THIS TASK AS SOON AS IT HAS * VOLUNTEERED, WITHOUT ALLOWING THE NORMAL GUARANTEED TIME. * * ENTRY TIMEOFDAY - TIME AT WHICH THIS RECALL CYCLE STARTED. * OLDTIMEDAY - TIME FOR START OF PREVIOUS RECALL CYCLE, * OR TIME OF VOLUNTEER EXECUTION WITHIN PREVIOUS * RECALL CYCLE IF VOLUNTEER WAS CALLED. * TASKTIME[CURTASK] - WHEN THIS TASK SWAPPED IN. * TASKNXTTTO[CURTASK] - WHETHER OUTPUT IS PENDING. * TASKSAVAIL - TOTAL USABLE TASKS. * TASKSBUSY - NUMBER OF TASKS USED NOW. * SWPQSIZE - NUMBER OF USERS AWAITING SWAPIN. * REMAINTTI - NUMBER OF UNUSED INPUT BUFFERS. * * EXIT TIMEOFDAY - UP TO DATE. * TASKNXTTTO[CURTASK] - CLEARED IF SWAPPED. * TASKTIME[CURTASK] - NEW VALUE IF SWAPPED. * CURUSCRUNCH - TRUE IF SWAP FOR OVERTIME BUT NO OUTPUT. * TASKREQUE[CURTASK] - TRUE IF SWAP NOT FOR OUTPUT. * * CALLS RTIME, MIN, MAX, SMFDLY, INSTRMNT1, INSTRMNT2, * TTSYNC, TTOTRAP. * * USES TTOADDR, TTOLEN, TTOTYPE. # ITEM DELTA, SURRENDER; RTIME(NEWTIMEDAY); NEWTIMEDAY=B<24,36>NEWTIMEDAY; DELTA=MAX(50,MIN(10,(TIMEOFDAY-OLDTIMEDAY)/2)); IF NEWTIMEDAY-TIMEOFDAY GR DELTA THEN BEGIN SMFDLY; CONTROL IFEQ METERING,1; INSTRMNT1(INSTST"DROPCPU"); CONTROL FI; END # NEXT CODE IS NON-REENTRANT, MUST RUN INSTANTANEOUSLY # DELTA=TIMEOFDAY-TASKTIME[CURTASK]; SURRENDER=0; # TRY TO MOVE IF SITTING ON TYPABLE OUTPUT FOR A LONG TIME # IF TASKNXTTTO[CURTASK] NQ 0 AND DELTA GQ VOLTIME*4 THEN SURRENDER=1; # ALSO TRY TO MOVE IF SHORT ON TASKS FOR A SHORT TIME # IF TASKSAVAIL-TASKSBUSY LS SWPQSIZE AND DELTA GQ VOLTIME THEN SURRENDER=2; # MOVE IF CRITICAL RESOURCE SHORTAGE REGARDLESS OF TIME # IF REMAINTTI LQ (NUMTTIBUF+3)/4 THEN SURRENDER=3; # END OF INSTANTANEOUS ALGORITHM # IF SURRENDER NQ 0 THEN # FACE WRITING ON WALL # BEGIN TRCBOTH("PREEMPT$",CURUSER); CONTROL IFEQ METERING,1; INSTRMNT2(INSTST"PREEMPT",SURRENDER); CONTROL FI; IF SCREENMODE THEN BEGIN # IF IN SCREEN MODE # VDSYNCH; # MUST PRECEED TASKNXTTTO CHECK # END # SINCE OUTPUT MAY BE FLUSHED # ELSE BEGIN # LINE MODE # TTSYNC; # MUST PRECEED TASKNXTTTO CHECK # END # SINCE OUTPUT MAY BE FLUSHED # IF TASKNXTTTO[CURTASK] EQ 0 THEN BEGIN # IF NO OUTPUT QUEUED FOR USER # CURUSCRUNCH=TRUE; # NO OUTPUT, JUST CRUNCHING # TASKREQUE[CURTASK]=TRUE; # SHOULD BE REQUEUED # END TTOADDR=0; TTOLEN=0; TTOTYPE=TTOST"FORCE"; TTOTRAP; END IOEND # OF VOLUNTEER # PAGE # TTY INPUT HOOK # PROC TTITRAP; IOBEGIN(TTITRAP) # ** TTITRAP - PROVIDE EDITOR CODE WITH INPUT TEXT. * * TTITRAP IS CALLED BY THE EDITOR IN LIEU OF CIO READ * FUNCTIONS AGAINST AN INPUT FET. CALLING TTITRAP INSTEAD OF * CIO IS DONE BY CONDITIONAL COMPILATION OF CODE IN THE * TERMIO MODULE. THE EDITOR IS NOT ALLOWED TO OBTAIN INPUT * BY ANY MEANS EXCEPT TO CALL TERMIO AND LET IT DECIDE WHICH * MECHANISM IS APPROPRIATE FOR THE VERSION OF THE EDITOR. * * IN ADDITION TO PROVIDING INPUT, TTITRAP ALSO PROVIDES A * MEANS TO PURGE ANY UNUSED INPUT FROM THE BUFFERS CHAINS. * THIS IS INDICATED BY A NEGATIVE VALUE IN THE TTILEN * PARAMETER. * * WHEN INPUT IS DESIRED, TERMIO USES A POSITIVE VALUE IN THE * TTILEN PARAMETER TO INDICATE THE MAXIMUM NUMBER OF WORDS IT * CAN ACCEPT. THE WORKING BUFFER IS POINTED TO BY THE * TTIADDR PARAMETER. THE TTINEXT PARAMETER IS MAINTAINED BY * THIS ROUTINE TO KEEP TRACK OF WHAT PORTIONS OF THE CURRENT * BUFFER CHAIN HAVE ALREADY BEEN TRANSMITTED. * * INPUT FROM IAFEX IS MAINTAINED BY SMFEX, UNTIL TTITRAP IS * READY TO PASS ON TO THE EDITING CODE, IN A CHAIN OF ONE OR * MORE TERMINAL INPUT BUFFERS. ONE BUFFER IS CHAINED FOR * EACH MESSAGE FRAGMENT SENT BY IAFEX. TTITRAP MAINTAINS THE * TTILAST PARAMETER, WHICH IS THE TEXT OF THE LAST WORD * PASSED TO TERMIO, TO KNOW WHETHER THE PREVIOUS TTITRAP * CYCLE ENCOUNTERED A ZERO BYTE TERMINATOR. THIS ENABLES * TTITRAP TO KNOW WHETHER THE EDITING TASK IS IN THE MIDDLE * OF A SERIES OF FRAGMENTS OR IS AWAITING THE FIRST FRAGMENT * OF A MESSAGE. * * IF TTITRAP DETERMINES THAT THE TASK IS IN THE MIDDLE OF A * SERIES OF FRAGMENTS, BUT DOES NOT YET HAVE THE NEXT * FRAGMENT FROM IAFEX, TTITRAP WILL ATTEMPT TO KEEP THE TASK * DELAYING IN MEMORY, USING THE SPINWHEELS FUNCTION. THIS IS * DONE BECAUSE THERE IS AN EXCELLENT PROBABILITY THAT THE * NEXT INPUT FRAGMENT WILL ARRIVE FROM IAFEX WITHIN A FEW * MILLISECONDS. ON THE OTHER HAND, WHEN THE EDITING TASK * ASKS FOR THE FIRST FRAGMENT OF A MESSAGE AND NO TEXT HAS * ARRIVED YET FROM IAFEX, IT IS LIKELY THAT SEVERAL SECONDS * WILL PASS BEFORE THE INPUT ARRIVES, SO THAT TASK IS ALLOWED * TO SWAPOUT. * * ONCE THE TASK ADVANCES THRU TTITRAP BEYOND ANY WHEELSPIN OR * ANY SWAPOUT, TTITRAP ASSEMBLES AS MUCH INPUT TEXT AS IT * CAN, UNTIL EITHER THE TTILEN PARAMETER IS EXHAUSTED OR THE * END OF ONE BUFFER IS REACHED. TEXT FROM THE NEXT BUFFER OF * A CHAIN OF SEVERAL FRAGMENTS CANNOT BE PROCESSED UNTIL THE * NEXT CALL TO TTITRAP. THIS MEANS THAT THE SIZE OF EACH * FRAGMENT ALLOWED BY IAFEX AND SMFEX MUST BE ADEQUATE FOR * COMPLETE INPUTS WHEN THE EDITOR IS USED IN LINE MODE. THIS * RESTRICTION IS IN EFFECT BECAUSE THE LINE EDITOR REQUESTS * INPUT IN COMPLETE LINE IMAGES. THE SCREEN EDITOR, HOWEVER, * ASKS FOR INPUT ONE WORD AT A TIME, SO MULTIPLE FRAGMENTS * CAN BE PROCESSED WITH MULTIPLE TTITRAP CALLS. * * WHEN TTITRAP DECIDES TO SWAP THE TASK OUT, IT CALLS PAUSEIO * IN THE WORKFILE MANAGER. THIS IS DONE BECAUSE WORKIO HOLDS * SOME TRANSIENT RESOURCES, SUCH AS ITS CIRCULAR BUFFER, AND * NEEDS THE OPPORTUNITY TO COMPLETE ITS USAGE OF THESE * RESOURCES. * * * ENTRY TTILEN - NEGATIVE MEANS PURGE UNUSED INPUT. * POSITIVE IS TASKS CAPACITY FOR TEXT. * TTIADDR - WHERE THE TEXT SHOULD GO. * CURUSTTIBUF - FIRST BUFFER LINKAGE. * TTINEXT - WHERE TTITRAP PREVIOUS EXTRACTED TEXT. * TTILAST - LAST WORD OF TEXT FROM PREVIOUS TTITRAP. * RUNTIME - FOR STATISTICS. * TIMEOFDAY, TASKTIME[CURTASK] - FOR STATISTICS. * ACCTCIO, ACCTTRANS - STATISTICS/ACCOUNTING. * TTOWORDS, TTOBLOCKS - FOR STATISTICS. * TTIBUFADDR[ALL] - SETUP. * TTIBUFLNK[ALL] - LINKAGES. * TTIBUFLEN[ALL] - FRAGMENT SIZES. * TTIBITS - ALLOCATION BITMASK FOR BUFFERS. * * EXIT TTINEXT - INCREMENTED. * CURUSTTIBUF - ADVANCED/CLEARED VIA LINKAGE. * TIMEOFDAY - UP TO DATE. * TASKTIME[CURTASK] - TIME OF SWAPOUT IF ANY. * RUNTIME, ACCTCIO, ACCTTRANS - INCREMENTED. * SPINTIME, RUNTIME, CIOCOUNT - CLEARED IF SWAPPED. * CURUSOKOUT - TRUE. * TTILEN - DECREMENTED. * TTILAST - UPDATED. * REMAINTTI - INCREMENTED IF BUFFER EMPTIED. * TTOWORDS, TTOBLOCKS - CLEARED IF SWAPPED. * * CALLS PURGETTI, SPINWHEELS, CLEARQUE, PAUSEIO, INSTRMNT1, * INSTRMNT2, INSTRMNT3, TTOTRAP, MIN, MOVEWD, DEALLOC. * * USES TTOLEN, TTOTYPE. # ITEM TMP1, TMP2, TMP3; ITEM QUESTION1 I=O"7100 0013 0000 0000 0000"; # PROMPT ONE # ITEM QUESTION2 I=O"7155 0013 0000 0000 0000"; # PROMPT TWO # ITEM XPTINCTL I=O"0006 4704 0015 0000 0000"; # XPARENT INPUT # TRCSTR("NTR ITRP$"); IF TTILEN LS 0 THEN # PURGE ANY BUFFERS # BEGIN IF CURUSTTIBUF NQ 0 THEN PURGETTI; TTINEXT=0; IORET END IF CURUSTTIBUF EQ 0 AND B<48,12>TTILAST NQ 0 THEN BEGIN # DELAY SINCE MORE INPUT DUE ASAP # SPINWHEELS; # DELAY TILL INPUT ARRIVES OR TIMEOUT # CLEARQUE; END IF NOT CURUSOKOUT THEN # FIRST TRANSACTION # BEGIN CURUSOKOUT=TRUE; IF SCREENMODE THEN BEGIN ROWPAINT[COMMANDROW]=TRUE; END ELSE BEGIN TTOADDR=LOC(QUESTION1); TTOLEN=1; TTOTYPE=TTOST"NORMAL"; TTOTRAP; END END IF CURUSTTIBUF EQ 0 THEN # NORMAL TRANSACTION SO SWAPOUT # BEGIN PAUSEIO; # LET WORKIO PP'S DROP # RUNTIME=TIMEOFDAY-TASKTIME[CURTASK]+RUNTIME; TASKTIME[CURTASK]=TIMEOFDAY; ACCTCIO=ACCTCIO+CIOCOUNT; ACCTTRANS=ACCTTRANS+1; CONTROL IFEQ METERING,1; INSTRMNT2(INSTST"TASKUTIL",RUNTIME); INSTRMNT3(INSTST"EDTCIO",RUNTIME-SPINTIME,CIOCOUNT); INSTRMNT1(INSTST"TRANS"); CONTROL FI; IF NOT CURUSINTRPT THEN BEGIN # NO FRESH BREAK, CAN PROMPT # TTOADDR=LOC(QUESTION2); TTOLEN=1; IF SCREENMODE THEN # IF SCREEN MODE # BEGIN IF TABTYPHEAD[0] THEN TTOLEN=0; # IF TYPEAHEAD USED # ELSE TTOADDR=LOC(XPTINCTL); # NO TYPEAHEAD # END TTOTYPE=TTOST"PROMPT"; TTOTRAP; END SPINTIME=0; RUNTIME=0; CIOCOUNT=0; CONTROL IFEQ METERING,1; INSTRMNT3(INSTST"XMIT",TTOWORDS,TTOBLOCKS); TTOBLOCKS=0; TTOWORDS=0; CONTROL FI; END # NOW WE KNOW WE HAVE SOMETHING # MEM[TTIADDR]=0; # ASSURE DEFAULT NULL LINE # WHYLE CURUSTTIBUF NQ 0 AND TTILEN GR 0 DO BEGIN TMP1=CURUSTTIBUF; P=TTIBUFADDR[TMP1]+TTINEXT; P=TTIADDR; TMP2=TTIBUFLEN[TMP1]-TTINEXT; TMP3=MIN(TMP2,TTILEN); MOVEWD(TMP3,MOVFROM,MOVTO); TTINEXT=TTINEXT+TMP3; # INCREMENT SOURCE ADDR # TTILEN=TTILEN-TMP3; # DECREMENT CALLERS COUNT # TTILAST=MEM[LOC(MOVFROM)+TMP3-1]; # SHOWS WHETHER EOL SEEN # IF TTINEXT GQ TTIBUFLEN[TMP1] THEN BEGIN # RELEASE THIS BUFFER, POINT NEXT # TTINEXT=0; DEALLOC(TTIBITS,TMP1,NUMTTIBIT); REMAINTTI=REMAINTTI+1; CURUSTTIBUF=TTIBUFLNK[TMP1]; END END IOEND # OF TTITRAP # PAGE # TTY OUTPUT AND SWAPOUT HOOK # PROC TTOTRAP; IOBEGIN(TTOTRAP) # ** TTOTRAP - QUEUE/ISSUE OUTPUT, SWAP AS NEEDED. * * TTOTRAP IS CALLED BY EDITOR CODE IN LIEU OF CIO WRITE * FUNCTIONS AGAINST AN OUTPUT FET. EDITING CODE IS REQUIRED * TO INTERFACE ALL OUTPUT THRU THE TERMIO MODULE, WHICH USES * CONDITIONAL COMPILATION TO SELECT THE RIGHT TECHNIQUE OF * OUTPUT FOR THE VERSION OF THE EDITOR. * * TTOTRAP IS ALSO USED BY THE VOLUNTEER AND TTITRAP ROUTINES * OF SMFEX. TTOTRAP PROVIDES SWAPOUT LOGIC FOR THOSE CALLERS * TOGETHER WITH ISSUANCE OF RESIDUAL OUTPUT. * * TTOTRAP IS CONTROLLED PRIMARILY BY THE TTOTYPE PARAMETER. * THE "NORMAL" VALUE INDICATES THAT THE CALLER'S INTENT IS TO * OFFER SOME OUTPUT TEXT. TTOTRAP QUEUES THIS TEXT INTO THE * TASK'S UNIQUE OUTPUT BUFFER AS POSSIBLE, CALLING THE * TRANSMIT ROUTINE TO SEND TO IAFEX WHENEVER THE BUFFER IS * FULL. FOR THIS TYPE OF COMMUNICATION TO IAFEX, TTOTRAP * THEN USES THE SPINWHEELS FUNCTION TO HOLD ONTO THE TASK FOR * A FEW EXTRA MILLISEONDS, SINCE THERE IS ABOUT AN EVEN * CHANCE THAT IAFEX WILL QUICKLY RESPOND AND ASK SMFEX TO * CONTINUE TASK EXECUTION. IF IAFEX FAILS TO RESPOND WITHIN * THE LIMITATIONS OF THE SPINWHEELS ROUTINE, THEN TTOTRAP * ASSUMES IT WILL BE SEVERAL SECONDS BEFORE IAFEX AND THE * NETWORK CAN ACCEPT ANY OTHER OUTPUT FOR THIS USER, AND * TTOTRAP SWAPS THE TASK OUT. * * THE SECOND BASIC VALUE FOR THE TTOTYPE PARAMETER IS * "PROMPT". THIS IS USED WHEN TTITRAP CALLS TTOTRAP. * TTOTRAP IS EXPECTED TO IMMEDIATELY TRANSMIT ANY RESIDUAL * QUEUE OF OUTPUT TEXT TO IAFEX, THEN SWAP THE TASK OUT. * TTOTRAP ASSUMES THAT THE CALLER (TTITRAP) HAS ALREADY MADE * A DETERMINATION AS TO LOW PROBABILITY OF RAPID REPLY FROM * IAFEX. * * THE THIRD AND LAST BASIC FUNCTION OF TTOTRAP IS THE "FORCE" * VALUE OF THE TTOTYPE PARAMETER. THIS IS USED BY VOLUNTEER * WHEN IT IS DETERMINED THAT SOME RESIDUAL TEXT HAS BEEN * QUEUED IN THE TASK'S OUTPUT BUFFER FOR AN EXCESSIVE REAL * TIME DELAY, AND OUGHT TO BE SENT TO THE USER'S TERMINAL. * WHEN TTOTRAP PROCESSES THE "FORCE" FUNCTION, IT TRANSMITS * THE BUFFER CONTENT TO IAF AND PROCEEDS TO SWAP THE TASK * WITHOUT ANY USAGE OF THE SPINWHEELS FUNCTION. * * WHEN TTOTRAP DECIDES TO SWAPOUT, IT MUST CALL PAUSEIO IN * THE WORKFILE MANAGER. THIS IS BECAUSE WORKIO DEALS WITH * SOME TRANSIENT RESOURCE, SUCH AS ITS CIRCULAR BUFFER, AND * NEEDS THE OPPORTUNITY TO COMPLETE ITS USAGE OF SUCH * RESOURCES. * * ENTRY TTOTYPE - FUNCTION CODE. * TTOADDR - ADDRESS OF TEXT. * TTOLEN - LENGTH OF TEXT. * TASKNXTTTO[CURTASK] - AMOUNT OF TEXT ALREADY QUEUED. * TTOBUFFER - ADDRESS OF THIS TASK'S BUFFER. * ALL SPINWHEELS PARAMETERS SETUP. * * EXIT TTOLEN - DECREMENTED. * TTOADDR - INCREMENETED. * TASKNXTTTO[CURTASK] - AMOUNT OF TEXT NOW IN BUFFER. * RUNTIME - INCREMENTED IF SWAPOUT. * ACCTOUT - INCREMENTED. * * CALLS MIN, MOVEWD, MELT, INSTRMNT1, TRANSMIT, * SPINWHEELS, CLEARQUE, PAUSEIO, ENDTRANS. * * USES TTOQUIT, TASKTYPTTO[CURTASK]. # ITEM TMP1 I; # TEMPORARY STORAGE # ITEM TMP2 I; # TEMPORARY STORAGE # ITEM ENDBLOCK I=O"0014 0000 0000 0000 0000"; # BLOCK TERMINATOR # TRCSTR("NTR OTRP$"); IF NOT CURUSOKOUT THEN IORET # FIRST TRANS ONLY # P=TTOADDR; ACCTOUT=ACCTOUT+TTOLEN; # ACCOUNT FOR OUTPUT # TTOQUIT=FALSE; WHYLE NOT TTOQUIT DO # SEND ALL TEXT # BEGIN TMP1=SIZTTOBUF-TASKNXTTTO[CURTASK]-1; # AVAILABLE ROOM # TMP2=MIN(TMP1,TTOLEN); # AMOUNT TO SEND # P=TTOADDR; P=LOC(TTOBUFFER)+2+TASKNXTTTO[CURTASK]; MOVEWD(TMP2,MOVFROM,MOVTO); TTOLEN=TTOLEN-TMP2; TTOADDR=TTOADDR+TMP2; TASKNXTTTO[CURTASK]=TASKNXTTTO[CURTASK]+TMP2; TASKTYPTTO[CURTASK]=0; # DETERMINE TRANSMIT # CONTROL IFGQ PARANOIA,4; IF TASKNXTTTO[CURTASK] GR SIZTTOBUF THEN MELT("TTOTRAP 1$"); CONTROL FI; CONTROL IFEQ METERING,1; IF TASKNXTTTO[CURTASK] GQ SIZTTOBUF-1 THEN INSTRMNT1(INSTST"XSTTO"); CONTROL FI; IF (TTOTYPE EQ TTOST"FORCE" AND TASKNXTTTO[CURTASK] NQ 0) OR TASKNXTTTO[CURTASK] GQ (SIZTTOBUF-1) THEN TASKTYPTTO[CURTASK]=SMF2IAF"MSGOUT"; IF TTOTYPE EQ TTOST"PROMPT" AND TTOLEN EQ 0 THEN TASKTYPTTO[CURTASK]=SMF2IAF"PROMPT"; IF TASKTYPTTO[CURTASK] NQ 0 THEN # TRANSMIT # BEGIN P=LOC(ENDBLOCK); # ADD 0014 CONTROL BYTE # P=LOC(TTOBUFFER)+2+TASKNXTTTO[CURTASK]; MOVEWD(1,MOVFROM,MOVTO); TASKNXTTTO[CURTASK]=TASKNXTTTO[CURTASK]+1; TRANSMIT; # TRANSMIT BUFFER TO IAFEX # TASKNXTTTO[CURTASK]=0; IF TTOTYPE NQ TTOST"FORCE" AND TASKTYPTTO[CURTASK] EQ SMF2IAF"MSGOUT" THEN # ATTEMPT CONTINUE # BEGIN SPINWHEELS; # LOOK FOR QUICK ACKNOWLEGE # IF TASKREQUE[CURTASK] THEN # CAN CONTINUE # BEGIN TRCSTR("KWIK TOCIN$"); CLEARQUE; # ACKNOWLEDGE # TASKTYPTTO[CURTASK]=0; # AVOID SWAPOUT # CONTROL IFEQ METERING,1; INSTRMNT1(INSTST"KWIKTOC"); CONTROL FI; END END END IF TASKTYPTTO[CURTASK] NQ 0 OR TTOTYPE NQ TTOST"NORMAL" THEN BEGIN PAUSEIO; RUNTIME=RUNTIME+TIMEOFDAY-TASKTIME[CURTASK]; ENDTRANS; END IF TTOLEN LQ 0 THEN TTOQUIT=TRUE; END IOEND # OF TTOTRAP # PAGE # SWAPPING ROUTINE # PROC SWAP; IOBEGIN(SWAP) # ** SWAP - SWAP THIS TASK IN OR OUT. * * THE SWAP ROUTINE READS OR WRITE THE SWAP PAGE FOR THE * CURRENTLY EXECUTING TASK. FOR A SWAPIN, WE READ THE PAGE * ALREADY ASSIGNED TO THE USER. FOR A SWAPOUT, WE ASSIGN A * PAGE. THE FIRST PRIORITY IN PAGE SELECTION IS EXTENDED * MEMORY IF AVAILABLE, HEALTHY, AND NOT YET FILLED UP. THE * LOWER PRIORITY IS TO SELECT A DISK PAGE. DISK PAGES ARE * PHASED OR INTERLEAVED ROUND-ROBIN ACROSS THE SEVERAL SWAP * DATABASES. * * THE SWAP ROUTINE CAN RECOVER FROM A PARITY ERROR IN AN * EXTENDED MEMORY TRANSFER, BUT DOES NOT ATTEMPT TO HANDLE * DEVICE ERRORS ON DISKS. WHEN AN ERROR IS DETECTED FROM * EXTENDED MEMORY, A USER BEING SWAPPED IN WILL HAVE HIS TASK * ABORTED, WHILE A USER BEING SWAPPED IS UNHARMED. ERROR * RECOVERY INCLUDES THE FAULT-OUT OF THE BAD PAGE, * DETERMINATION WHETHER THE ENTIRE EXTENDED MEMORY FIELD * LENGTH SHOULD BE ABANDONED DUE TO WIDESPREAD ERRORS, AND * FOR SWAPOUT WE ALSO ALLOCATE ANOTHER PAGE AND TRY THE SWAP * AGAIN. * * WHILE SWAP IS EXECUTING, THE DISPATCHING AREA IN THE COMMON * BLOCK IS CONSIDERED TO BE IRRELEVANT. THEREFORE, THE * ROUTINE WHICH MANAGES THE BRUTE-FORCE SHUFFLING OF THE * COMMON BLOCK (SUBROUTINE GETCOMON) NEEDS TO KNOW WHEN * SWAPPING IS IN EFFECT. THIS SWAP ROUTINE IS THUS REQUIRED * TO TURN THE TASKSWPNOW FLAG ON AT ENTRY TIME AND TO TURN IT * OFF AT EXIT TIME. NO REENTRANT EVENTS CAN OCCUR BETWEEN * THE ACTUAL ENTRY OR EXIT AND THE SETTING OF THE TASKSWPNOW * FLAG. * * ENTRY TASKSWPIN[CURTASK] - DIRECTION OF SWAP. * TIMEOFDAY - CURRENT. * SWAPBITS - ALLOCATION BITMASK. * P - ENABLES/DISABLES EXTENDED MEMORY PAGES. * CURUSSWPPAG - USERS PAGE FOR SWAPIN. * TASKADDR[CURTASK] - MEMORY ADDRESS FOR SWAP PAGE. * SWAPLEN - LENGTH OF A PAGE IN WORDS. * SWAPPRUS - LENGTH OF A PAGE IN SECTORS. * ECSERRORS - PREVIOUS EXTENDED MEMORY ERRORS. * SWPFETDONE[ALL] - INTERLOCKS ON DISK FILES. * SWPFET[ALL] - THE DISK FET'S. * MAXSWPPAG - HIGHEST DISK PAGE CREATED TO DATE. * * EXIT TIMEOFDAY - CURRENT. * SWAPBITS - UPDATED. * SWAPGOOD - EXTENDED MEMORY ERRORS FAULTED HERE. * ECSERRORS - INCREMENTED IF ERRORS. * MAXSWPPAG - INCREMENTED IF HIGHWATERMARK RAISED. * TASKTIME[CURTASK] - TIMEOFDAY. * STATUSMSG - MAY CONTAIN ERROR MSG. * CURUSSWPPAG - PAGE SELECTED FOR SWAPOUT. * CURUSVICTIM - TRUE IF ECS PARITY ERROR ON SWAPIN. * * CALLS ALLOC, MELT, INSTRMNT1, READECS, WRITECS, * FORCEALLOC, SMFDLY, MAKEFET, READ, WRITER, REWRITR, * SMFRCL, INSTRMNT2. * * USES TASKSWPNOW[CURTASK], TASKSWPFIL[CURTASK], * TASKSWPPRU[CURTASK], P, P, SWPFET. # ITEM TMP1,TMP2; # SWAP IS REQUIRED TO SET TASKSWPNOW BEFORE ANY DELAYS OCCUR # TRCSTR("NTR SWP$"); TASKSWPNOW[CURTASK]=TRUE; TASKTIME[CURTASK]=TIMEOFDAY; SWAPSTART: IF NOT TASKSWPIN[CURTASK] THEN # ALLOC PAGE # BEGIN ALLOC(SWAPBITS,SWAPMASK,TMP1,NUMSWPBIT); IF TMP1 EQ 0 THEN MELT("SWPO 2$"); CURUSSWPPAG=TMP1; # SAVE INVIOLATE # END TRCBOTH("PAGE$",CURUSSWPPAG); IF CURUSSWPPAG LQ NUMSWPECS THEN # ECS SWAP # BEGIN TRCSTR("SWPECS$"); CONTROL IFEQ ECSCODE,0; CONTROL IFGQ PARANOIA,1; MELT("SWP 3$"); CONTROL FI; CONTROL FI; CONTROL IFEQ ECSCODE,1; CONTROL IFEQ METERING,1; INSTRMNT1(INSTST"SWPE"); CONTROL FI; P=TASKADDR[CURTASK]; P=(CURUSSWPPAG-1)*SWAPLEN; IF TASKSWPIN[CURTASK] THEN READECS(SWAPLEN,MOVTO,MOVFROM,TMP1); ELSE WRITECS(SWAPLEN,MOVFROM,MOVTO,TMP1); CONTROL IFGQ ECSERRMAX,0; IF TMP1 NQ 0 THEN # ECS ERROR, RETRY OR KILL # BEGIN ECSERRORS=ECSERRORS+TMP1; IF ECSERRORS GQ ECSERRMAX THEN BEGIN ECSDOWN=TRUE; C<22,6>STATUSMSG="EM OFF"; END # FAULT OUT THIS PAGE TO PROTECT FUTURE USERS # FORCEALLOC(SWAPGOOD,CURUSSWPPAG,NUMSWPBIT); IF TMP1 GQ 3 AND TASKSWPIN[CURTASK] THEN CURUSVICTIM=TRUE; IF NOT TASKSWPIN[CURTASK] THEN GOTO SWAPSTART; END CONTROL FI; CONTROL IFLS ECSERRMAX,0; IF TMP1 NQ 0 THEN MELT("ECS HARDWARE$"); CONTROL FI; CONTROL FI; END ELSE # DISK SWAPPING # BEGIN TASKSWPFIL[CURTASK]=1+MOD(CURUSSWPPAG-1-NUMSWPECS,NUMSWPFET); TASKSWPPRU[CURTASK]=1+(CURUSSWPPAG-1-NUMSWPECS)/NUMSWPFET*SWAPPRUS; TRCBOTH("SWPDSK$",TASKSWPFIL[CURTASK]); TRCDEC(TASKSWPPRU[CURTASK]); CONTROL IFEQ METERING,1; INSTRMNT1(INSTST"SWPD"); CONTROL FI; WHYLE NOT SWPFETDONE[TASKSWPFIL[CURTASK]] DO SMFDLY; TMP1=TASKSWPFIL[CURTASK]; # VALID TILL SMFRCL # C<0,6>TMP2="SMFSWP"; C<6,1>TMP2=O"33"+TMP1-1; P=LOC(SWPFET[TMP1]); P=TASKADDR[CURTASK]; MAKEFET(ANYFET,TMP2,MOVFROM,SWAPBUFLEN); SWPFETR[TMP1]=TRUE; SWPFETRR[TMP1]=TASKSWPPRU[CURTASK]; IF TASKSWPIN[CURTASK] THEN READ(ANYFET,0); ELSE BEGIN SWPFETIN[TMP1]=SWPFETIN[TMP1]+SWAPLEN; IF CURUSSWPPAG GR MAXSWPPAG THEN BEGIN MAXSWPPAG=CURUSSWPPAG; SWPFETRR[TMP1]=LOC(DUMB); WRITER(ANYFET,0); END ELSE REWRITR(ANYFET,0); END SMFRCL(ANYFET); END # ONCE TASKSWPNOW IS CLEARED, SWAP CANNOT DELAY ANYMORE # TASKSWPNOW[CURTASK]=FALSE; CONTROL IFEQ METERING,1; TMP1=TIMEOFDAY-TASKTIME[CURTASK]; INSTRMNT2(INSTST"SWPTIM",TMP1); CONTROL FI; TASKTIME[CURTASK]=TIMEOFDAY; IOEND # OF SWAP # PAGE # USER/SYSTEM-STATE CHANGING CODE # PROC PAUSE; IOBEGIN(PAUSE) # ** PAUSE - INTERFACE TO EXECUTIVE TO START/END TIME SLICE. * * PAUSE IS THE ROUTINE USED BY USER-MODE ROUTINES TO END A * SLICE ON THE CPU, AND BY THE EXECUTIVE TO RESUME EXECUTION * WHERE THE PREVIOUS TIME SLICE ENDED. THUS, PAUSE EXITS TO * LABEL AFTERSLICE AND PROVIDES THE LABEL RESUME, WHICH * RUNJOBS WILL JUMP TO JUST IN FRONT OF THE DEFINITION OF THE * AFTERSLICE LABEL. # XDEF LABEL RESUME; XREF LABEL AFTERSLICE; GOTO AFTERSLICE; RESUME: # HERE FOR NEW TIMESLICE # IOEND # OF PAUSE # PROC TRANSACT; BEGIN # ** TRANSACT - TRANSITION FROM EXECUTIVE TO USER FOR SWAPIN. * * TRANSACT IS USED BY THE RUNJOBS EXECUTIVE, JUST BEFORE * RUNJOBS DEFINES THE AFTERTRANS LABEL, TO SWITCH FROM * EXECUTIVE MODE TO USER MODE AND PERFORM SWAPINS. TRANSACT * THEN REPAIRS THE COMMON BLOCK DISPATCHING AREA, REPAIRS THE * RETURN JUMP WORDS IN ALL ACTIVE SUBROUTINE ENTRY POINTS, * CHECKS FOR CERTAIN INTERRUPT CONDITIONS, AND EXITS VIA A * JUMP TO THE MORETRANS LABEL IN THE ENDTRANS ROUTINE. THIS * TYPE OF EXIT IS USED BECAUSE THE TASK IS PRESUMED TO HAVE * HAVE COMPLETED ITS PREVIOUS SWAPPED-IN TRANSACTION BY * CALLING ENDTRANS, AND ENDTRANS IS RESUMED TO HAVE JUMPED TO * THE EXECUTIVE JUST IN FRONT OF THE DEFINITION OF LABEL * MORETRANS. * * ENTRY MAY BE CALLED ONLY BY RUNJOBS TRANSACTION SECTION. * CURUSINTRPT - AN INTERRUPT MUST BE COMMUNICATED TO * USER-MODE CODE AFTER SWAPIN IS COMPLETE. * SHUTDOWN - CAUSES SYNTHESIS OF INTERRUPTS. * CURUSRCVRY - FSE JOB DETACHING. * RSTKPTR - SIZE OF REENTRANCY STACK. * * EXIT VIA LABEL MORETRANS. * RENTSTK - REENTRANCY STACK. * COMMON BLOCK AND SUBROUTINE ENTRIES REBUILT. * USRBRK - 1 IF ANY INTERRUPT PASSED TO USER. * CURUSVICTIM - TRUE IF SWAPIN RUINED USER DATA. * FORCESINGLE, FORCENULL - ADDITIONAL INTERRUPTS. * WORKFILE CHECKPOINTED AND EXIT VIA LABEL AFTEREDIT * IF CURUSRCVRY IS ON. * * CALLS SWAP, DEALLOC, GETCOMMON, CHECKIO, MOVEWD, SPREAD. * * USES SWAPRSTK, TASKSWPIN[CURTASK]. # XREF LABEL AFTEREDIT; XREF LABEL AFTERTRANS; XREF LABEL MORETRANS; TRCSTR("NTR TRNSCT$"); TASKSWPIN[CURTASK]=TRUE; SWAP; # ONCE SWAP RETURNS, WE CANNOT REENTER UNTIL AFTER GETCOMMON # IF SHUTDOWN NQ 2 THEN DEALLOC(SWAPBITS,CURUSSWPPAG,NUMSWPBIT); CURUSSWPPAG=0; LASTTASK=0; # RECONSTRUCT COMMON AFTER SWAP # GETCOMMON; # END OF TIME-CRITICAL SEQUENCE # IF CURUSINTRPT THEN USRBRK=1; IF CURUSVICTIM THEN BEGIN SMFVICTIM=TRUE; ERRSTRING="ECS HARDWARE$"; END IF SHUTDOWN NQ 0 THEN FORCESINGLE=TRUE; IF SHUTDOWN GR 1 THEN BEGIN CURUSINTRPT=TRUE; USRBRK=1; FORCENULL=TRUE; END IF CURUSRCVRY THEN # CHKPT AND END EDIT # BEGIN FORCENULL=TRUE; IF SHUTDOWN EQ 2 THEN BEGIN USRBRK=0; CURUSINTRPT=FALSE; END IF NOT CURUSDONE THEN BEGIN # IF WORKFILE NOT YET CHECKPOINTED # CHECKIO; # CHECKPOINT WORKFILE # END GOTO AFTEREDIT; END MOVEWD(RSTKPTR-1,SWAPRSTK,RENTSTK); SPREAD(RSTKPTR,RENTSTK); # OUR RETURN GOES NOT TO CALLER BUT TO USER # GOTO MORETRANS; # LET GATHERED CODE RETURN TO USER # END # OF TRANSACT # PROC ENDTRANS; IOBEGIN(ENDTRANS) # ** ENDTRANS - END A TRANSACTION WITH SWAPOUT. * * ENDTRANS IS CALLED ONLY BY TTOTRAP, FOR THE PURPOSE OF * SWAPPING THE CURRENT TASK OUT. ENDTRANS CONSOLIDATES DATA * FROM SUBROUTINE ENTRY POINTS AND FROM THE DISPATCHABLE * COMMON BLOCK, THEN SWAPS THE MEMORY IMAGE OUT. ENDTRANS * THEN EXITS BY JUMPING TO LABEL AFTERTRANS IN THE RUNJOBS * EXECUTIVE. ENDTRANS PROVIDES THE DEFINITION OF LABEL * MORETRANS SO THAT WHEN RUNJOBS CALLS TRANSACT TO SWAP THIS * USER BACK IN, TRANSACT CAN JUMP TO THE ADDRESS AT WHICH THE * TASK HAD FORFEITED CONTROL. * * ENDTRANS IS ALLOWED TO BE CALLED ONLY BY TTOTRAP BECAUSE * THE TASK CANNOT SWAPOUT WITH ANY RESIDUAL OUTPUT LEFT IN * THE BUFFER OWNED BY THE TASK. * * ENTRY ONLY FROM TTOTRAP. * RSTKPTR - DEPTH OF REENTRANCY STACK. * RENTSTK - THE REENTRANCY STACK. * * EXIT VIA LABEL AFTERTRANS. * * CALLS GATHER, MOVEWD, PUTCOMMON, SWAP, MELT. * * USES SWAPRSTK, TASKSWPIN[CURTASK]. # XREF LABEL AFTERTRANS; XDEF LABEL MORETRANS; TRCSTR("NTR NDTRNS$"); GATHER(RSTKPTR,RENTSTK); MOVEWD(RSTKPTR-1,RENTSTK,SWAPRSTK); # MUST EXECUTE INSTANTLY FROM PUTCOMMON INTO START OF SWAP # PUTCOMMON; # MAKE COMMON SWAPPABLE # TASKSWPIN[CURTASK]=FALSE; SWAP; # END OF TIME-CRITICAL SEQUENCE # CONTROL IFGQ PARANOIA,3; IF TASKNXTTTO[CURTASK] NQ 0 THEN MELT("NTRNS 2$"); CONTROL FI; GOTO AFTERTRANS; # RETURN GOES BACK TO USER # MORETRANS: IOEND # OF ENDTRANS # PAGE # INITIALIZATION AND TERMINATION # PROC INITMON; BEGIN # ** INITMON - INITIALIZE SMFEX. * * INITMON IS THE PRESET ROUTINE FOR SMFEX. THE FOLLOWING * FUNCTIONS ARE PERFORMED -- * * THE PARAMETERS CRACKED BY THE OPERATING SYSTEM AT RA+2 ARE * CHECKED FOR RECOVERY MODE OR FOR A NUMBER OF TASKS TO BE * FORMATTED. THE SHUTDOWN FLAG IS SET FOR RECOVERY MODE, AND * THE TASKSAVAIL FLAG IS SET AND FORCED WITHIN REASONABLE * BOUNDS FOR A NUMERIC PARAMETER. * * MISCELLANEOUS POINTERS AND LENGTH FIELDS ARE COMPUTED. THE * FIELD LENGTH IS THEN INCREASED TO MAKE ROOM FOR THE MODEL * FORMAT OF THE COMMON BLOCK. THE FIELD LENGTH IS INCREASED * TO ALLOCATE THE TERMINAL INPUT BUFFERS. THE BITMAPS USED * FOR ALLOCATION OF SMFEX RESOURCES ARE INITIALIZED. THEN * EACH TASK IS BUILT OUT OF INCREASED FIELD LENGTH. * * TASK CONSTRUCTION IS DELEGATED TO THE BUILDTASK ROUTINE. * THE COMMON BLOCK IS USED TO CONSTRUCT THIS TASK'S POINTERS * FOR A NUMBER OF PER-TASK ARRAYS. THEN COMMON BLOCK IS THEN * COPIED INTO THE EXTENDED FIELD LENGTH SO EACH TASK CAN * CONTAIN ITS OWN BASE ADDRESSES FOR ARRAYS. * * THE EXTENDED MEMORY FIELD LENGTH IS DETERMINED BY SEEING * HOW MUCH MEMORY IS DESIRED FOR THE COMPILE-TIME * CONFIGURATION AND HOW MUCH IS ACTUALLY AVAILABLE FROM THE * RUNNING SYSTEM. THE ALLOCATION BITMAPS ARE MODIFIED IF * NEEDED TO ALLOW FOR THE NUMBER OF ECS SWAP PAGES ACTUALLY * OBTAINED. EXTENDED MEMORY SWAPPING IS COMPLETELY DISABLED * IF THE RUNNING SYSTEM CANNOT PROVIDE ANY SECONDARY FIELD * LENGTH AT ALL. * * FOR SUBSYSTEM RECOVERY, A SHORTENED INITMON ALGORITHM TAKES * EFFECT. WE ASSUME THE PREVIOUS SMFEX JOB STEP EXECUTED AN * IDENTICAL VERSION OF THIS PROGRAM (I.E., SMFEX HAS NOT BEEN * SYSEDITED WHILE RUNNING) AND THAT THE TERMINATION OF THE * PREVIOUS SMFEX JOB STEP EXITED INTO A "DMB" FORMAT DUMP * FILE. THE DUMP FILE IS SCANNED TO OBTAIN THE USER TABLE. * WE CHECK ALL USER TABLE ENTRIES FOR RECOVERABLE TASKS AND * QUEUE THOSE USERS FOR SWAPIN. RECOVERABLE TASKS ARE THOSE * WHICH EXIST ON A SWAP PAGE BUT NOT IN MEMORY. FOR * SUBSYSTEM RECOVERY, THE INITMON ALGORITHM TERMINATES AT * THIS POINT IN THE FLOW. * * FOR TRUE SUBSYSTEM INITIATION, THE REMAINDER OF THE INITMON * ALGORITHM IS AS FOLLOWS -- * * ALL DISK SWAP FILES ARE EVICTED. * * THE NEGATIVE FIELD LENGTH IS BOOSTED TO ITS ANTICIPATED * HIGH WATER MARK BY ALLOCATING THEN RETURNING A LARGE GROUP * OF TOKEN FILES. THIS IS DONE BECAUSE THE TLX PP ROUTINE * WHICH TRANSITIONS USERS FROM SINGLE USER MODE TO MULTI USER * MODE IS UNABLE TO INCREASE OUR NEGATIVE FIELD LENGTH TO * MAKE ROOM FOR LOCAL FNT'S FOR THE TRANSFERRED WORKFILES. * TLX TREATS OUR PRE-ALLOCATED NEGATIVE FIELD LENGTH AS A * LIMITATION AS TO WHETHER A PARTICULAR SINGLE USER EDITOR * JOB WILL BE ACCEPTED INTO MULTI USER MODE. * * FINALLY, COMMUNICATIONS ARE ESTABLISHED WITH IAFEX VIA THE * SCP/UCP FACILITY. SMFEX IS THE UCP AND IAFEX IS THE SCP. # ITEM TMP1,TMP2,TMP3,TMP4; P=0; IF B<00,01>MEM[CSMR] EQ 0 THEN # IF SYSTEM CHARACTER SET = 63 # BEGIN XLTINTXP[O"00"]=O"4045"; # COLON = PERCENT # XLTINTXP[O"63"]=O"4072"; # PERCENT = COLON # XLTDSPXP[O"00"]=O"4045"; # COLON = PERCENT # XLTDSPXP[O"63"]=O"4072"; # PERCENT = COLON # XLTDSPINT[O"00"]=O"0063"; # COLON = PERCENT # XLTDSPINT[O"63"]=O"0000"; # PERCENT = COLON # XLTXPINT[O"45"]=O"0000"; # PERCENT = COLON # XLTXPINT[O"72"]=O"0063"; # COLON = PERCENT # XLTINTDSP[O"00"]=O"0055"; # COLON = BLANK # END SFMCSTF; IF C<0,7>MEM[2] EQ "RECOVER" THEN SHUTDOWN=2; ELSE BEGIN TMP1=0; TMP2=0; TMP3=C<0,1>MEM[2]-O"33"; WHYLE TMP2 LQ 6 AND TMP3 GQ 0 AND TMP3 LQ 9 DO BEGIN TMP1=TMP1*10+TMP3; TMP2=TMP2+1; TMP3=CMEM[2]-O"33"; END IF TMP1 GQ 2 AND TMP1 LQ NUMTASKS THEN TASKSAVAIL=TMP1; END P=LOC(SWAPGOOD); SWAPLEN=LOC(SWAPEND)-LOC(DATASTART); DISPATLEN=LOC(DISPATEND)-LOC(DATASTART); FIELDLEN=B<42,18>MEM[O"65"]; MODELPTR=FIELDLEN; MODELLEN=LOC(SWAPEND)-LOC(DATAEND); FIELDLEN=MODELPTR+MODELLEN; FLDLEN(FIELDLEN+4); P=MODELPTR; MOVEWD(MODELLEN,DATAEND,MOVTO); FOR TMP3=1 STEP 1 UNTIL NUMTTIBUF DO BEGIN TTIBUFADDR[TMP3]=FIELDLEN; FIELDLEN=FIELDLEN+SIZTTIBUF; FLDLEN(FIELDLEN+4); END FOR TMP1=1 STEP 1 UNTIL MAXREENT DO RSTK[TMP1]=LOC(RSTK[TMP1]); INITBITMAP(SWAPBITS,NUMSWPBIT,NUMSWPECS+NUMSWPDSK); INITBITMAP(TTIBITS,NUMTTIBIT,NUMTTIBUF); INITBITMAP(SWAPGOOD,NUMSWPBIT,NUMSWPECS+NUMSWPDSK); INITBITMAP(SWAPBAD,NUMSWPBIT,NUMSWPECS+NUMSWPDSK); INITBITMAP(TTIGOOD,NUMTTIBIT,NUMTTIBUF); KILLECSBITS(SWAPBAD); MEM[USRBASE]=LOC(USERTAB); # FOR EASY C DISPLAY # MEM[SLTBASE]=LOC(TASKTAB); CONTROL IFEQ METERING,1; MEM[STTBASE]=LOC(INSTDATA); CONTROL FI; FOR CURTASK=1 STEP 1 UNTIL TASKSAVAIL DO BUILDTASK(CURTASK); SWAPLEN=SWAPLEN+TASKDSPADR[1]-TASKADDR[1]; SWAPPRUS=((SWAPLEN+O"100")/O"100"); SWAPBUFLEN=1+SWAPPRUS*O"100"; FLDLEN(FIELDLEN+4); CONTROL IFEQ ECSCODE,1; # REQUEST ECS FIELD LENGTH/ SEE IF IT IS THERE # GETFLCE(ECSFIELD); # GET VALIDATION LIMIT # TESTECS(TMP1); # GET EXISTENCE/ENABLED # IF B<0,12>ECSFIELD NQ 0 AND TMP1 NQ 0 THEN BEGIN # ALLOCATE DESIRED OR AVAILABLE ECS FIELD LENGTH # ECSFIELD=MIN(NUMSWPECS*SWAPLEN,512*B<0,12>ECSFIELD); FLDLENE(ECSFIELD); SETRFLE((ECSFIELD+511)/512); PROTEON; # SINCE ABORT RECOVERY ADVANCES # # TURN OFF ANY SWAP PAGES BEYOND AVAILABLE FIELD LENGTH # TMP1=ECSFIELD/SWAPLEN; FOR TMP2=TMP1+1 STEP 1 UNTIL NUMSWPECS DO FORCEALLOC(SWAPGOOD,TMP2,NUMSWPBIT); END ELSE KILLECSBITS(SWAPGOOD); # TURN OFF ALL ECS SWAP PAGES # CONTROL FI; CONTROL IFEQ ECSCODE,0; KILLECSBITS(SWAPGOOD); CONTROL FI; IF SHUTDOWN EQ 2 THEN BEGIN TERMMON; # SHUT OFF ANY INPUT # P=FIELDLEN; MAKEFET(DMBFET,"ZZZZDMB",MOVFROM,O"1001"); FIELDLEN=FIELDLEN+O"1001"; FLDLEN(FIELDLEN+4); REWIND(DMBFET,1); READ(DMBFET,0); TMP1=0; TMP4=0; FOR TMP2=0 STEP 1 UNTIL O"61" DO BEGIN # UNTIL RIGHT (CM) CONTROL WORD # READO(DMBFET,TMP1,TMP4); # ACCESS NEXT CONTROL WORD # IF TMP4 NQ 0 THEN MELT("RCVR 1$"); END IF C<0,2>TMP1 NQ "CM" THEN MELT("RCVR 2$"); FOR TMP2=1 STEP 1 UNTIL LOC(USERTAB) DO BEGIN # CONSUME LOWER FIELD LENGTH # READO(DMBFET,TMP1,TMP4); IF TMP4 NQ 0 THEN MELT("RCVR 3$"); END # FINALLY READ IN THE USER TABLE # FOR TMP2=1 STEP 1 UNTIL 3*NUMSMFUSR DO BEGIN P=LOC(USERTAB)+TMP2-1; READO(DMBFET,MOVTO,TMP4); IF TMP4 NQ 0 THEN MELT("RCVR 4$"); END REWIND(DMBFET,1); # IN CASE TAKE ANOTHER DUMP # FOR CURUSER=1 STEP 1 UNTIL NUMSMFUSR DO BEGIN # UPDATE AND QUEUE CHECKPT # GETUSER; IF CURUSINTASK THEN CURUSDONE=TRUE; CURUSINQUE=FALSE; CURUSTTIBUF=0; CURUSQUELNK=0; CURUSINTASK=FALSE; IF CURUSLOGGED AND CURUSSWPPAG NQ 0 AND NOT CURUSDONE THEN BEGIN TRCBOTH("RECOVER$",CURUSER); CURUSRCVRY=TRUE; CURUSINTRPT=TRUE; PUTINQ; END PUTUSER; END FOR TMP1=1 STEP 1 UNTIL NUMSWPFET DO SWPFETDONE[TMP1]=TRUE; TRCSTR("END INITMON RCVR$"); RETURN; # SO THAT NOTHING ELSE IS DONE # END FOR TMP1=1 STEP 1 UNTIL NUMSWPFET DO BEGIN CONTROL IFGR NUMSWPFET,8; BADSTUFF; CONTROL FI; # CODE DEPENDENT # C<0,6>TMP2="SMFSWP"; C<6,1>TMP2=TMP1+O"32"; P=LOC(SWPFET[TMP1]); P=TASKADDR[1]; MAKEFET(ANYFET,TMP2,MOVFROM,SWAPBUFLEN); READ(ANYFET,1); # ASSURE FNT ALLOCATE # EVICT(ANYFET,1); # ASSURE NO OLD DATA # END PROC GENNULL; BEGIN C<0,2>TMP2="NE"; TMP3=TMP1; FOR TMP4=6 STEP -1 UNTIL 2 DO BEGIN CTMP2=O"33"+MOD(TMP3,10); TMP3=TMP3/10; END MAKEFET(NULLFET,TMP2,CORE,O"101"); END # OF GENNULL # FOR TMP1=1 STEP 1 UNTIL NUMSMFUSR DO BEGIN # MANY NULL FILES TO ENLARGEN NFL # GENNULL; READ(NULLFET,1); # ASSURE FNT ALLOCATE # END FOR TMP1=1 STEP 1 UNTIL NUMSMFUSR DO BEGIN # THEN RELEASE THE FNT SPACE # GENNULL; RETERN(NULLFET,1); END IAFPTR=LOC(SSCINP[0]); INITSSCWORD[1]=IAFHEADER; P=LOC(INITSSC); SSCSTATUS=-1; # DELAY UNTIL IAF HANDSHAKE WORKS OR IDLE COMMAND DETECTED # WHYLE SSCSTATUS LAN O"7776" NQ 0 AND MEM[0] LAN O"100000" EQ 0 DO BEGIN SSCWORD=O"0000 0000 0000 0103 0000"; SYSREQ("SSC",0,LOC(SSCBUFFER),IAFSSID); RECALL(0); END SFMSSTF; TRCSTR("END INITMON$"); END # OF INITMON # PROC TERMMON; BEGIN # ** TERMMON - TERMINATE SMFEX NORMALLY. * * CALLS SFMCSTF. # SFMCSTF; TRCSTR("COMPLETE$"); TRCFRC; END # OF TERMMON # PROC MELT(STR); BEGIN # ** MELT - TERMINATE SMFEX ABNORMALLY. * * ENTRY STR - ERROR MESSAGE FOR INTERNAL ABORT CONDITION. * COMPLETE - IF ALREADY SET, THEN MELT FUNCTIONS AS * AN INTERFACE TO PRODUCE A DAYFILE MESSAGE THEN * ALLOW NORMAL TERMINATION. NORMALLY, COMPLETE * IS FALSE AND MELT ABORTS THE JOB STEP. * * EXIT VIA LABELS ABORT OR ENDRUN. * * CALLS MESSAGE, TERMMON. # ITEM STR C(30); ITEM STR2 C(30); ITEM TMP1,TMP2; TRCSTR(STR); TRCFRC; STR2=STR; TMP2=0; FOR TMP1=0 STEP 1 UNTIL 29 DO BEGIN IF TMP2 EQ 0 AND CSTR2 EQ "$" THEN TMP2=TMP1+1; END FOR TMP1=29 STEP -1 UNTIL TMP2 DO CSTR2=0; MESSAGE(STR2,0,1); TERMMON; IF COMPLETE THEN ENDRUN; ELSE ABORT; END # OF MELT # PAGE # ** SMFEX - MAIN ALGORITHM. * * THE SMFEX MAIN ALGORITHM INITIALIZES THE SUBSYSTEM, THEN * LOOPS ON THE THREE MAIN PROCESSES UNTIL A COMPLETION FLAG * IS DETECTED. THE SUBSYSTEM IS THEN TERMINATED. * * THE THREE PROCESSING PHASES PERFORM TIME-DEPENDENT FUNCTIONS, * RECEIVE AND QUEUE MESSAGES FROM IAFEX, AND EXECUTE TASKS AND * REPLY TO IAFEX. * * CALLS INITMON, DOTIMER, DOINPUT, RUNJOBS, INITMON. # ITEM TMP1; INITMON; # GET EVERYTHING SET UP # WHYLE NOT COMPLETE DO # MAIN LOOP # BEGIN DOTIMER; # RECALL CPU, HANDLE TIMER AND OPERATOR # DOINPUT; # PROCESS INPUT FROM IAFEX # RUNJOBS; # EXECUTE ANY TASKS THAT CAN # END # OF MAIN LOOP # TERMMON; # CLOSE UP SHOP # ENDRUN; END TERM