Google
 

Trailing-Edge - PDP-10 Archives - tops10_703_distr_bb-x140b-sb - 10,7/703mon/queser.mac
There are 11 other files named queser.mac in the archive. Click here to see a list.
TITLE	QUESER -- ENQUEUE/DEQUEUE SERVICE FACILITY - V140
SUBTTL	S. BLOUNT /WRS/PMV	10 SEP 85
	SEARCH	F,S
	$RELOC
	$HIGH

;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
;  OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
.CPYRT<1974,1986>
;COPYRIGHT (C) 1974,1975,1976,1977,1978,1979,1980,1982,1984,1986
;BY DIGITAL EQUIPMENT CORP., MAYNARD, MASS.
;ALL RIGHTS RESERVED.

;
;
XP VQUESR,140

ENTRY	QUESER		;LOAD THIS MODULE IF NEEDED

QUESER::


;ENQ/DEQ PROVIDES A QUEUEING FACILITY WHICH CAN BE USED
; TO INSURE MUTUAL EXCLUSION OF PROCESSES OPERATING ON
; A COMMON DATA BASE.

	SALL
	SUBTTL	DATA BASE DEFINITIONS
REPEAT 0,<

;Q-BLOCK FORMAT

		+-------------------------+-------------------------+
		!.QBLJQ:                  !.QBNJQ:                  !
		!      BACK POINTER TO    !    FORWARD POINTER TO   !
		!   LAST Q-BLOCK FOR JOB  !   NEXT Q-BLOCK FOR JOB  !
		+-------------------------+----------+--------------+
		!.QBJCH:                  !.QBCHN:   !.QBFLG:       !
		!                         ! CHANNEL  !              !
		!    JOB CONTEXT HANDLE   !  NUMBER  !   FLAGS      !
		+-------------------------+----------+--------------+
		!.QBLQ:                   !.QBNQ:                   !
		!  POINTER TO LAST QUEUE  !  POINTER TO NEXT QUEUE  !
		!  BLOCK FOR THIS LOCK    !  BLOCK FOR THIS LOCK    !
		+-------------------------+-------------------------+
		!.QBLQR:                  !.QBNQR:                  !
		!     POINTER TO LAST     !     POINTER TO NEXT     !
		!     MULTIPLE Q-BLOCK    !     MULTIPLE Q-BLOCK    !
		+-------------------------+-------------------------+
		!.QBRID:                  !.QBNRP:                  !  
                !                         !                         !
		!       REQUEST I.D.      !  # REQUESTED FROM POOL  !
		+-------------------------+-------------------------+
		!.QBGRP:                  !.QBLB:                   !
                !       GROUP             !     POINTER TO          !
                !       NUMBER            !     LOCK-BLOCK          !
		+-------------------------+-------------------------+
		! .QBCHK:                 ! .QBMSK:                 !
		! POINTER TO NEXT Q-BLOCK !       MASK BLOCK        !
		! TO BE DEADLOCK CHECKED  !                         !
		+-------------------------+-------------------------+
;LOCK-BLOCK FORMAT

		+-------------------------+-------------------------+
		!.LBLHS:                  !.LBNHS:                  !
		!   BACK POINTER TO LAST  !     POINTER TO NEXT     !
		! LOCK-BLOCK ON HASH CHAIN! LOCK-BLOCK ON HASH CHAIN!
		+-------------------------+-------------------------+
		!.LBLEN:                  !.LBFLG:                  !
		!    LENGTH OF LOCK-BLOCK !          FLAGS          !
		!                         !                         !
		+-------------------------+-------------------------+
 		!.LBLQ:                   !.LBNQ:                   !
                !     POINTER TO LAST     !     POINTER TO NEXT     !
                !     Q-BLOCK IN CHAIN    !     Q-BLOCK IN CHAIN    !
		+-------------------------+-------------------------+
		!.LBLVL:                  !.LBACC:                  !
                !     LEVEL NUMBER        ! ADDRESS OF ACCESS TABLE !
                !                         ! -2, -3, OR 400000+JOB # !
		+-------------------------+-------------------------+
		!.LBPUL:                  !.LBAVL:                  !
                !                         !                         !
                !     # IN POOL           !    # AVAILABLE          !
		+-------------------------+-------------------------+
                !.LBTIM:                                            !
                !                     TIME-STAMP                    !
                !                                                   !
		+---------------------------------------------------+
		!.LBTLN:                  !.LBTBL:                  !
		!  LENGTH OF TABLE BLOCK  !  LOCK ASSOCIATED TABLE  !
		!                         !                         !
		+-------------------------+-------------------------+
		!.LBNMS                   !.LBPLT!                  !
		!  NUMBER OF WORDS IN THE !TIMER !                  !
		!       MASK BLOCK        !                         !
		+-------------------------+-------------------------+
		!.LBTXT:             ASCIZ STRING                   !
		!              OR 500000,,0 + USER CODE             !
		!                OR 36-BIT USER CODE                !
		+---------------------------------------------------+

>	;END OF REPEAT 0
	SUBTTL	STRUCTURE MACRO DEFINITIONS


;DEFINE DATA STRUCTURE
; NAM - NAME OF STRUCTURE AS USED IN CODE
; LOCN - ADDRESS OF DATA
; POS - POSITION OF DATA WITHIN WORD (RIGHTMOST BIT NUMBER)
; SIZ - SIZE OF DATA WITHIN WORD

DEFINE DEFSTR(NAM,LOCN,POS,SIZ)<
	RADIX	10
	.TSTSZ(POS,SIZ)
	DEFST1(\..TYP,NAM,LOCN,POS,SIZ)
	RADIX	8>

DEFINE DEFST1(TYP,NAM,LOCN,POS,SIZ)<
	DEFINE	NAM(OPS,AC,Y)<
	  ..C==0
	  IRP OPS,<
	    IFE TYP-..C,<
	      STOPI
	      IFN 3-..C,<
		OPS AC,LOCN''Y>
	      IFE 3-..C,<
		OPS AC,[POINT SIZ,LOCN''Y,POS]>>
	    ..C=..C+1>>>

DEFINE .TSTSZ(POS,SIZ)<
	..TYP==3
	IFE	SIZ-36,<..TYP=0>
	IFE	SIZ-18,<
	  IFE POS-35,<..TYP=1>
	  IFE POS-17,<..TYP=2>>>

;LOAD, STORE.
; AC - AC OPERAND
; STR - STRUCTURE NAME
; Y - (OPTIONAL) LOCATION OF DATA IF NOT AS ORIGINALLY SPECIFIED

DEFINE LOAD.(AC,STR,Y)<
	STR(<MOVE,HRRZ,HLRZ,LDB>,AC,Y)>

DEFINE STOR.(AC,STR,Y)<
	STR(<MOVEM,HRRM,HRLM,DPB>,AC,Y)>



	SUBTTL	DATA STRUCTURE DEFINITIONS

;DATA STRUCTURE DEFINITIONS


;Q-BLOCK STRUCTURES

DEFSTR(.QBLJQ,0,17,18)		;POINTER TO LAST ENTRY IN JOB-Q
DEFSTR(.QBNJQ,0,35,18)		;POINTER TO NEXT ENTRY IN JOB-Q
DEFSTR(.QBJCH,1,17,18)		;JOB/CONTEXT HANDLE OF THIS USER
DEFSTR(.QBCHN,1,26,9)		;CHANNEL NUMBER
DEFSTR(.QBFLG,1,35,9)		;FLAGS FOR THIS Q-ENTRY
DEFSTR(.QBLQ,2,17,18)		;POINTER TO LAST ENTRY IN QUEUE
DEFSTR(.QBNQ,2,35,18)		;POINTER TO 1ST ENTRY IN QUEUE
DEFSTR(.QBLQR,3,17,18)		;POINTER TO PREVIOUS ENTRY IN THIS REQUEST
DEFSTR(.QBNQR,3,35,18)		;POINTER TO NEXT ENTRY IN THIS REQUEST
DEFSTR(.QBRID,4,17,18)		;REQUEST ID FOR THIS ENTRY
DEFSTR(.QBNRP,4,35,18)		;NUMBER OF RESOURCES REQUESTED FROM POOL
DEFSTR(.QBGRP,5,17,18)		;GROUP NUMBER FOR THIS USER
DEFSTR(.QBLB,5,35,18)		;POINTER TO LOCK BLOCK
DEFSTR(.QBCHK,6,17,18)		;NEXT BLOCK TO BE DEADLOCK CHECKED
DEFSTR(.QBMSK,6,35,18)		;POINTER TO MASK BLOCK
QBSIZE==7			;SIZE OF Q-BLOCK



;LOCK-BLOCK STRUCTURES

DEFSTR(.LBLHS,0,17,18)		;POINTER TO LAST ENTRY IN HASH CHAIN
DEFSTR(.LBNHS,0,35,18)		;POINTER TO NEXT ENTRY IN HASH CHAIN
DEFSTR(.LBLEN,1,17,18)		;LENGTH IN WORDS OF THIS LOCK-BLOCK
DEFSTR(.LBFLG,1,35,12)		;FLAGS
DEFSTR(.LBLQ,2,17,18)		;POINTER TO LAST ENTRY IN QUEUE
DEFSTR(.LBNQ,2,35,18)		;POINTER TO 1ST ENTRY IN QUEUE
DEFSTR(.LBLVL,3,17,18)		;LEVEL NUMBER OF THIS RESOURCE
DEFSTR(.LBACC,3,35,18)		;ACCESS TABLE ADDRESS/-2/-3/400000+JOB #
DEFSTR(.LBPUL,4,17,18)		;NUMBER OF RESOURCES IN POOL
DEFSTR(.LBAVL,4,35,18)		;NUMBER OF RESOURCES AVAILABLE
DEFSTR(.LBPWD,4,35,36)		;ENTIRE POOLED RESOURCE WORD
DEFSTR(.LBTIM,5,35,36)		;TIME-STAMP WHEN LOCK WAS LAST LOCKED
DEFSTR(.LBTLN,6,17,18)		;LENGTH OF LOCK-ASSOCIATED TABLE
DEFSTR(.LBTBL,6,35,18)		;ADDRESS OF LOCK-ASSOCIATED TABLE
DEFSTR(.LBNMS,7,17,18)		;LENGTH OF MASK BLOCK FOR THIS LOCK
DEFSTR(.LBPLT,7,22,5)		;PERMANENT LOCK TIMER
DEFSTR(.LBTXT,10,35,36)		;USER CODE/TEXT STRING
LBSIZE==10			;SIZE OF LOCK-BLOCK (MINUS TEXT)
				;IDLE
O.TEXT==10			;OFFSET OF START OF TEXT (SEE LB.TXT)


	SUBTTL	PARAMETER DEFINITIONS


;ENQ FUNCTION CODES:
.ENQBL==0	;ENQ BLOCK OPTION
.ENQAA==1	;ENQ ALLOCATE ONLY IF AVAILABLE
.ENQSI==2	;ENQ SOFTWARE INTERRUPT OFTION
.ENQMA==3	;ENQ MODIFY ACCESS


;DEQ FUNCTION CODES
.DEQDR==0	;DEQ RESOURCE
.DEQDA==1	;DEQ ALL
.DEQID==2	;DEQ REQUEST I.D.


;ENQC FUNCTION CODES:
.ENQCS==0	;RETURN STATUS
.ENQCG==1	;GET USER'S ENQ QUOTA
.ENQCC==2	;CHANGE USER'S ENQ QUOTA
.ENQCD==3	;DUMP DATA BASE



;MAXIMUM FUNCTION CODES ALLOWED
EQMXFC==3	;HIGHEST ENQ FUNCTION CODE
DQMXFC==2	;HIGHEST DEQ CODE
QCMXFC==3	;ENQC MAX CODE



;FLAGS DEFINED IN LEFT HALF OF 1ST WORD OF USER LOCK SPEC
EN%SHR==(1B0)	;THIS LOCK IS SHARABLE
EN%BLN==(1B1)	;BYPASS LEVEL NUMBER SEQUENCING
EN%LTL==(1B2)	;LONG TERM LOCK
EN%NDR==(1B3)	;NOT DEQUEUED ON RESET
EN%ABT==(1B4)	;PROGRAM SAID THIS LOCK HAS BEEN ABORTED
EN%DED==(1B5)	;DEADLOCK DETECTION
EN%UCW==(1B6)	;USER CODE WORD INSTEAD OF STRING POINTER

;FLAGS RETURNED TO USER IN LEFT HALF OF STATUS WORD FOR ENQC. UUO:
EN%QCE==(1B0)	;ERROR OCCURED 
EN%QCO==(1B1)	;THIS USER IS THE OWNER
EN%QCQ==(1B2)	;THIS USER HAS ISSUED AN ENQ FOR THIS RESOURCE
EN%QCX==(1B3)	;OWNER HAS EXCLUSIVE ACCESS

;FLAGS RETURNED ON DATA-BASE DUMP OPTION OF ENQC.:
EN%QCL==(1B0)	;THIS IS A LOCK BLOCK DUMP
EN%QCT==(1B2)	;THIS LOCK HAS TEXT
EN%QCB==(1B4)	;THIS PROCESS IS BLOCKED
EN%QCN==(1B5)	;THIS LOCK IS NOT DEQUEUED ON RESET
EN%QCA==(1B6)	;THIS LOCK IS ABORTED (NO FURTHER REQUESTS GRANTED)
EN%QCI==(1B7)	;THIS QUEUE BLOCK IS INVISIBLE
EN%QCD==(1B8)	;THIS QUEUE BLOCK WILL BE CHECKED FOR DEADLOCK


;SOME VARIOUS DEFINITIONS
HDRMAX==3	;MAXIMUM SIZE OF HEADER
LKMIN==2	;MINIMUM SIZE OF LOCK
LKMAX==5	;MAXIMUM SIZE OF LOCK
EQMXCH==:^D30	;MAXIMUM STRING SIZE IN WORDS
EQDFEQ==:^D100	;DEFAULT ENQ QUOTA
EQMXMW==:17	;MAXIMUM PIE SLICE MASK WORD BLOCK LENGTH
EQMXTB==:^D512	;MAXIMUM LOCK-ASSOCIATED TABLE LENGTH
EQMLTL==:^D5	;MINUTES LONG TERM LOCKS STAY AROUND
EQMXAQ==:^D500	;MAXIMUM NUMBER OF ACTIVE QUEUES

CODMIN==400000	;MINIMUM VALUE OF A SPECIAL CODE

;FLAGS IN Q-BLOCK
QBOWNR==1B35	;THIS IS THE LOCK OWNER
QBEXCL==1B34	;EXCLUSIVE REQUEST
QBPSI==1B33	;USER WANTS INTERRUPT (CODE WAS 2)
QBWAIT==1B32	;USER WANTS TO BLOCK (CODE WAS 0)
QBINVS==1B31	;THIS ENTRY IS "INVISIBLE"
QBLKBK==1B30	;THIS IS A LOCK-BLOCK (ALWAYS ZERO)
QBDEAD==1B29	;CHECK FOR DEADLOCK
QBLNDR==1B28	;NOT DEQ'D ON RESET
QBLABT==1B27	;ABORTED RESOURCE


;FLAGS IN LOCK-BLOCK
LBLKBK==1B30	;THIS IS A LOCK-BLOCK (Q-BLOCKS MUST HAVE THIS BIT ZERO)
LBLAUC==1B26	;READ COUNT INCREMENTED IN THE ACCESS TABLE
LBLLTL==1B25	;LONG TERM LOCK
LBTEXT==1B24	;THIS BLOCK HAS TEXT INSTEAD OF USER CODE

	SUBTTL	ERROR CODES RETURNED TO USER

ENQRU%==1	;SOME RESOURCE(S) REQUESTED WERE UNAVAILABLE
ENQBP%==2	;ILLEGAL # OF RESOURCES REQUESTED (POOLED RESOURCES)
ENQBJ%==3	;BAD JOB NUMBER
ENQBB%==4	;BAD BYTE SIZE IN TEXT STRING
ENQST%==5	;STRING TOO LONG
ENQBF%==6	;BAD FUNCTION CODE
ENQBL%==7	;ILLEGAL ARGUMENT BLOCK LENGTH
ENQIC%==10	;ILLEGAL NUMBER OF LOCKS SPECIFIED
ENQBC%==11	;BAD CHANNEL NUMBER
ENQPI%==12	;OPERATOR/JACCT PRIVILEGE REQUIRED
ENQNC%==13	;NO CORE AVAILABLE
ENQFN%==14	;FILE NOT OPEN ON SPECIFIED CHANNEL, OR DEVICE NOT A DISK
ENQIN%==15	;INDIRECT OR INDEXED BYTE POINTER NOT ALLOWED
ENQNO%==16	;NO RESOURCES WERE OWNED
ENQLS%==17	;LEVEL SEQUENCING ERROR (LEVEL # TOO LOW)
ENQCC%==20	;CAN'T CHANGE ACCESS
ENQQE%==21	;QUOTA EXCEEDED
ENQPD%==22	;# OF RESOURCES IN POOL NOT SAME AS IN LOCK
ENQDR%==23	;DUPLICATE REQUEST FOR RESOURCE (LOCK ALREADY REQUESTED)
ENQNE%==24	;NOT ENQ'ED ON THIS LOCK
ENQLD%==25	;LEVEL # IN REQUEST DOES NOT MATCH LOCK
ENQED%==26	;ENQ/DEQ PRIVILEGES REQUIRED
ENQME%==27	;MASK IS TOO LONG, OR LENGTHS DO NOT MATCH.
ENQTE%==30	;ENQ. TABLE IS TOO LONG
ENQAB%==31	;ATTEMPT TO ENQ. AN ABORTED LOCK
ENQGF%==32	;ATTEMPT TO LOCK WITH NDR ON A 'GHOST FILE'
ENQDD%==33	;DEADLOCK DETECTED
ENQTL%==34	;TIME LIMIT EXCEEDED
	SUBTTL	ENQ--ENQ A RESOURCE REQUEST
;
;
;


ENQ::	PUSHJ	P,SAVE4##	;SAVE P1-P4
	SETZ	P4,		;CLEAR LIST OF REQUESTS
	AOS	%ENQTE##	;BUMP TOTAL NUMBER OF ENQ'S
	SETOM	ENQFLG		;FLAG THAT THIS IS AN ENQ
	MOVEI	T2,EQMXFC	;SET UP MAX FUNCTION CODE
	PUSHJ	P,SETUP		;DO PRELIMINARY THINGS
	  POPJ	P,		;ERROR, GIVE ERROR CODE
	PUSHJ	P,CHKBLK	;CHECK OUT THE PARAMETER BLOCK
	  JRST	STOTAC##	;  ERROR RETURN
IFN FTMP,<
	PUSHJ	P,EQLOCK	;GET THE EQ RESOURCE
>
	HRRZ	T1,FCODE	;GET SAVED FUNCTION CODE
	CAIE	T1,.ENQMA	;IS THIS A "MODIFY ACCESS"?
	PUSHJ	P,PRVJC##	;OR, IS THIS GUY PRIVILEGED?
 	  JRST	ENQ1		;YES, DON'T CHECK QUOTAS FOR HIM
	HLRZ	T1,LOCKCT	;GET # OF LOCKS IN THIS REQUEST
	MOVE	T2,.PDEQQ##(W)	;AND QUOTA OF SAME
	TLON	T2,EQ.HBS##	;QUOTA ALREADY BEEN SET?
	HRR	T2,%ENQDF##	;NO, SET DEFAULT QUOTA
	MOVEM	T2,.PDEQQ##(W)	;STORE QUOTA BACK AGAIN
	ADD	T1,REQCNT	;ADD # OF OUTSTANDING LOCKS
	CAILE	T1,(T2)		;TOO MANY REQUESTS?
	JRST	ENQER3		;YES, ABORT UUO

ENQ1:	PUSHJ	P,GETLOK	;GET A LOCK SPEC
	PUSHJ	P,ENQIT		;PERFORM APPROPRIATE FUNCTION
	  JRST	BACKUP		;ERROR--UNWIND PREVIOUS STUFF
	PUSHJ	P,CHKCNT	;MORE LOCKS TO GO?
	  JRST	ENQ1		;YES, CYCLE BACK FOR MORE
	HRRZ	T2,FCODE	;GET STORED FUNCTION CODE
	CAIE	T2,.ENQMA	;IS IT "MODIFY ACCESS"?
	JRST	ENQ2		;NO, FINISH UP
	SKIPN	T1,NQERRF	;YES, WAS THERE AN ERROR?
	JRST	CPOPJ1##	;NO, GIVE SKIP RETURN
	JRST	STOTAC##	;RETURN ERROR CODE TO USER

;COME HERE WHEN ALL ENQ REQUESTS HAVE BEEN PERFORMED

ENQ2:	HLRZ	T1,P4		;GET SAVED ADDRESS OF LAST Q
	PUSHJ	P,QSKD		;SCHEDULE IT
	  JRST	ENQFAI		;NOT ALL WERE FREE
ENQ3:	HLLZS	LOCKCT		;RESET LOCK ARGUMENT COUNTER
FILTBL:	PUSHJ	P,GETLOK	;GET A LOCK ARGUMENT
	SKIPE	EQLTBL		;IS THERE A TABLE TO FILL?
	PUSHJ	P,REDTBL	;YES, GO FILL IT FOR HIM
	PUSHJ	P,CHKCNT	;ANY OTHER LOCKS TO CHECK?
	  JRST	FILTBL		;YES, GET NEXT
	JRST	GUDXIT		;EVERYTHING WAS OK

;COME HERE IF ONE OR MORE OF THE RESOURCES WERE BUSY

ENQFAI:	HLRZ	T1,P4		;GET ADDRESS OF LAST ENQ'ED BLOCK
	LOAD.	T2,.QBFLG,(T1)	; AND IT'S FLAGS
	TRNN	T2,QBPSI	;IF USING PSI, IGNORE TIME LIMIT (TEMP)
	SKIPN	TIMLIM		;IF TIME LIMIT, POSTPONE DEADLOCK CHECK
	TRNN	T2,QBDEAD	;SHOULD DEADLOCKS BE DETECTED?
	JRST	ENQFA2		;NO, USER DOESN'T CARE
	PUSHJ	P,DEADLK	;CHECK TO SEE IF DEADLOCK
	  JRST	ENQDED		;YES, GO CLEAN UP
	HLRZ	T1,P4		;GET ADDRESS OF LAST ENQ'ED BLOCK
	LOAD.	T2,.QBFLG,(T1)	; AND IT'S FLAGS
ENQFA2:	TRNE	T2,QBWAIT	;SHOULD HE BE BLOCKED?
	JRST	BLKHIM		;YES, GO BLOCK HIM
	MOVEI	T1,ENQRU%	;SET UP ERROR CODE
	TRNN	T2,QBPSI	;IS HE USING SOFT. INTERRUPT SYS?
	JRST	BACKUP		;NO, WE MUST RELEASE HIS REQUEST
	JRST	STOTAC##	;NO, JUST RETURN AN ERROR CODE TO HIM
;HERE TO BLOCK USER ON A FUNCTION CODE OF "0"

BLKHIM:	
	PUSH	P,P4		;SAVE POINTER TO LAST Q-BLOCK
	PUSH	P,NQERRF	;SAVE ERROR FLAG
	PUSH	P,REQCNT
	PUSH	P,LOKSIZ
	PUSH	P,HDRSIZ
	PUSH	P,RBLOCK
	PUSH	P,FCODE
	PUSH	P,LOCKCT
	PUSH	P,REQID
	PUSH	P,TIMLIM
	PUSH	P,HILEVL
IFN FTMP,<
	PUSHJ	P,DWNEQ		;FREE EQ RESOURCE OVER HIBER CALL
>
	MOVSI	T1,HBRSEC##	;TIME LIMIT IS IN SECONDS
	HRR	T1,TIMLIM	;TELL HIBER HOW LONG TO WAIT
	PUSHJ	P,HIBER##	;HIBERNATE HIM
	  JFCL
IFN FTMP,<
	PUSHJ	P,UPEQ		;GET THE EQ RESOURCE BACK
>
	POP	P,HILEVL
	POP	P,TIMLIM
	POP	P,REQID
	POP	P,LOCKCT
	POP	P,FCODE
	POP	P,RBLOCK
	POP	P,HDRSIZ
	POP	P,LOKSIZ
	POP	P,REQCNT
	POP	P,NQERRF	;GET ERROR FLAG BACK
	POP	P,P4		;GET BACK P4
	HLRZ	T1,P4		;GET SAVED ADDRESS OF LAST Q
	LOAD.	T1,.QBLB,(T1)	;GET ADDRESS OF LOCK BLOCK
	PUSHJ	P,CHKABT	;WAS THIS LOCK ABORTED?
	  JRST	[MOVEI T1,ENQAB%	;YES, SET UP ERROR CODE
		JRST BACKUP]		;AND RELEASE THE REQUEST
	HLRZ	T1,P4		;GET SAVED ADDRESS OF LAST Q
	PUSHJ	P,QSKD		;SCHEDULE IT
	 CAIA			;NOT ALL REQUESTS WERE GRANTED
	JRST	ENQ3		;ALL LOCKED
	JUMPN	T1,ENQFAI	;WAIT SOME MORE IF ANYTHING GRANTED
	SKIPN	TIMLIM		;WAS A TIME LIMIT SPECIFIED?
	JRST	ENQFAI		;NO, SO WE CAN'T TIMEOUT
	PUSHJ	P,DEADLK	;SEE IF TIMEOUT DUE TO DEADLOCK
	  JRST	ENQDED		;YES, COUNT AND RELEASE REQUEST
	AOS	%ENQTO##	;COUNT TIMEOUTS
	MOVEI	T1,ENQTL%	;SET UP ERROR CODE
	JRST	BACKUP		;AND RELEASE THE REQUEST

ENQDED:	AOS	%ENQDD##	;COUNT DEADLOCKS DETECTED
	MOVEI	T1,ENQDD%	;SET UP ERROR CODE
	JRST	BACKUP		;AND RELEASE THE REQUEST
;ROUTINE TO FILL A USER TABLE WITH DATA FROM THE LOCK-ASSOCIATED TABLE

REDTBL:	PUSHJ	P,FNDLOK	;FIND THE LOCK BLOCK
	  STOPCD CPOPJ##,DEBUG,ENQLNF	;++LOCK NOT FOUND
	HLRZ	T2,EQLTBL	;LENGTH OF USER'S DATA BLOCK
	LOAD.	T3,.LBTLN,(T1)	;LENGTH OF STORED DATA
	CAMG	T2,T3		;IS THE USERS TABLE LONGER?
	JRST	REDTB1		;NO, JUST COPY THE DATA
	HRRZ	T4,EQLTBL	;POINTER TO USER'S ARGUMENT BLOCK
	EXCTXU	<SETZM (T4)>	;CLEAR FIRST WORD FOR HIM
	ADDI	T2,-1(T4)	;LAST WORD OF BLOCK
	HRL	T4,T4		;SETUP SOURCE AT START OF BLOCK
	ADDI	T4,1		;MAKE PROPAGATING POINTER
	CAIL	T2,(T4)		;ONE WORD BLOCK?
	EXCTUU	<BLT T4,(T2)>	;ZERO BLOCK TO ENSURE ZERO FILL
	MOVEI	T2,(T3)		;COPY ONLY REAL DATA
REDTB1:	JUMPE	T2,CPOPJ##	;IF ZERO LENGTH, JUST RETURN
	LOAD.	T3,.LBTBL,(T1)	;GET ADDRESS OF LOCK-ASSOCIATED TABLE
	HRLZ	T3,T3		;MAKE THIS THE SOURCE ADDRESS FOR BLT
	HRR	T3,EQLTBL	;AND USERS TABLE IS THE DESTINATION
	ADDI	T2,-1(T3)	;LAST WORD TO TRANSFER
	EXCTXU	<BLT T3,(T2)>	;COPY THE DATA FOR HIM
	POPJ	P,		;AND RETURN
;COME HERE IF AN ENQ REQUEST FAILED FOR SOME REASON
; THE ENTIRE CHAIN OF REQUESTS (IF ANY) MUST
; BE REMOVED FROM THE QUEUES. THE POINTER TO THE
; LAST MEMBER OF THE CHAIN IS KEPT IN THE
; LEFT HALF OF P4.
;
;ON ENTRY, THE ERROR CODE IS IN T1
BACKUP:	HLRZ	T3,P4		;GET ADDRESS OF LAST Q'ED ENTRY
	JUMPE	T3,STOTAC##	;THERE WAS NONE--EXIT
	MOVEM	T1,NQERRF	;SAVE ERROR CODE
	SKIPA	T1,T3		;MOVE THIS ADDRESS TO USE AS PTR
BACK2:	MOVE	T1,T2		;GET ADDRESS OF LAST BLOCK
	LOAD.	T2,.QBLQR,(T1)	;GET PREVIOUS Q-BLOCK'S LOCATION
	PUSHJ	P,DEQIT		;REMOVE THE CURRENT BLOCK
	  JFCL			;DON'T CARE IF Q IS FLUSHED
	CAIE	T2,(T1)		;HAVE WE GONE IN CIRCLE?
	JRST	BACK2		;NO
	MOVE	T1,NQERRF	;YES, GET ERROR CODE
	JRST	STOTAC##	;GIVE HIM ERROR CODE
	SUBTTL	SUBROUTINE TO PERFORM AN ENQ FUNCTION

;SUBROUTINE TO PERFORM ONE ENQ FUNCTION FOR A GIVEN RESOURCE.
;THIS ROUTINE (EXCEPT IN THE CASE OF A "MODIFY ACCESS")
; CHECKS THE LEVEL #, POOL COUNT, AND WHETHER THIS
; USER ALREADY HAS AN ENTRY QUEUED. IF ALL THESE CHECKS
; ARE OK, THE USER IS QUEUED FOR THE RESOURCE.
;
;ENTER:
;	P1-P4 SET UP BY GETLOK
;	PUSHJ	P,ENQIT
;RETURNS:
;	+1	ERROR RETURN
;	+2	NORMAL RETURN
;USES T1-T4

ENQIT:	HRRZ	T2,FCODE	;GET SAVED FUNCTION CODE
	CAIN	T2,.ENQMA	;IS IT A MODIFY ACCESS?
	JRST	DOFC3		;YES, DO SPECIAL STUFF
	LDB	T2,[POINT 9,P1,17]	;GET LEVEL #
	HRRE	T3,HILEVL	; AND THE STORED HIGHEST LEVEL #
	HRRZ	T4,P1		;GET TYPE OF REQUEST
	CAIN	T4,-3		;IS THIS A MONITOR REQUEST?
	HLRE	T3,HILEVL	;YES, USE HIGHEST MONITOR LEVEL #
	CAMLE	T2,T3		;IS THE LEVEL NUMBER OK?
	JRST	ENQIT1		;YES
	TLNN	P1,EN%BLN	;NO, DOES HE WANT TO BYPASS IT?
	JRST	ENQER1		;NO, FLUSH HIM
	MOVEI	T2,ENQLS%	;YES, REMEMBER THAT THIS HAPPENED
	MOVEM	T2,NQERRF	;  IN THE ERROR FLAG
ENQIT1:	PUSHJ	P,LOCKOK	;CHECK OTHER THINGS ABOUT THE LOCK
	  POPJ	P,		; ERROR FOUND
	HRRE	T2,P1		;GET CHANNEL #
	JUMPL	T2,ENQIT2	;NOT A CHANNEL NUMBER
	MOVE	T3,DEVMOD(F)	;AND DEVICE BITS
	TLNE	T3,DVDSK	;IS THIS A DISK?
	TLNN	F,LOOKB+ENTRB	; AND HAS THE FILE BEEN 'LOOKED-UP'...
	JRST	HDRER9		;NO, ERROR
ENQIT2:	PUSHJ	P,CHPRIV	;DOES THIS GUY HAVE THE PRIVILEGES?
	  POPJ	P,		;NO, ERROR
	PUSHJ	P,FNDLOK	;DOES LOCK EXIST?
	  JRST	ENQIT4		;NO, WE MUST CREATE IT

;CONTINUED ON THE NEXT PAGE
;CONTINUED FROM THE PREVIOUS PAGE

	PUSHJ	P,CHKABT	;HAS THIS LOCK BEEN ABORTED
	  JRST	ABTERR		;YES, INFORM THE NEW REQUESTER
	LOAD.	T2,.LBLVL,(T1)	;GET LEVEL #
	LDB	T3,[POINT 9,P1,17]	;AND NUMBER FOR NEW REQUEST
	TLNE	P1,EN%BLN	;DON'T CHECK IF HE DOESN'T CARE
	JRST	ENQIT3		;SKIP OVER CHECK
	CAIE	T2,(T3)		; SAME?
	JRST	DOERR0		;NO, ERROR RETURN
ENQIT3:	HLRZ	T3,P3		;GET # IN POOL
	LOAD.	T2,.LBPUL,(T1)	; AND CORRECT # IN POOL
	CAIE	T2,(T3)		;AND THEY THE SAME?
	JRST	DOERR2		;NO, POOL REQUEST ERROR
	PUSH	P,T1
	MOVE	T2,.CPJCH##
	PUSHJ	P,CHECKQ	;IS THIS GUY IN THE Q?
	JRST	[POP P,T1
		 JRST DOERR3]
	POP	P,T1
	JRST	ENQIT5		;NO, GO PUT HIM IN IT
; HERE IF NEW LOCK

ENQIT4:	MOVE	T1,EQTMP4	;GET HASH BACK AGAIN
	MOVEI	T2,LBLKBK	;FLAGS="LOCK-BLOCK"
	TLNE	P1,EN%UCW	;CODE WORD?
	JRST	ENQIT7		;YES, DON'T SET TEXT FLAG
	LDB	T3,[POINT 3,P2,2]
	CAIE	T3,5		;IS THIS A USER CODE?
	TRO	T2,LBTEXT	;NO, SET "TEXT" FLAG IN LOCK BLOCK
ENQIT7:	TLNE	P1,EN%LTL	;USER WANTS THIS ONE TO STAY AROUND?
	TRO	T2,LBLLTL	;YES, SET 'LONG TERM LOCK' BIT
	PUSHJ	P,BLDLOK	;BUILD A LOCK BLOCK
	  POPJ	P,		;ERROR (NO CORE,BAD STRING,ETC.)
	MOVEM	T1,LSTLOK	;REMEMBER LAST LOCK ADDRESS
	AOS	%ENQNQ##	;BUMP THE NUMBER OF QUEUES
	TLNE	P3,-1		;IF THIS IS A POOLED RESOURCE..
	AOS	%ENQNP##	;  BUMP THE TOTAL OF THEM TOO
ENQIT5:	SETZ	T3,
	SKIPL	P1		;IS THE "SHARED" BIT ON?
	MOVEI	T3,QBEXCL	;NO, SET EXCLUSIVE BIT IN BLOCK
	TLNN	P1,EN%NDR
	JRST	ENQIT6
	TRO	T3,QBLNDR
	PUSHJ	P,OKNDR		;OK TO NDR LOCK?
	  JRST	STOTAC		;GIVE ERROR RETURN
	PUSH	P,T1		;SAVE LOCK BLOCK ADDRESS
	LOAD.	T1,.LBACC,(T1)	;GET THE ACCESS-TABLE ADDRESS
	CAIGE	T1,CODMIN	;IS IT REALLY A SPECIAL CODE?
	PUSHJ	P,FILNDR##	;NO, MARK FILE AS NDR
	POP	P,T1		;GET BACK LOCK BLOCK ADDRESS
ENQIT6:	MOVE	T4,FCODE	;GET STORED FUNCTION CODE
	TDO	T3,[EXP QBWAIT,0,QBPSI](T4) ;SET BIT IN FLAG WORD
	TLNE	P1,EN%ABT	;ABORT REQUESTED?
	TRO	T3,QBLABT	;YES, REMEMBER IT
	TLNE	P1,EN%DED	;DEADLOCK DETECTION REQUESTED?
	TRO	T3,QBDEAD	;YES, REMEMBER IN QUEUE BLOCK
	HLRZ	T2,P4		;GET MULTIPLE REQUEST ADDRESS
	PUSHJ	P,QHIM		;PUT HIM ON Q
	  JRST	DOERR6		;NO MORE CORE LEFT
	MOVEM	T1,LASTQ	;REMEMBER LAST Q-BLOCK ADDRESS
	PUSHJ	P,BLDMSK	;BUILD THE MASK BLOCK FOR HIM
	  POPJ	P,		;NO CORE OR MISMATCHED LENGTHS
	HRL	P4,T1		;SAVE ADDRESS OF THIS ENTRY
	PJRST	CPOPJ1##	;SKIP RETURN
;COME HERE IF USER WANTS TO MODIFY ACCESS

DOFC3:	PUSHJ	P,LOCKOK	;IS THE LOCK SPEC OK?
	  JRST	MARETN		;NO, ERROR FOUND
	PUSHJ	P,FNDLOK	;DOES THE LOCK EXIST?
	  JRST	DOERR4		;NO, GIVE USER ERROR
	MOVE	T2,.CPJCH##
	PUSHJ	P,CHECKQ	;IS HE ALREADY IN Q?
	  SKIPA			;YES
	JRST	DOERR4		;NO, FLAG IT AS ERROR
	LOAD.	T2,.QBFLG,(T1)	;FETCH FLAGS
	TRNN	T2,QBOWNR	;IS HE THE OWNER
	JRST	DOFC3B		;NO
	TRNN	T2,QBEXCL	;EXCLUSIVE?
	JRST	DOFC3A		;NO
;OWNER/EXCLUSIVE
	TLNN	P1,EN%SHR	;HE WANTS IT SHARABLE NOW?
	JRST	DOFC3E		;NO, NO CHANGE
	TRZ	T2,QBEXCL	;CLEAR EXCLUSIVE
	JRST	DOFC3D		;AND STORE FLAGS

;OWNER/SHARED
DOFC3A:	TLNE	P1,EN%SHR	;HE WANTS IT EXCLUSIVE NOW?
	JRST	DOFC3E		;NO, NO CHANGE
	PUSHJ	P,CKONLY	;ONLY IF HE'S THE ONLY ONE
	  JRST	DOERR5		;SOMEONE ELSE IS SHARING ALSO
	TRO	T2,QBEXCL	;OKAY, SET EXCLUSIVE
	JRST	DOFC3D		;AND STORE UPDATED FLAGS

;HERE IF A NON-OWNER
DOFC3B:	TRNN	T2,QBEXCL	;EXCLUSIVE
	JRST	DOFC3C		;NO
;NON-OWNER/EXCLUSIVE
	TLNN	P1,EN%SHR	;WANTS IT SHARABLE NOW?
	JRST	DOFC3E		;NO, NO CHANGE
	TRZ	T2,QBEXCL	;CLEAR EXCLUSIVE
	JRST	DOFC3D		;AND STORE FLAGS

;NON-OWNER/SHARED
DOFC3C:	TLNE	P1,EN%SHR	;WANTS IT EXCLUSIVE NOW?
	JRST	DOFC3E		;NO, NO CHANGE
	TRO	T2,QBEXCL	;SET EXCLUSIVE
DOFC3D:	STOR.	T2,.QBFLG,(T1)	;STORE UPDATED FLAGS
DOFC3E:	

;CONTINUED ON THE NEXT PAGE
;CONTINUED FROM THE PREVIOUS PAGE

;CHECK HERE FOR SETTING/CLEARING THE ABORT STATUS IN THE QUEUE
;  BLOCK

	TLNN	P1,EN%ABT	;CHANGE TO ABORT STATUS?
	JRST	[TRNE	T2,QBOWNR	;NO, OWNING QUEUE BLOCK?
		 TRZ	T2,QBLABT	;YES, CLEAR ABORT STATUS
		 JRST	DOFC3F]
	TRNN	T2,QBOWNR	;OWNING QUEUE BLOCK?
	JRST	DERNJQ		;NO, ERROR
	TRO	T2,QBLABT	;SET ABORT FLAG
DOFC3F:	STOR.	T2,.QBFLG,(T1)	;RESTORE FLAGS IN Q-BLOCK

;CHECK HERE FOR SETTING/CLEARING THE NO DEQUEUE ON RESET BIT

				;T1-T4 ALREADY SET UP ABOVE
	TLNN	P1,EN%NDR	;CLEAR NO DEQUEUE ON RESET?
	JRST	[TRNE	T2,QBOWNR	;OWNING QUEUE BLOCK?
		 TRZ	T2,QBLNDR	;YES, CLEAR NO DEQUEUE ON RESET
		 JRST	DOFC3G]
	TRNN	T2,QBOWNR	;OWNING QUEUE BLOCK?
	JRST	DERNJQ		;NO, ERROR
	MOVE	T3,T1		;SAVE Q-BLOCK ADDRESS
	LOAD.	T1,.QBLB,(T1)	;LOCK BLOCK ADDRESS
	PUSHJ	P,OKNDR
	  JRST	STOTAC		;GIVE ERROR RETURN
	LOAD.	T1,.LBACC,(T1)	;GET ACCESS TABLE ADDRESS
	CAIGE	T1,CODMIN	;IS IT REALLY A SPECIAL CODE?
	PUSHJ	P,FILNDR##	;NO, MARK THE FILE AS NDR
	MOVE	T1,T3		;GET BACK Q-BLOCK ADDRESS
	TRO	T2,QBLNDR	;SET NO DEQUEUE ON RESET
DOFC3G:	STOR.	T2,.QBFLG,(T1)	;RESETORE FLAGS IN Q-BLOCK
	LOAD.	T1,.QBLB,(T1)	;GET LOCK BLOCK ADDRESS
	PUSHJ	P,LOKSKD	;SCHEDULE THIS LOCK
	PJRST	CPOPJ1##	;RETURN FROM "ENQIT"
;ROUTINE TO CHECK FOR SOLE OWNERSHIP OF A SHARABLE LOCK

;CALL WITH T1 CONTAINING THE ADDRESS OF A QUEUE-BLOCK
;	PUSHJ	P,CKONLY
;	  <IF OTHER OWNERS ALSO>
;	<IF SOLE OWNER>


CKONLY:	PUSHJ	P,SAVT##	;PRESERVES ALL AC'S
	MOVEI	T3,(T1)		;INITIALIZE CURRENT BLOCK POINTER
CKONL1:	LOAD.	T3,.QBNQ,(T3)	;NEXT REQUEST
	CAIN	T3,(T1)		;BACK TO SELF?
	JRST	CPOPJ1##	;YES, GOOD RETURN
	LOAD.	T2,.QBFLG,(T3)	;FLAG BITS
	TRNN	T2,QBLKBK	;IF THIS IS THE LOCK BLOCK, IGNORE IT
	TRNN	T2,QBOWNR	;IS THIS ONE AN OWNER?
	JRST	CKONL1		;NO, LOOP OVER WHOLE LIST
	PUSHJ	P,CKOMSK	;CHECK FOR NON-CONFLICTING MASKS
	  POPJ	P,		;MATCH ON MASKS
	JRST 	CKONL1		;NO MATCH


;CKOMSK -- ROUTINE FOR CKONLY TO CHECK FOR MULTI-RESOURCE
;	   MASKS.

CKOMSK:	PUSHJ	P,SAVE3##	;SAVE P1-P3
	LOAD.	P1,.QBMSK,(T1)	;MASK ADDRESS
	JUMPE	P1,CPOPJ	;NO MASK IMPLIES ALL.  MUST CONFLICT
	LOAD.	P2,.QBMSK,(T3)	;MASK ADDRESS
	JUMPE	P2,CPOPJ	;NO MASK IMPLIES ALL.  MUST CONFLICT
	TLO	P1,(<POINT 36,>) ;MAKE A BYTE POINTER TO THE MASK
CKOMS1:	LDB	T2,P1		;WORD FROM MASK 1
	AND	T2,(P2)		;AND WITH WORD OF MASK 2
	JUMPN	T2,CPOPJ	;FAIL IF ANY MATCHES
	SOJLE	P3,CPOPJ1	;IF END OF BLOCK.  NO CONFLICT
	AOJA	P2,CKOMS1	;NOT END.  LOOP OVER WHOLE MASK STRING
	SUBTTL	DEQ--DEQ A RESOURCE REQUEST

DEQ::	PUSHJ	P,SAVE4##	;SAVE P1-P4
	AOS	%ENQTD##	;BUMP TOTAL OF DEQ'S SINCE RELOAD
	SETZM	ENQFLG		;THIS IS NOT AN ENQ.
	MOVEI	T2,DQMXFC	;SET MAX FUNCTION CODE
	PUSHJ	P,SETUP		;DO PRELIMINARY THINGS
	  POPJ	P,		;ERROR RETURN
	JRST	@[EXP DEQFC0,DEQFC1,DEQFC1](T3)

DEQFC1:	PUSHJ	P,FNDPDS##	;GET THIS GUY'S PDB
IFN FTMP,<
	PUSHJ	P,EQLOCK	;KEEPS OTHERS OUT OF THE DATABASE
>
	HRRZ	T1,.PDEQJ##(W)	;GET START OF JOB Q
	JUMPE	T1,DERNJQ	;EXIT IF NO JOB Q
	HRRZ	T3,FCODE	;GET FUNCTION
	SETZM	DQFLAG		;ASSUME NO LOCKS
DEQ0:	LOAD.	T2,.QBNJQ,(T1)	;GET NEXT JOB IN Q POINTER
	LOAD.	T4,.QBRID,(T1)	;GET REQUEST ID
	CAME	T4,RBLOCK	;IF SAME AND CODE=2, DEQ
	CAIN	T3,.DEQDA	; OR IF CODE=1, THEN DEQ
	PUSHJ	P,DEQIT		;REMOVE CURRENT ONE FROM Q
	MOVE	T1,T2		;GET NEXT Q-ENTRY
	JUMPN	T2,DEQ0		;KEEP GOING IF THERE IS ONE..
	SKIPE	DQFLAG		;DID U DEQ ANYTHING?
	JRST	CPOPJ1##	;SKIP RETURN
	JRST	DERNJQ		;NO, ERROR
;COME HERE FOR A STANDARD DEQ (FUNCTION CODE=0)
; ENTIRE RESOURCE BLOCK MUST BE SCANNED
; AND CHECKED AND EACH LOCK MUST BE DEQ'ED.

DEQFC0:	PUSHJ	P,CHKBLK	;CHECK OUT THE USER'S PARAMETER BLOCK
	  JRST STOTAC##		;  ERROR

IFN FTMP,<
	PUSHJ	P,EQLOCK	;INTERLOCK ON EQ
>
FC0.2:	PUSHJ	P,GETLOK	;GET A NEW LOCK
	PUSHJ	P,LOCKOK	;IS THE LOCK SPEC OK?
	  JRST	FC0ERR		;NO
	PUSHJ	P,CHPRIV	;ARE THE PRIVILEGES OK?
	  JRST	FC0ERR		;NO
	PUSHJ	P,FNDLOK	;FIND THIS LOCK IN DATA BASE
	  JRST	FCERR1		;DOESN'T EXIST
	MOVE	T2,.CPJCH##
	PUSHJ	P,CHECKQ	;IS HE IN THE Q?
	  SKIPA			;YES
	JRST	FCERR1		;NO, ITS AN ERROR
	TLNN	P3,-1		;IS THIS A POOLED RESOURCE?
	JRST	FC0.3		;NO
	LOAD.	T2,.QBNRP,(T1)	;GET NUMBER THIS GUY HAS OUT
	CAIN	T2,(P3)		;IS HE RETURNING ALL OF THEM?
	JRST	FC0.3		;YES, HE'S GIVING THEM ALL BACK
	CAIG	T2,(P3)		;IS HE RETURNING TOO MUCH?
	JRST	FCERR0		;YES, DON'T LET HIM DO IT
	SUBI	T2,(P3)		;COMPUTE NUMBER HE STILL HAS
	STOR.	T2,.QBNRP,(T1)	;UPDATE HIS TOTAL
	LOAD.	T1,.QBLB,(T1)	;GET LOCK-BLOCK
	LOAD.	T2,.LBAVL,(T1)	;GET # OF FREE RESOURCES
	ADDI	T2,(P3)		;ADD THOSE HE IS RETURNING
	STOR.	T2,.LBAVL,(T1)	;UPDATE # AVAILABLE
	PUSHJ	P,LOKSKD	;..AND SCHEDULE LOCK-BLOCK
	JRST	FC0.4		;DON'T DEQ THIS Q-BLOCK

FC0.3:	PUSHJ	P,STOTBL	;STORE USER SUPPLIED TABLE INFORMATION
	PUSHJ	P,DEQIT		;DEQ THIS ENTRY
	JRST	FC0.4		;DON'T SET ERROR FLAG

FCERR0:	SKIPA	T1,[ENQBP%]	;TRYING TO GIVE BACK TOO MUCH
FCERR1:	MOVEI	T1,ENQNE%	;NOT ENQ'ED ON THIS LOCK
FC0ERR:	HRRM	T1,NQERRF	;REMEMBER THAT THERE WAS AN ERROR
FC0.4:	PUSHJ	P,CHKCNT	;ARE THERE MORE LOCKS?
	  JRST	FC0.2		;YES, GO BACK
	SKIPN	T1,NQERRF	;WAS THERE AN ERROR?
	JRST	CPOPJ1##	;NO, SKIP RETURN
	JRST	STOTAC##	;GIVE ERROR RETURN
	SUBTTL	DEQ A PARTICULAR Q-ENTRY

;SUBROUTINE TO DEQ ONE Q-ENTRY
;
;CALL:
;	MOVE	T1,Q-ENTRY-ADDRESS
;	PUSHJ	P,DEQIT
;	RETURN HERE ALWAYS
;
;ON RETURN, ALL SCHEDULING FOR THE Q-ENTRY AND THE LOCK-BLOCK
; HAS BEEN DONE
;
;CLOBBERS:	NONE			;DQFLAG IS BUMPED ON EXIT


DEQIT:	AOS	DQFLAG		;FLAG DOING A DEQ. FUNCTION
	PUSHJ	P,SAVT##	;SAVE T-REGISTERS (RESET NEEDS THEM)
IFN FTMP,<
	PUSHJ	P,HAVEQ
	  STOPCD .+1,DEBUG,ENQDNL	;++DEQ NOT INTERLOCKED
>
	LOAD.	T3,.QBNJQ,(T1)	;GET NEXT JOB PTR
	LOAD.	T2,.QBLJQ,(T1)	;AND LAST JOB PTR
	STOR.	T3,.QBNJQ,(T2)	;RELINK JOB Q
	SKIPE	T3		;THIS MAY BE THE END OF THE JOB Q
	STOR.	T2,.QBLJQ,(T3)	;MAKE NEXT ENTRY POINT TO LAST ONE
	LOAD.	T2,.QBLQ,(T1)	;GET LAST Q-PTR
	LOAD.	T3,.QBNQ,(T1)	; AND NEXT Q-ENTRY
	STOR.	T2,.QBLQ,(T3)	;RELINK THESE Q'S
	STOR.	T3,.QBNQ,(T2)	; SAME
	LOAD.	T2,.QBLQR,(T1)	;GET NEXT LINKED REQUEST
	LOAD.	T3,.QBNQR,(T1)	; POINTERS
	STOR.	T2,.QBLQR,(T3)	;RELINK REQUEST Q
	STOR.	T3,.QBNQR,(T2)
	MOVE	T2,T1		;PUT Q-BLOCK ADDRESS IN T2
	LOAD.	T1,.QBLB,(T2)	;GET LOCK BLOCK ADDR
	LOAD.	T3,.LBNQ,(T1)	;GET ADDR OF NEXT Q
	CAIN	T3,(T1)		;IS Q EMPTY?
	JRST	FLUSH		;YES, GO FLUSH IT
	LOAD.	T3,.QBFLG,(T2)	;GET FLAGS AGAIN
	TRNN	T3,QBOWNR	;OWNER?
	JRST	FLSHQ		;NO, GO FLUSH IT
	LOAD.	T3,.QBNRP,(T2)	;YES, GET ALLOCATION
	LOAD.	T4,.LBAVL,(T1)	;AND # AVAILABLE
	ADD	T3,T4		;COMBINE THEM
	STOR.	T3,.LBAVL,(T1)	;PUT BACK IN POOL
	JRST	FLSHQ		;NOW, GO AND FLUSH Q
;COME HERE TO FLUSH A LOCK BLOCK AND A Q BLOCK FROM THE DATA BASE

FLUSH:	LOAD.	T3,.LBPUL,(T1)	;GET NUMBER IN RESOURCE POOL
	SKIPE	T3		;IS IT POOLED?
	SOS	%ENQNP##	;YES, DECREMENT THAT TOTAL
	LOAD.	T3,.LBFLG,(T1)	;GET FLAGS
	TRNE	T3,LBLLTL	;IS THIS A PERMANENT LOCK?
	PJRST	FLUSH1		;YES DON'T DELETE NOW. GO START TIMER
	PUSH	P,T1		;SAVE LOCK BLOCK ADDRESS
	PUSHJ	P,FLSHQ1	;DELETE Q-BLOCK WITHOUT RESCHEDULING
	POP	P,T1		;RESTORE LOCK BLOCK ADDRESS
	PJRST	DLTLOK		;DELETE THE LOCK BLOCK


;HERE IF THE LOCK BLOCK HAS THE LONG-TERM-LOCK BIT SET.  START
;THE TIMER, THEN GO DELETE THE QUEUE BLOCK.

FLUSH1:	MOVE	T3,%ENQLT##	;GET NUMBER OF MINUTES TO WAIT
	STOR.	T3,.LBPLT,(T1)	;PERMANENT LOCK TIMER VALUE
	PJRST	FLSHQ1		;DELETE Q-BLOCK WITHOUT RESCHEDULING


;HERE TO DELETE THE QUEUE BLOCK.   ENTER AT FLSHQ1 TO SKIP RESCHEDULING
;IF THE ASSOCIATED LOCK BLOCK HAS EITHER BEEN DELETED OR NOW HAS
;A NULL QUEUE.

FLSHQ:	PUSHJ	P,RSKED		;DO RE-SCHEDULING FOR THIS Q-BLOCK
FLSHQ1:	PUSHJ	P,DLTMSK	;DELETE MASK BLOCK, IF ANY
	MOVEI	T1,QBSIZE	;SET UP SIZE
	PJRST	GIVWDS##	;FREE UP THIS SPACE NO RETURN


;SUBROUTINE TO DELETE A LOCK BLOCK. 
;CALL:
;	MOVE	T1, LOCK-BLOCK ADDRESS
;	PUSHJ	P,DLTLOK
;	ALWAYS RETURNS HERE
;PRESERVES ALL AC'S

DLTLOK:	PUSHJ	P,SAVT##	;SAVE T1-T4
	PUSHJ	P,DLTTBL	;DELETE LOCK ASSOCIATED TABLE, IF ANY
	MOVE	T2,T1		;COPY LOCK BLOCK ADDRESS
	LOAD.	T3,.LBFLG,(T2)	;GET FLAGS
	TRNN	T3,LBLAUC	;INCREMENTED A.T. READ COUNT?
	JRST	DLTLK1		;NO, DON'T NEED TO DECREMENT
	LOAD.	T1,.LBACC,(T2)	;GET ACCESS TABLE ADDRESS
	CAIGE	T1,CODMIN	;IS IT REALLY A SPECIAL CODE?
	PUSHJ	P,FILDRC##	;NO, DECREMENT READER COUNT
DLTLK1:	LOAD.	T3,.LBNHS,(T2)	;GET NEXT HASH PTR
	LOAD.	T4,.LBLHS,(T2)	; AND LAST HASH PTR
	STOR.	T3,.LBNHS,(T4)	;RELINK THEM
	STOR.	T4,.LBLHS,(T3)	; SO THIS BLOCK DISAPPEARS
	LOAD.	T1,.LBLEN,(T2)	;GET LENGTH
	SOS	%ENQNQ##	;REDUCE NUMBER OF QUEUES
	PJRST	GIVWDS##	;GIVE BACK THE SPACE TO MONITOR
	SUBTTL	ENQC QUEUE CONTROLLER UUO

;THIS UUO HAS FOUR FUNCTIONS:
;	0. RETURN STATUS OF LIST OF RESOURCES
;	1. RETURN USER'S LOCK QUOTA
;	2. SET USER'S LOCK QUOTA (PRIVILEGED)
;	3. DUMP OUT QUEUE STRUCTURE INTO USER BUFFER
;FOR OPTION 0, THREE WORDS ARE RETURNED FOR EACH LOCK SPECIFIED BY THE
; USER. THE 1ST WORD HAS THE FOLLOWING FORMAT:
;
;	B0	ERROR BIT 
;	B1	THIS USER IS THE OWNER OF THE LOCK
;	B2	THIS USER HAS ISSUED AN ENQ FOR THE LOCK
;	B3	THE OWNER OF THE LOCK HAS EXCLUSIVE ACCESS
;	B9-17	LEVEL NUMBER OF RESOURCE
;	B18-26	CONTEXT NUMBER OF OWNER
;	B27-35	JOB NUMBER OF OWNER/ERROR CODE 
;			THE JOB NUMBER OF THE OWNER MAY BE
;			ONLY ONE OF MANY OWNERS IF THE
;			LOCK IS SHARED.
;
;THE SECOND WORD IS A 36-BIT TIME-STAMP WHICH REPRESENTS
;	THE TIME AT WHICH THE RESOURCE WAS LAST
;	"ALLOCATED". THIS PROVIDES A METHOD OF DETERMINING
;	IF A USER HAS HELD THE RESOURCE FOR AN EXCESSIVE
;	AMOUNT OF TIME.
;
;THE THIRD WORD HAS THE REQUEST-ID IN THE RIGHT HALF.
;IF BIT 2 IN WORD 1 IS ON, THIS IS THE CALLER'S ID
;OTHERWISE, IT'S THE OWNER'S ID.
;
;IF THE LOCK HAS NO CURRENT OWNER, THE STATUS WORD WILL HAVE
; -1 IN THE RIGHT HALF

ENQC::	PUSHJ	P,SAVE4##	;PRESERVE P1-P4
	SETZM	ENQFLG		;THIS IS NOT AN ENQ.
	MOVEM	M,SBLOCK	;SAVE ADDRESS OF AC FOR LATER
	MOVEI	T2,QCMXFC	;SET UP MAX FUNCTION CODE
	PUSHJ	P,SETUP		;DO STANDARD STUFF
	  POPJ	P,		;ERROR IN FUNCTION CODE
	JRST	@[EXP QC0,QC1,QC2,QC3](T3)
;ENQC. FUNCTION 0 (.ENQCS), RETURN STATUS

QC0:	PUSHJ	P,CHKBLK	;CHECK LEGALITY OF RESOURCE BLOCK
	  JRST	STOTAC##	;  ERROR FOUND--ABORT
IFN FTMP,<
	PUSHJ	P,EQLOCK	;
>
	AOS	SBLOCK		;GET ADDRESS OF AC BACK
	HRR	M,SBLOCK	;GET AC+1
	PUSHJ	P,GETWDU##	;GET CONTENTS OF AC+1
	SOS	T1		;PUTWD1 BUMPS IT
	HRRZM	T1,SBLOCK	;SAVE IT
QC0.0:	PUSHJ	P,GETLOK	;GET A LOCK
	PUSHJ	P,LOCKOK	;IS THE LOCK SPEC OK?
	  JRST	QCERR		;NO, RETURN ERROR CODE
	PUSHJ	P,FNDLOK	;DOES THIS QUEUE EXIST?
	  JRST	[MOVEI P3,-1	;SET STATUS=-1
		 SETZB P4,P2	;CLEAR TIME-STAMP
		 JRST TELHIM]
	PUSHJ	P,COUNTQ	;COUNT THE QUEUE LENGTH
	JUMPE	T2,[MOVEI P3,-1	;NO ONE IN QUEUE, SET STATUS=-1
		    SETZB P4,P2	;CLEAR TIME-STAMP
		    JRST TELHIM]
	HRLZ	P2,T2		;PUT IT IN LEFT HALF
	MOVE	T2,.CPJCH##
	PUSHJ	P,CHECKQ	;YES, BUT AM I IN IT?
	  JRST	GOTHIM		; YES
	SETZB	P3,P4		;CLEAR AC
	LOAD.	T1,.LBNQ,(T1)	;GET FIRST Q-BLOCK IN QUEUE (T1=LOCK)
	LOAD.	T2,.QBRID,(T1)	;REQUEST ID
	HRR	P2,T2		;PUT IT IN RIGHT HALF
	LOAD.	T2,.QBFLG,(T1)	;AND FLAGS FOR THIS ENTRY
	TRNE	T2,QBOWNR	;IS HE THE OWNER?
	JRST	GOTHM2		;YES
	MOVEI	P3,-1		;SET RH OF STATUS TO -1
	JRST	TELLVL		;AND GO GET LEVEL #
QCERR:	HRLI	T1,EN%QCE	;SET ERROR BIT IN STATUS WORD
	MOVE	P3,T1		;MOVE ENTIRE WORD TO P3
	HRRM	P3,NQERRF	;REMEMBER THAT AN ERROR OCCURED
	SETZB	P2,P4		;CLEAR OTHER STUFF
	JRST	TELHIM		;AND GIVE IT BACK TO USER
;COME HERE WHEN THE CURRENT JOB IS IN THE QUEUE

GOTHIM:	MOVSI	P3,EN%QCQ	;TELL HIM HE'S IN THE QUEUE
	LOAD.	T2,.QBRID,(T1)	;GET REQUEST ID.
	HRR	P2,T2		;PUT IT IN RIGHT HALF
	LOAD.	T2,.QBFLG,(T1)	;GET HIS FLAG'S
	TRNE	T2,QBOWNR	;IS HE THE OWNER?
	JRST	GOTHM1		;YES, GO AND SET THE BIT
	LOAD.	T1,.QBLB,(T1)	;NO, GO AND FIND THE OWNER
	LOAD.	T1,.LBNQ,(T1)	;GET FIRST ENTRY IN QUEUE
	LOAD.	T2,.QBFLG,(T1)	;GET Q-BLOCK FLAGS
	TRNN	T2,QBINVS	;IS IT INVISIBLE?
	JRST	GOTHM2		;NO
	HRRI	P3,-1		;YES, TELL USER THAT THERE IS NO OWNER
	JRST	TELLVL

GOTHM1:	TLO	P3,EN%QCO	;TELL HIM HE'S THE OWNER
GOTHM2:	TRNE	T2,QBEXCL	;EXCLUSIVE ACCESS?
	TLO	P3,EN%QCX	;YES, SET THE BIT
	LOAD.	T2,.QBJCH,(T1)	;GET JOB/CONTEXT HANDLE
	HRR	P3,T2		;MOVE INTO STATUS WORD
TELLVL:	LOAD.	T1,.QBLB,(T1)	;GET LOCK-BLOCK ADDRESS
	LOAD.	T2,.LBLVL,(T1)	; AND LEVEL #
	DPB	T2,[POINT 9,P3,17] ;STICK IT IN
	LOAD.	P4,.LBTIM,(T1)	;GET TIME-STAMP
TELHIM:	MOVE	T1,P3		;MOVE STATUS WORD
	HRR	M,SBLOCK	;GET STATUS BLOCK
	PUSHJ	P,PUTWD1##	;GIVE WORD TO USER
	MOVE	T1,P4		;GET TIME-STAMP
	PUSHJ	P,PUTWD1##	;PUT IT OUT
	MOVE	T1,P2		;GET ID
	PUSHJ	P,PUTWD1##	;PUT IT OUT
	HRRM	M,SBLOCK	;PUT IT BACK
	PUSHJ	P,CHKCNT	;MORE LOCKS TO GO?
	  JRST	QC0.0		;YES
	SKIPN	T1,NQERRF	;WAS THERE AN ERROR?
	JRST	CPOPJ1##	;NO, EXIT
	JRST	STOTAC##	;YES, RETURN ERROR CODE
;ENQC. FUNCTION 1 (.ENQCG), RETURN USER'S QUOTA
;
QC1:	PUSHJ	P,GETQUO	;GET CURRENT QUOTA
	  JRST	STOTAC##	; ERROR
	TLZ	T1,-1		;CLEAR PDB FLAGS
	JRST	GUDXT2		;SKIP RETURN WITH QUOTA IN AC


;ENQC. FUNCTION 2 (.ENQCC), SET USER'S QUOTA
;
QC2:	TLZ	M,400000	;FOR PRVBIT
	MOVSI	T1,JP.POK
	PUSHJ	P,PRVBIT##	;AM I [1,2] OR JACCT?
	  TLOA	M,400000	;YES, IT'S OK
	JRST	HDRERC		;NO, EXIT
	PUSHJ	P,GETQUO	;GET CURRENT QUOTA
	  JRST	STOTAC##	; ERROR
	TLO	T1,EQ.HBS##	;REMEMBER THAT IT'S BEEN SET
	HLR	T1,P1		;GET NEW QUOTA
	MOVEM	T1,.PDEQQ##(W)	;STORE IT AWAY
	JRST	CPOPJ1##	;.AND EXIT



;SUBROUTINE TO RETURN USER'S QUOTA
;
;CALL:
;	PUSHJ	P,GETQUO
;	  ERROR RETURN
;	NORMAL RETURN
;
;ON RETURN, T1 = QUOTA
;	    P1 = HEADER WORD OF EFFECTIVE ADDRESS OF UUO (NEW QUOTA,,JOB #)
;
GETQUO:	PUSHJ	P,GETWDU##	;GET FIRST WORD
	MOVE	P2,J		;SAVE CURRENT JOB NUMBER
	MOVE	P1,T1		; AND HEADER WORD
	HRRE	J,P1		;JOB NUMBER
	CAMN	J,[-1]		;IF -1
	MOVE	J,.CPJOB##	;USE CURRENT
	PUSHJ	P,FNDPDB##	;FIND USER'S PDB
	  JRST	QCERR1		;BAD JOB NUMBER
	MOVE	T1,.PDEQQ##(W)	;FETCH USER'S QUOTA
	TLZN	T1,EQ.HBS##
	HRRZ	T1,%ENQDF
	MOVE	J,P2		;RESTORE CURRENT JOB NUMBER
	JRST	CPOPJ1##	;SKIP RETURN
	SUBTTL	DATA-STRUCTURE DUMP UTILITY

;ENQC. FUNCTION 3 (.ENQCD), DUMP ENQ/DEQ DATABASE.

;THIS OPTION OF THE ENQC. UUO DUMPS THE ENTIRE QUEUE STRUCTURE
; INTO A USER-SPECIFIED PARAMETER BLOCK. THIS FUNCTION IS
; RESTRICTED TO PEEK/SPY PRIVILEGE  AND SHOULD BE USEFUL ONLY IN
; EXCEPTION CIRCUMSTANCES.
;
QC3:	MOVSI	T1,PVSPYM!PVSPYA ;GET SPY BITS
	PUSHJ	P,PRVBIT##	;CAN HE SPY?
	  SKIPA			;YES, LET HIM THRU
	JRST	HDRERC		;NO, FLUSH THIS REQUEST
IFN FTMP,<
	PUSHJ	P,EQLOCK	;INTERLOCK ON EQ RESOURCE
>
	HRRZ	T1,RBLOCK	;GET ADDRESS OF HIS BLOCK
	PUSHJ	P,GETWDU##	;GET LENGTH OF BLOCK
	SOJL	T1,QCERR2	;LENGTH MUST BE POSITIVE
	MOVEM	T1,EQTMP1	;SAVE IT FOR LATER
	MOVEI	P1,HSHLEN##-1	;INIT POINTER TO HASH TABLE

;GET THE NEXT ENTRY IN THE HASH TABLE
QC3.1:	HRRZ	P2,HSHTAB##(P1)	;GET THIS ENTRY
	CAIE	P2,HSHTAB##(P1)	;IS IT EMPTY?
	JRST	QC3.2		;NO, GO GET LOCK
QC3.1A:	SOJGE	P1,QC3.1	;MORE?
PUTEND:	SETO	T1,		;YES, SET UP INSERT -1 AT END
	SOSL	EQTMP1		;IS THERE ROOM?
	PUSHJ	P,PUTWD1##	;YES, DO IT
	JRST	CPOPJ1##	;EXIT FROM UUO
;COME HERE TO SEARCH A PARTICULAR CHAIN IN THE HASH TABLE
;
;P1- ENTRY IN HASH TABLE
;P2- LOCK BLOCK ON CHAIN

QC3.1B:	LOAD.	P2,.LBNHS,(P2)	;GET NEXT LOCK-BLOCK ON CHAIN
	CAIN	P2,HSHTAB##(P1)	;BACK TO HASH TABLE?
	JRST	QC3.1A		;YES
QC3.2:	LOAD.	T1,.LBLEN,(P2)	;GET LENGTH OF LOCK BLOCK
	SUBI	T1,LBSIZE-2	;FIND LENGTH OF STATUS ENTRY
	CAMLE	T1,EQTMP1	;IS THERE ROOM IN BLOCK?
	JRST	PUTEND		;NO, PUT END MARKER IN IT
	EXCH	T1,EQTMP1	;YES, UPDATE LENGTH LEFT
	SUBM	T1,EQTMP1	;...
	LOAD.	T1,.LBACC,(P2)	;GET ACCESS TABLE ADDR
	LOAD.	T2,.LBFLG,(P2)	;..AND FLAGS
	LOAD.	T3,.LBLVL,(P2)	;..AND LEVEL
	HRL	T1,T3		;PUT LEVEL NUMBER IN WORD
	TLO	T1,EN%QCL	;THIS IS A LOCK BLOCK
	TRNE	T2,LBTEXT	;DOES IT HAVE TEXT?
	TLO	T1,EN%QCT	;YES, SET THE BIT
	PUSHJ	P,PUTWD1##	;STORE IT AWAY
	LOAD.	T1,.LBPWD,(P2)	;GET POOLED LOCK WORD
	PUSHJ	P,PUTWD1##	;STORE IT
	LOAD.	T1,.LBTIM,(P2)	;GET TIME-STAMP
	PUSHJ	P,PUTWD1##	;GIVE IT TO USER
	LOAD.	P3,.LBLEN,(P2)	;GET LENGTH BACK AGAIN
	SUBI	P3,LBSIZE	;FIND SIZE OF TEXT/CODE
	MOVEI	P4,O.TEXT(P2)	;SET UP POINTER TO TEXT
QC3.4:	MOVE	T1,(P4)		;GET WORD OF TEXT
	PUSHJ	P,PUTWD1##	;STORE IT
	AOS	P4		;BUMP POINTER
	SOJN	P3,QC3.4	;MORE?
	LOAD.	P3,.LBNQ,(P2)	;GET 1ST Q-BLOCK

;FALL THRU TO NEXT PAGE...
;COME HERE TO DUMP OUT A Q-BLOCK ENTRY

QC3.4A:	CAMN	P3,P2		;ARE WE BACK AT LOCK-BLOCK?
	JRST	QC3.1B		;YES, GET NEXT LOCK IN CHAIN
	MOVE	T1,EQTMP1	;GET LENGTH LEFT
	SUBI	T1,QBSIZE	;UPDATE IT
	JUMPL	T1,PUTEND	;JUMP IF NO ROOM
	MOVEM	T1,EQTMP1	;YES
	LOAD.	T1,.QBJCH,(P3)	;GET JOB/CONTEXT HANDLE
	LOAD.	T2,.QBFLG,(P3)	;AND FLAGS
	TRNN	T2,QBOWNR	;IS HE THE OWNER?
	JRST	QC3.5		;NO
	TLO	T1,EN%QCO	;YES, SET BIT
	JRST	QC3.6		;KEEP GOING
QC3.5:	TRNE	T2,QBWAIT	;NOT OWNER--IS HE WAITING?
	TLO	T1,EN%QCB	;YES, SET BLOCKED BIT
QC3.6:	TRNE	T2,QBEXCL	;EXCLUSIVE?
	TLO	T1,EN%QCX	;YES, SET IT
	TRNE	T2,QBLNDR	;NOT DEQ'ED ON RESET?
	TLO	T1,EN%QCN	;YES, SET IT
	TRNE	T2,QBLABT	;ABORT?
	TLO	T1,EN%QCA	;YES, SET IT
	TRNE	T2,QBINVS	;INVISIBLE?
	TLO	T1,EN%QCI	;YES, SET IT
	TRNE	T2,QBDEAD	;CHECK FOR DEADLOCK?
	TLO	T1,EN%QCD	;YES, SET IT
	PUSHJ	P,PUTWD1##	;STORE WORD
	LOAD.	T1,.QBRID,(P3)	;GET REQUEST ID
	LOAD.	T2,.LBPUL,(P2)	;GET LOCK-BLOCK POOL COUNT
	LOAD.	T3,.QBGRP,(P3)	;GET GROUP #
	SKIPE	T2		;IS IT A POOLED RESOURCE
	LOAD.	T3,.QBNRP,(P3)	;GET # HE WANTS
	HRL	T1,T3		;FORM WORD
	PUSHJ	P,PUTWD1##	;GIVE IT TO USER
	LOAD.	P3,.QBNQ,(P3)	;GET NEXT Q-BLOCK
	JRST	QC3.4A		;GO CHECK IF WE ARE BACK AT LOCK
;SUBROUTINE TO VALIDATE THE HEADER WORD AND CHECK ALL
; ENTRIES IN THE RESOURCE BLOCK FOR ADDRESS CHECK, ETC.
;
;CALL:
;	PUSHJ	P,CHKBLK
;	  HERE IF ERROR DISCOVERED (ERROR CODE IN T1)
;	NORMAL RETURN
;
;
;CLOBBERS:	T1-T4,P1-P3,M
;

CHKBLK:
IFN FTMP,<
	PUSHJ	P,HAVEQ		;DOES HE HAVE EQ RESOURCE
	  CAIA			;NO, OKAY
	STOPCD	.+1,DEBUG,ENQAVE	;++ALREADY HAVE EQ
>
	HRR	M,RBLOCK	;GET PARAMETER BLOCK ADDRESS
	PUSHJ	P,GETWDU##	;GET THE HEADER WORD
	LDB	T2,[POINT 6,T1,5] ;LENGTH OF BLOCK HEADER
	CAIN	T2,0		;ANY HEADER SIZE SPECIFIED?
	MOVEI	T2,2		;NO, MAKE IT 2 (HISTORICAL)
	CAILE	T2,HDRMAX	;WITHIN DEFINED RANGE?
	JRST	BFERR		;NO, TOO MANY ARGUMENTS
	MOVEM	T2,HDRSIZ	;NOTE HEADER LENGTH
	HRRZ	T2,T1		;TOTAL LENGTH OF THE BLOCK
	SUB	T2,HDRSIZ	;NUMBER OF WORDS FOR LOCK BLOCKS
	LDB	T4,[POINT 12,T1,17] ;NUMBER OF LOCKS
	IDIVI	T2,(T4)		;COMPUTE OF WORDS PER LOCK
	JUMPN	T3,BFERR	;IF NON-ZERO REMAINDER
	CAIL	T2,LKMIN	;IF LOCK TOO SMALL
	CAILE	T2,LKMAX	;OR TOO LARGE
	JRST	BFERR
	MOVEM	T2,LOKSIZ	;REMEMBER PER-LOCK BLOCK LENGTH
	HRLZM	T4,LOCKCT	;SAVE LOCK-COUNT,,0
	HRRE	T4,T4		;GET # OF LOCKS
	JUMPLE	T4,HDRERB	;BAD LOCK COUNT
	IMUL	T4,LOKSIZ	;COMPUTE WHAT LENGTH SHOULD
	PUSHJ	P,FNDPDS##	;FIND PDB OR DIE
	PUSHJ	P,GETLVL	;GET LEVEL # OF HIGHEST LOCK
	MOVEM	T1,HILEVL	;SAVE IT FOR USE LATER
	SETZ	T1,		;DEFAULT REQUEST ID IS 0
	MOVE	T2,HDRSIZ	;GET THE SIZE OF THE HEADER
	CAIL	T2,2		;DID HEADER INCLUDE REQUEST ID?
	PUSHJ	P,GETWD1##	;GET THE REQUEST ID
	MOVEM	T1,REQID	;SAVE IT FOR LATER
	SETZ	T1,		;DEFAULT TIME LIMIT IS FOREVER
	MOVE	T2,HDRSIZ	;GET THE SIZE OF THE HEADER
	CAIL	T2,3		;DID HEADER INCLUDE TIME LIMIT?
	PUSHJ	P,GETWD1##	;YES, GET IT
	MOVEM	T1,TIMLIM	;SAVE IN CASE WE BLOCK
CHKBK2:	PUSHJ	P,GETLOK	;GET A LOCK SPEC
	TLNE	P1,EN%UCW	;CODE WORD?
	JRST	CHKBK3		;YES, DON'T ADDRESS CHECK
	MOVE	T1,P2		;ARGUMENT TO CHKSTR
	PUSHJ	P,CHKSTR	;MAKE SURE STRING IS IN CORE AND LEGAL
	  POPJ	P,		;PROPAGATE ERROR
CHKBK3:	SKIPN	EQLMSK		;CHECK FOR MASK ARGUMENT
	JRST	CHKBK4		;IF NONE
	HRRZ	T1,EQLMSK	;MASK WORD ADDRESS
	HLRZ	T2,EQLMSK	;AND LENGTH
	CAMLE	T2,%ENQMM##	;IS IT OF LEGAL LENGTH?
	JRST	MWLERR		;IF NO
	PUSHJ	P,ARNGE##	;RANGE CHECK
	  JRST	UADERR##	;ADDRESS IS ILLEGAL
	  JRST	UADERR##	;ADDRESS IS ILLEGAL FOR I/O
CHKBK4:	SKIPN	EQLTBL		;DID USER SPECIFY A LOCK TABLE?
	JRST	CHKBK5		;NO, SKIP ADDRESS CHECKS
	HRRZ	T1,EQLTBL	;GET START ADDRESS
	HLRZ	T2,EQLTBL	;AND LENGTH
	CAMLE	T2,%ENQMT##	;MAXIMUM TABLE LENGTH
	JRST	TBLERR		;TABLE SIZE TOO LARGE
	PUSHJ	P,ARNGE##	;RANGE CHECK
	  JRST	UADERR##	;ADDRESS IS ILLEGAL
	  JRST	UADERR##	;ADDRESS IS ILLEGAL FOR I/O

CHKBK5:	PUSHJ	P,CHKCNT	;UPDATE LOCK COUNT AND CHECK IT
	  JRST	CHKBK2		;THERE'S MORE LOCKS TO GO
	HLLZS	LOCKCT		;RESET LOCK-COUNT WORD
	PJRST	CPOPJ1##	;SKIP RETURN
	SUBTTL	QUEUE SCHEDULING SUBROUTINES


;SUBROUTINE TO INITIATE SCHEDULING AFTER ONE DEQ CALL
;
;CALL:
;	MOVE	T1,LOCK-BLOCK-ADDR
;	MOVE	T2,Q-BLOCK-ADDR
;	PUSHJ	P,RSKED
;	RETURN HERE ALWAYS
;
;

RSKED:	PUSH	P,T2		;SAVE Q-BLOCK ADDRESS
	PUSH	P,T1		;SAVE LOCK-BLOCK ADDRESS
	LOAD.	T1,.QBNQR,(T2)	;GET NEXT REQUEST
	CAIN	T1,(T2)		;IS IT A MULTIPLE REQUEST?
	JRST	RSKED3		;NO, DON'T RESCHEDULE THE Q-ENTRY
	MOVEM	T2,EQTMP4	;SAVE Q-BLOCK ADDRESS
	PUSHJ	P,QSKD		;SCHEDULE THIS Q-BLOCK
	  JRST	RSKED3		;GO ON AND SCHEDULE LOCK
	MOVE	T2,EQTMP4	;GET Q-BLOCK ADDRESS BACK
	LOAD.	T4,.QBFLG,(T2)	;GET FLAGS OF OLD REQUEST
	JUMPN	T1,RSKED1	;JUMP IF SCHEDULER DID SOMETHING
	TRNE	T4,QBOWNR	;WAS HE AN OWNER?
	JRST	RSKED3		;YES, DON'T GIVE AN INTERRUPT

RSKED1:
IFN FTPI,<
	TRNN	T4,QBPSI	;DOES THIS USER EXPECT AN INTERRUPT?
	JRST	RSKED2		;NO, HE IS BLOCKED
	PUSH	P,J		;SAVE J
	LOAD.	J,.QBJCH,(T2)	;GET JOB/CONTEXT HANDLE OF USER
	MOVE	T1,T2		;COPY LOCK BLOCK ADDRESS
	SETZ	T2,		;INDICATE NORMAL REQUEST
	PUSHJ	P,GENPSI	;YES, GIVE HIM ONE
	POP	P,J		;RESTORE J
	JRST	RSKED3		; AND GO ON TO SCHEDULE LOCK
>;END OF IFN FTPI
RSKED2:	LOAD.	T1,.QBJCH,(T2)	;GET HIS JOB NUMBER
	PUSHJ	P,CTXWAK##	;GO AND WAKE HIM UP
	  STOPCD .+1,JOB,ENQCWD	;++CAN'T WAKE JOB/CONTEXT AFTER DEQ.
RSKED3:	POP	P,T1		;GET LOCK-BLOCK BACK AGAIN
	PUSHJ	P,LOKSKD	;SCHEDULE LOCK BLOCK
	JRST	T2POPJ##	;GET Q-BLOCK ADDRESS BACK AND RETURN



;SUBROUTINE TO SCHEDULE ONE Q REQUEST CHAIN
;
;CALL:
;	MOVE	T1,Q-BLOCK-ADDR
;	PUSHJ	P,QSKD
;	  RETURN HERE IF REQUEST NOT LOCKED
;	HERE IF FULLY LOCKED
;	T1 = 0  IF ALREADY LOCKED BEFORE THIS CALL
;	T1 =-1  IF THIS CALL DID ANY LOCKING
;
;

QSKD:	PUSHJ	P,SAVE2##	;SAVE P1-P2
	SETZM	QSKDF		;ASSUME LOCK ALREADY LOCKED
	MOVEM	T1,QSKDQ	;SAVE THE ADDRESS OF THIS Q-BLOCK
QSKD0:	PUSHJ	P,SETINV	;SET INVISIBLE BITS
	  JRST	QSKD1		;GO MAKE SCHEDULING PASS
	JRST	QSKD6		;LOCK IS ALREADY LOCKED

QSKD1:	MOVE	T1,QSKDQ	;GET ADDR OF THIS BLOCK AGAIN
QSKD2:	MOVEM	T1,QSKDT	;SAVE IT TEMPORARILY
	LOAD.	T2,.QBMSK,(T1)	;GET THE MASK WORD
	MOVEM	T2,QSKDM	;SAVE FOR FUTURE REFERENCE
	LOAD.	T2,.QBLB,(T1)	;GET POINTER TO LOCK BLOCK
	LOAD.	T2,.LBNMS,(T2)	;GET LENGTH OF MASK BLOCK
	MOVEM	T2,QSKDN	;SAVE
	LOAD.	T2,.QBGRP,(T1)	;GET GROUP NUMBER
	MOVEM	T2,QSKDG	;SAVE IT
	LOAD.	T2,.QBFLG,(T1)	;GET FLAGS
	TRNE	T2,QBOWNR+QBINVS ;IS THIS THE OWNER OR IS IT INVISIBLE?
	JRST	QSKD5		;YES, CONTINUE SCANNING SIDEWAYS
QSKD3:	LOAD.	T1,.QBLQ,(T1)	;GET PREVIOUS BLOCK IN QUEUE
	MOVE	T2,QSKDT	;GET THIS QUEUE BLOCK ADDRESS AGAIN
	LOAD.	T3,.QBFLG,(T2)	;AND FLAGS FROM SAME
	LOAD.	T4,.QBFLG,(T1)	;AND FLAGS FROM PREVIOUS
	TRNE	T4,LBLKBK	;IS THIS A LOCK BLOCK?
	JRST	QSKD4		;YES, WE ARE AT TOP OF QUEUE
	TRNE	T4,QBINVS	;IS IT INVISIBLE?
	JRST	QSKD3		;YES, IGNORE IT ENTIRELY
	TRNN	T4,QBEXCL	;IS THIS AN EXCLUSIVE REQUEST?
	TRNE	T3,QBEXCL	;OR IS ORIGINAL REQUEST EXCLUSIVE?
	JRST	QSKD7		;YES TO EITHER. MUST LOOK AT MASKS
	LOAD.	T2,.QBGRP,(T1)	;GET GROUP NUMBER
	CAME	T2,QSKDG	;SAME GROUP AS OWNER?
	JRST	QSKD13		;NO, STOP HERE
	JRST	QSKD3		;LOOP BACK UNTIL AT LOCK BLOCK

;QSKD CONTINUES ON NEXT PAGE...
QSKD4:	MOVE	T3,QSKDT
	LOAD.	T3,.QBNRP,(T3)	;GET # OF RESOURCES WANTED
	LOAD.	T4,.LBAVL,(T1)	;GET # OF RESOURCES AVAILABLE
	SUB	T4,T3		;SUBTRACT REQUEST
	JUMPL	T4,QSKD13	;NOT ENOUGH AVAILABLE
	STOR.	T4,.LBAVL,(T1)	;STORE BACK UPDATED TOTAL
	MOVE	T3,QSKDT	;GET BACK ADDR OF Q-BLOCK
	LOAD.	T2,.QBFLG,(T3)	;GET FLAGS FROM SAME
	TRO	T2,QBOWNR	;SET THE OWNER FLAG
	STOR.	T2,.QBFLG,(T3)	;STORE THE UPDATED FLAGS
	LOAD.	T2,.QBNQ,(T3)	;NOW MOVE THIS Q-BLOCK TO THE HEAD
	LOAD.	T4,.QBLQ,(T3)	;  OF THE Q FOR THIS LOCK
	STOR.	T2,.QBNQ,(T4)	;FIRST, REMOVE IT FROM THE Q
	STOR.	T4,.QBLQ,(T2)	;...
	LOAD.	T2,.LBNQ,(T1)	;NOW ADD IT TO THE START OF THE Q
	STOR.	T3,.QBLQ,(T2)	; PTR BACK TO Q-BLOCK FROM Q-2
	STOR.	T3,.LBNQ,(T1)	; PTR TO Q-BLOCK FROM LOCK-BLOCK
	STOR.	T2,.QBNQ,(T3)	; PTR TO SECOND Q-BLOCK
	STOR.	T1,.LBLQ,(T3)	; PTR BACK TO LOCK-BLOCK
	MOVE	T2,DATE##	;GET DATE-TIME
	STOR.	T2,.LBTIM,(T1)	;PUT IN LOCK-BLOCK
	MOVE	T1,QSKDQ	;GET BACK ORIGINAL Q-BLOCK ADDR
	SETOM	QSKDF		;MARK THAT A LOCK WAS LOCKED
	JRST	QSKD0		;AND SCAN AGAIN

QSKD5:	MOVE	T2,QSKDT	;GET Q-BLOCK ADR OF PRESENT Q-BLOCK
	LOAD.	T1,.QBNQR,(T2)	;GET NEXT Q-BLOCK IN THIS REQUEST
	CAME	T1,QSKDQ	;ARE WE BACK AT THE Q-BLOCK YET?
	JRST	QSKD2		;NO, GO CHECK IF THIS Q LOCKED
QSKD6:	MOVE	T1,QSKDF	;GET FLAG
	PJRST	CPOPJ1##	;SKIP RETURN

QSKD7:	LOAD.	T3,.QBMSK,(T1)	;CHECK FOR MASKS
	SKIPE	T4,QSKDM	;IS THERE A MASK FOR THE QUEUE BLOCK
	JUMPN	T3,QSKD10	;YES, DO BOTH HAVE A MASK
	JUMPN	T4,QSKD8	;NO, ONLY ONE?
	JUMPE	T3,QSKD13	;YES, CAN'T GET THE LOCK NOW
	MOVE	T4,T3		;GET ADDRESS OF BLOCK TO USE
QSKD8:	MOVE	T3,QSKDN	;GET LENGTH OF MASK BLOCK
QSKD9:	SKIPE	(T4)		;ANY BIT ON IN MASK
	JRST	QSKD13		;YES, QUIT
	ADDI	T4,1		;ADVANCE TO NEXT WORD IN MASK
	SOJG	T3,QSKD9	;LOOP OVER WHOLE MASK BLOCK
	JRST	QSKD3		;ENTIRE MASK BLOCK IS ZERO

QSKD10:	MOVE	P1,QSKDN	;LENGTH OF MASK BLOCK
QSKD11:	MOVE	P2,0(T3)	;NEXT WORD IN MASK BLOCK
	AND	P2,0(T4)	;CLEAR NON-CONFLICTING BITS
	JUMPN	P2,QSKD13	;RETURN IF ANY CONFLICTS
	ADDI	T3,1		;STEP
	ADDI	T4,1		; TO NEXT MASK WORD
	SOJG	P1,QSKD11	;LOOP OVER WHOLE BLOCK
	JRST	QSKD3		;THESE BLOCKS CAN CO-EXIST

QSKD13:	MOVE	T1,QSKDF	;GET FLAG
	POPJ	P,		;GIVE ERROR RETURN
;SUBROUTINE TO SET INVISIBLE BITS FOR A Q-BLOCK ENTRY
;
;CALL:	
;	MOVE	T1,Q-BLOCK-ADDR
;	PUSHJ	P,SETINV
;	  HERE IF NOT FULLY LOCKED YET
;	HERE IF REQUEST IS NOW FULLY LOCKED
;
;CLOBBERS:	T1-T4
;
SETINV:	MOVSI	T2,1		;INIT LEVEL NUMBER SCANNER
	MOVEM	T2,EQTMP2	;SAVE LEVEL NUMBER FOR LATER
	MOVEM	T1,EQTMP1	;SAVE Q-BLOCK ADDR TOO
SETIN1:	LOAD.	T2,.QBLB,(T1)	;GET ADDR OF LOCK BLOCK
	LOAD.	T4,.LBLVL,(T2)	;AND ITS LEVEL
	LOAD.	T3,.QBFLG,(T1)	;  AND FLAGS FOR Q-BLOCK
	TRNE	T3,QBOWNR	;IS THIS THE OWNER OF THE LOCK?
	JRST	SETIN2		;YES
	CAMG	T4,EQTMP2	;NO, IS THIS A NEW LOW VALUE?
	MOVEM	T4,EQTMP2	;YES, REMEMBER LOWEST NON-LOCKED VALUE
SETIN2:	LOAD.	T1,.QBNQR,(T1)	;GET NEXT Q-BLOCK IN THIS REQUEST
	CAME	T1,EQTMP1	;ARE WE BACK AT STARTING POINT?
	JRST	SETIN1		;NO, LOOP BACK FOR MORE Q-BLOCKS
	MOVE	T2,EQTMP2	;GET LEVEL NUMBER
	TLNE	T2,-1		;SEE IF AN UNLOCKED WAS SEEN
	JRST	CPOPJ1##	;NONE SEEN, LOCK IS FULLY LOCKED

SETIN3:	LOAD.	T2,.QBLB,(T1)	;GET LOCK ADDR
	LOAD.	T4,.LBLVL,(T2)	;AND ITS LEVEL
	LOAD.	T3,.QBFLG,(T1)	;GET Q-BLOCK'S FLAGS
	CAMG	T4,EQTMP2	;IS LEVEL ABOVE LOWEST UNLOCKED LEVEL?
	TRZA	T3,QBINVS	;NO, MAKE THIS Q-BLOCK VISIBLE
	TRO	T3,QBINVS	;YES, MAKE IT INVISIBLE
	STOR.	T3,.QBFLG,(T1)	;STORE FLAGS BACK
	LOAD.	T1,.QBNQR,(T1)	;GET ADDR OF Q-BLOCK IN REQUEST
	CAME	T1,EQTMP1	;HAVE WE SEEN ALL Q-BLOCKS?
	JRST	SETIN3		;NO, LOOP BACK FOR MORE
	POPJ	P,		;NON-SKIP RETURN
	SUBTTL	LOCK SCHEDULING SUBROUTINE

;SUBROUTINE TO SCHEDULE ONE ENTIRE QUEUE FOR A GIVEN LOCK
;
;CALL:
;	MOVE	T1,LOCK-BLOCK-ADDR
;	PUSHJ	P,LOKSKD
;	RETURN HERE ALWAYS
;
;ON RETURN, ALL PROCESSES HAVE BEEN INTERRUPTED, OR WOKEN UP
;WHICH HAD RESOURCES THAT HAD BECOME AVAILABLE OR ABORTED
;

LOKSKD:	MOVEM	T1,LKTMP1	;SAVE ADDRESS OF LOCK
	MOVEM	T1,LKTMP2	;INIT Q-BLOCK ADDRESS REGISTER
	LOAD.	T2,.LBAVL,(T1)	;GET NUMBER AVAILABLE
	MOVEM	T2,LKTMP3	;SAVE IT
	SETZM	LKTMP4		;FLAG THAT LOCK IS NOT ABORTED
	PUSHJ	P,CHKABT	;CHECK TO SEE IF LOCK IS ABORTED
	  SETOM	LKTMP4		;YES IT IS ABORTED
LOKSK1:	LOAD.	T1,.LBNQ,(T1)	;GET ADDR OF NEXT Q-BLOCK FOR LOCK
	CAMN	T1,LKTMP1	;BACK TO THE LOCK-BLOCK YET?
	POPJ	P,		;YES, THRU
	MOVEM	T1,LKTMP2	;SAVE THIS ADDRESS
	LOAD.	T2,.QBFLG,(T1)	;GET FLAGS FOR THIS ENTRY
	TRNE	T2,QBINVS	;IS IT INVISIBLE?
	JRST	LOKSK1		;YES, IGNORE IT
	SKIPE	LKTMP4		;WAS LOCK JUST ABORTED?
	JRST	LOKSK4		;YES, GO WAKEUP OR SIGNAL
	TRNE	T2,QBOWNR	;IS HE THE OWNER?
	JRST	LOKSK0		;YES
	LOAD.	T2,.QBNRP,(T1)	;GET NUMBER REQUESTED
	MOVE	T3,LKTMP3	;GET NUMBER AVAILABLE
	SUB	T3,T2		;GET DIFFERENCE
	MOVEM	T3,LKTMP3	;UPDATE # AVAILABLE
	JUMPL	T3,CPOPJ##	;LEAVE IF SUPPLY EXHAUSTED
LOKSK0:	PUSHJ	P,QSKD		;SCHEDULE THIS Q-BLOCK
	  JRST	LOKSK2		;IT WASN'T LOCKED
	JUMPE	T1,LOKSK2	;IF NOT JUST LOCKED, DON'T INTERRUPT
LOKSK4:	MOVE	T1,LKTMP2	;GET ADDR OF THIS Q-BLOCK
IFN FTPI,<
	LOAD.	T2,.QBFLG,(T1)	;GET FLAGS
	TRNN	T2,QBPSI	;IS HE EXPECTING AN INTERRUPT?
	JRST	LOKSK3		;NO, HE IS WAITING
	PUSH	P,J		;SAVE J
	LOAD.	J,.QBJCH,(T1)	;GET JOB/CONTEXT HANDLE OF USER
	MOVE	T2,LKTMP4	;INDICATE NORMAL OR ABORTED REQUEST
	PUSHJ	P,GENPSI	;GENERATE AN INTERRUPT FOR HIM
	POP	P,J		;RESTORE J
	JRST	LOKSK2		;AND CONTINUE
>;END FTPI
LOKSK3:	LOAD.	T1,.QBJCH,(T1)	;GET THIS GUY'S JOB NUMBER
	PUSH	P,W		;JUST IN CASE W GETS LOST
	PUSHJ	P,CTXWAK##	;WAKE HIM UP
	  STOPCD .+1,JOB,ENQCWJ	;++CAN'T WAKE JOB/CONTEXT
	POP	P,W		;GET IT BACK

LOKSK2:	MOVE	T1,LKTMP2	;GET Q-BLOCK ADDR AGAIN
	JRST	LOKSK1		;LOOP BACK FOR REST OF Q
	SUBTTL	DEADLK -- CHECK QUEUE STRUCTURE FOR DEADLOCK

;CALL:
;	PUSHJ	P,DEADLK
;	  <IF DEADLOCK>
;	<IF NO DEADLOCK>

DEADLK:	PUSHJ	P,SAVE4##	;PRESERVE P1-P4
	PUSHJ	P,SAVJW##	;PRESERVE J AND W TOO
	SETZM	ENQTBC		;NULL LIST OF REQUESTS TO BE CHECKED
	SETZM	ENQHBC		;NULL LIST OF ALREADY CHECKED REQUESTS
	MOVE	P3,.CPJCH##	;GET REQUESTER'S JOB/CONTEXT HANDLE

DEADJQ:	MOVE	T1,P3		;GET THE NEXT JOB/CONTEXT HANDLE
	PUSHJ	P,CTXENQ##	;GET START OF JOB/CONTEXT'S QUEUE CHAIN
	  STOPCD .+1,JOB,ENQIJC	;++ILLEGAL JOB/CONTEXT HANDLE
	SKIPA	P1,T2		;SAVE IN P1

DEADR:	LOAD.	P1,.QBNJQ,(P1)	;GET THE NEXT QUEUE ENTRY
	JUMPE	P1,DEADJ	;NO QUEUE CHAIN, CHECK NEXT JOB
	LOAD.	T1,.QBFLG,(P1)	;GET QUEUE BLOCK FLAGS
	TRNE	T1,QBOWNR	;IS THIS A REQUEST?
	JRST	DEADR		;NO, TRY NEXT QUEUE ENTRY
	LOAD.	P2,.QBLB,(P1)	;GET THE LOCK-BLOCK FOR THIS QUEUE ENTRY

DEADQ:	LOAD.	P2,.QBNQ,(P2)	;GET THE NEXT QUEUE ENTRY
	CAMN	P1,P2		;HAVE WE LOOPED BACK TO THE REQUEST?
	JRST	DEADR		;YES, CHECK JOB'S NEXT REQUEST
	LOAD.	T1,.QBJCH,(P2)	;GET OWNER OF THIS QUEUE ENTRY
	CAMN	T1,.CPJCH##	;ARE WE THE OWNER?
	POPJ	P,		;YES, DEADLOCK
	SKIPN	T2,ENQHBC	;HAS THIS JOB ALREADY BEEN CHECKED?
	MOVEM	P2,ENQHBC	;NULL LIST, MAKE NON-EMPTY
	JUMPE	T2,DEADQ2	;AND GO STORE ON CHECK LIST
DEADQ1:	MOVE	T4,T2		;SAVE ORIGINAL POINTER
	LOAD.	T3,.QBJCH,(T2)	;GET OWNER OF THIS QUEUE ENTRY
	CAMN	T3,T1		;ALREADY ON CHECK LIST?
	JRST	DEADQ		;YES, GET ANOTHER QUEUE ENTRY
	LOAD.	T2,.QBCHK,(T2)	;GET NEXT ENTRY IN CHECK LIST
	JUMPN	T2,DEADQ1	;ANY MORE ENTRIES IN CHECK LIST?
	STOR.	P2,.QBCHK,(T4)	;NO, PUT REQUEST ON TAIL OF CHECK LIST
DEADQ2:	STOR.	T2,.QBCHK,(P2)	;PUT NULL AT END OF CHECK LIST
	SKIPN	ENQTBC		;LIST EMPTY?
	MOVEM	P2,ENQTBC	;YES, MAKE NOT EMPTY
	JRST	DEADQ		;GET ANOTHER QUEUE ENTRY

DEADJ:	SKIPN	P4,ENQTBC	;GET NEXT REQUEST TO BE CHECKED
	JRST	CPOPJ1##	;NO JOBS FOUND, NO DEADLOCK!
	LOAD.	P3,.QBJCH,(P4)	;GET JOB/CONTEXT HANDLE OF REQUEST
	LOAD.	T1,.QBCHK,(P4)	;GET NEXT REQUEST TO BE CHECKED
	MOVEM	T1,ENQTBC	;SAVE FOR LATER
	JRST	DEADJQ		;GO CHECK OUT THIS JOB
	SUBTTL	SUBROUTINE TO GET A LOCK SPECIFICATION FROM USER SPACE

;SUBROUTINE TO GET ONE LOCK SPEC (2 TO 5 WORDS) FROM THE USER'S
; RESOURCE BLOCK
;
;ON ENTRY, THE WORD "LOCKCT" IS SET UP AS FOLLOWS:
;	XWD	TOTAL # OF LOCKS, # OF LOCK TO GET
;
;CALL:
;	PUSHJ	P,GETLOK
;	NORMAL RETURN ALWAYS
;
;ON EXIT, P1-P3 WILL CONTAIN THE FIRST 3 WORDS OF THE LOCK
; SPECIFICATION AS GIVEN BY THE USER WITH THE FOLLOWING
; CHANGES:
;	1.  IF LH OF P2 WAS -1, IT IS CHANGED TO "POINT 7,0"
;	2.  THE RH OF P4 HAS THE ACCESS TABLE ADDRESS IN IT
;		(OR -2/-3/400000+JOB #)
; IN ADDITION, THE WORD EQLTBL WILL CONTAIN A WORD OF THE
; FORM XWD LENGTH,ADDRESS FOR THE USERS LOCK-ASSOCIATED TABLE,
; AND THE WORD EQLMSK WILL CONTAIN A WORD OF THE FORM 
; XWD LENGTH,ADDRESS FOR THE USERS PIE-SLICE LOCK MASK WORD.

GETLOK:	HRR	M,RBLOCK	;GET ADDRESS OF PARAMETERS
	HRRZ	T1,LOCKCT	;GET THE NUMBER OF THIS LOCK
	IMUL	T1,LOKSIZ	;MULTIPLY BY SIZE
	ADD	T1,HDRSIZ	;ADD IN HEADER SIZE
	ADDI	M,-1(T1)	;BUMP POINTER, ALLOW FOR PREINCREMENT
	SETZM	USRLOK		;CLEAR MONITOR COPY TO ZERO
	MOVE	T1,[USRLOK,,USRLOK+1]
	BLT	T1,USRLOK+LKMAX-1
	MOVN	P1,LOKSIZ	;LENGTH OF PER-LOCK BLOCK
	MOVSI	P1,(P1)		;MAKE INTO AN AOBJN POINTER
GETLK1:	PUSHJ	P,GETWD1##	;FETCH A WORD
	MOVEM	T1,USRLOK(P1)	;SAVE IT
	AOBJN	P1,GETLK1	;LOOP OVER WHOLE BLOCK
	MOVE	P3,[USRLOK,,P1]	;SET TO COPY TO AC'S
	BLT	P3,P3		;ZAP!
	TLNE	P1,EN%UCW	;CODE WORD?
	JRST	GETLK2		;YES, CAN'T BE BYTE POINTER
	HLRZ	T1,P2		;GET LEFT HALF OF PTR
	CAIN	T1,-1		;IS IT -1?
	HRLI	P2,(POINT 7,0)	;YES, MAKE IT INTO A BYTE PTR
GETLK2:	HRRE	T1,P1		;GET CHANNEL #/-1/-2/-3
	CAMN	T1,[-1]		;IS THIS A JOB WIDE LOCK?
	HRRI	T1,CODMIN(J)	;YES, MAKE 400000+JOB NUMBER
	JUMPL	T1,GETLK3	;LEAVE NOW IF NOT A FILE LOCK
	HRRZS	P1		;ISOLATE CHANNEL NUMBER
	PUSHJ	P,SETUF##	;POINT F AT THE DDB
	  TDZA	T1,T1		;CHANNEL ILLEGAL OR NOT YET OPEN
	HRRZ	T1,DEVACC##(F)	;GET ACCESS TABLE ADDRESS
	CAIL	T1,CODMIN	;DOES IT LOOK LIKE A SPECIAL CODE?
	STOPCD	.+1,DEBUG,ENQATA ;++BAD ACCESS TABLE ADDRESS
	MOVE	P1,USRLOK	;RESTORE P1
GETLK3:	HRRM	T1,P4		;SAVE THIS IN RH OF P4 FOR USE LATER
	POPJ	P,		;RETURN
	SUBTTL	SUBROUTINES TO CHECK PRIVILEGES AND LOCK SPECS

;SUBROUTINE TO CHECK USER PRIVILEGES
;
;ON ENTRY, P1-P4 MUST BE SET UP BY GETLOK
;
;CALL:
;	PUSHJ	P,CHPRIV
;	  RETURN HERE IF PRIV. INSUFFICIENT
;	USER IS OK
;
CHPRIV:	HRRE	T1,P1		;GET CHANNEL #/-1/-2/-3
	CAMN	T1,[-2]		;IS IT A LOGICAL LOCK?
	JRST	[MOVSI T1,JP.ENQ	;YES, DOES HE HAVE ENQ PRIV?
		 PUSHJ	P,PRVBIT##	;CHECK PRIVILEGES
		 JRST	CPOPJ1##	;IT'S OK
 		JRST	PRVERR]	;NO, HE'S AN IMPOSTER
	CAMN	T1,[-3]		;OR, IS IT A [1,2]/JACCT LOCK?
	JRST	[PUSHJ	P,PRVJC##	;YES, IS HE PRIVILEGED?
		 JRST	CPOPJ1##	;YES, ITS OK
		  JRST	HDRER7]		; NO
	PJRST	CPOPJ1##	;SKIP RETURN


;SUBROUTINE TO CHECK ASCII STRING
;CALL:
;	MOVE	T1,USERS BYTE STRING
;	PUSHJ	P,CHKSTR
;	  BAD POINTER RETURN
;	STRING OKAY RETURN
;USES T1-T2.  NEVER RETURNS IF BAD ADDRESS IN THE STRING.

CHKSTR:	LDB	T2,[POINT 3,T1,2] ;FIRST 3 BITS
	CAIN	T2,5		;IS IT A 5 (SPECIAL CODE FOR NUMBER)
	JRST	CPOPJ1##	;YES, GIVE GOOD RETURN NOW
	TLNE	T1,37		;ANY INDEX OR INDIRECT BITS
	JRST	HDRERA		;YES, THE POINTER IS BAD
	PUSHJ	P,SAVE4##	;PRESERVE P1-P4
	MOVEI	P1,^D36		;BITS PER WORD
	LDB	P2,[POINT 6,T1,11] ;BITS PER USER BYTE
	JUMPE	P2,HDRER1	;IF ZERO
	CAILE	P2,^D36		;OR MORE THAN A WORD
	JRST	HDRER1		;BAD BYTE SIZE ERROR
	IDIVI	P1,(P2)		;COMPUTE BYTES PER WORD
	IMUL	P1,%ENQML##	;COMPUTE MAXIMUM ALLOWABLE NR. OF BYTES
	MOVE	P2,T1		;SAVE BYTE POINTER
	SETO	P3,		;LAST PAGE CHECKED
CHKST1:	IBP	P2		;POINT TO ADDRESS ACTUALLY REFERENCED
	LDB	P4,[POINT 9,P2,26] ;PAGE ADDRESS OF THE POINTER
	CAME	P4,P3		;CHECKED LAST TIME?
	PUSHJ	P,ADRCHK	;CHECK ADDRESS
	EXCTUX	<LDB T2,P2>	;GET A BYTE
	JUMPE	T2,CPOPJ1##	;IF END OF STRING
	SOJG	P1,CHKST1	;LOOP OVER MAXIMUM LENGTH
	JRST	HDRER2		;ELSE RETURN ERROR


;ROUTINE TO CHECK AN ADDRESS, CALLED ONLY BY CKST1.
;CALL WITH BYTE POINTER IN P2, PAGE ADDRESS IN P4.
;RETURNS WITH LAST PAGE CHECKED IN P3.  CLOBBERS T1.

ADRCHK:	PUSH	P,M		;SAVE M
	HRRZ	M,P2		;ADDRESS TO BE CHECKED
	PUSHJ	P,GETWDU##	;LOOK AT THE WORD
	MOVE	P3,P4		;UPDATE PAGE LAST CHECKED
	PJRST	MPOPJ##		;RESTORE M AND RETURN
;SUBROUTINE TO CHECK THE BYTE POINTER SIZE, POOL VALUES,
; AND CHANNEL #
;
;ON ENTRY, P1-P4 SET UP BY GETLOK
;
;CALL:
;	PUSHJ	P,LOCKOK
;	  ERROR RETURN
;	NORMAL RETURN
;
LOCKOK:	TLNE	P1,EN%UCW	;CODE WORD?
	JRST	LKOK2		;YES, CODE IS OK
	LDB	T2,[POINT 3,P2,2] ;GET STRING IDENTIFIER
	CAIN	T2,5		;IS IT A USER CODE?
	JRST	LKOK2		;YES, DON'T CHECK BYTE SIZE
	LDB	T2,[POINT 6,P2,11] ;GET BYTE SIZE
	JUMPE	T2,HDRER1	;ZERO BYTE SIZE?
	CAILE	T2,^D36		;TOO LARGE?
	JRST	HDRER1		;YES, BAD BYTE SIZE ERROR
	TLNE	P2,37		;INDEXED OR INDIRECT POINTER?
	JRST	HDRERA		;YES, ERROR
LKOK2:	HRRE	T1,P1		;GET CHANNEL #
	CAMGE	T1,[-3]		;OR LESS THAN -3?
	JRST	HDRER6
	JUMPL	T1,LKOK3	;NO DDB IF NEGATIVE
	CAIGE	T1,775		;CAN'T USE CHANNELS 775,776,777
	PUSHJ	P,QSTUF		;LEGAL CHANNEL?
	  JRST	HDRER6		;YES TO EITHER IS AN ERROR
LKOK3:	JUMPL	P3,HDRER8	;NEGATIVE POOL NUMBER IS ILLEGAL
	HLRE	T2,P3		;GET POOL VALUES
	JUMPE	T2,CPOPJ1##	;DON'T CHECK IF NOT A POOLED RESOURCE
	TRNE	P3,-1		;DOES HE WANT NONE OF THEM?
	CAIGE	T2,(P3)		;REQUEST MORE THAN IN POOL?
	JRST	HDRER8		;YES, ERROR
	PJRST	CPOPJ1##	;SKIP RETURN

;
QSTUF:	PUSHJ	P,SAVE1##	;SAVE P1
	HRRZ	P1,P1		;CLEAR LEFT HALF
	PJRST	SETUF##		;CHECK FOR LEGAL CHANNEL
;SUBROUTINE TO CHECK IF A USER HAS BEEN QUEUE'D FOR A LOCK
;
;CALL:
;	MOVE	T1,LOCK-BLOCK-ADDR
;	MOVE	T2,JOB-CONTEXT-HANDLE
;	PUSHJ	P,CHECKQ
;	  HERE IF HE IS Q'ED
;	HERE IF USER NOT Q'D
;
;ON NON-SKIP RETURN, T1 WILL HAVE THE Q-BLOCK ADDRESS IN IT
;ON SKIP RETURN, T1 WILL STILL HAVE THE LOCK-BLOCK ADDRESS IN IT
;
;
CHECKQ:	PUSHJ	P,SAVE3##	;SAVE P1-P3
	MOVE	P1,T1		;SAVE LOCK BLOCK ADDR 
	LOAD.	P2,.LBNMS,(T1)	;LENGTH OF RESOURCE MASK
	MOVE	P3,T2		;SAVE JOB/CONTEXT HANDLE
	LOAD.	T1,.LBNQ,(T1)	;GET ADDRESS OF NEXT Q-BLOCK
CHECK2:	CAIN	T1,(P1)		;ARE WE BACK AT START?
	JRST	CPOPJ1##	;YES, SKIP RETURN
	LOAD.	T2,.QBJCH,(T1)	;GET THIS Q-BLOCK'S JOB/CONTEXT HANDLE
	CAME	T2,P3		;FOR THIS JOB/CONTEXT?
	JRST	CHECK3		;NO, KEEP LOOKING
	JUMPE	P2,CPOPJ	;IF NO MASKS SPECIFIED
	PUSH	P,T1		;SAVE ADDRESS OF THIS QUEUE ENTRY
	LOAD.	T1,.QBMSK,(T1)	;ADDRESS OF MASK
	HRRZ	T2,EQLMSK	;ADDRESS OF TARGET MASK
	MOVEI	T3,(P2)		;LENGTH OF MASK BLOCK
	PUSHJ	P,TSTMSK	;SEE IF IDENTICAL
	  CAIA			;NO
	JRST	TPOPJ##		;RETURN SKIP.  HE'S QUEUED
	POP	P,T1		;RESTORE Q-BLOCK ADDRESS
CHECK3:	LOAD.	T1,.QBNQ,(T1)	;TO NEXT Q-BLOCK
	JRST	CHECK2		;KEEP GOING

;SUBROUTINE TO CHECK IF A LOCK HAS BEEN ABORTED
;
;CALL:
;	MOVE	T1,LOCK-BLOCK-ADDR
;	PUSHJ	P,CHKABT
;	  HERE IF LOCK IS ABORTED
;	HERE IF LOCK IS NOT ABORTED
;
;ON RETURN, T1 WILL STILL HAVE THE LOCK-BLOCK ADDRESS IN IT
;
CHKABT:	MOVE	T3,T1		;SAVE LOCK BLOCK ADDR 
CHKAB2:	LOAD.	T3,.LBNQ,(T3)	;GET ADDRESS OF NEXT Q-BLOCK
	CAIN	T3,(T1)		;ARE WE BACK AT START?
	JRST	CPOPJ1##	;YES, SKIP RETURN
	LOAD.	T4,.QBFLG,(T3)	;GET THIS Q-BLOCK'S FLAGS
	TRNE	T4,QBOWNR	;IS THIS THE OWNER OF THE LOCK?
	TRNN	T4,QBLABT	;AND IS IT AN ABORTED LOCK?
	JRST	CHKAB2		;NO, SO KEEP GOING
	POPJ	P,		;YES, RETURN
;TSTMSK -- ROUTINE TO COMPARE TWO MASK BLOCKS FOR EQUALITY
;CALL:
;	MOVEI	T1,ADDR-OF-MASK BLOCK
;	MOVEI	T2,ADDR-OF MASK BLOCK
;	MOVEI	T3,LENGTH
;	PUSHJ	P,TSTMSK
;	  <IF DIFFERENT>
;	<IF EQUAL>

TSTMSK:	JUMPE	T1,TSTMS1	;IF ZERO ADDRESS (ASSUMES -1,...,-1)
	MOVE	T4,(T1)		;FIRST WORD OF MASK 1
	AOJA	T1,TSTMS2	;INCREMENT POINTER, JOIN PROCESSING

TSTMS1:	SETO	T4,		;ASSUME -1 FOR MISSING BLOCK
TSTMS2:	JUMPN	T2,TSTMS3	;IF NON-ZERO ADDRESS FOR MASK 2
	SETCA	T4,		;MASK-2 = -1,...,-1. SEE IF MASK 1 = -1
	JUMPN	T4,CPOPJ	;NO, NOT SAME
	SOJG	T3,TSTMSK	;THIS WORD MATCHES, CHECK NEXT
	JRST	CPOPJ1		;NO MORE.  WHOLE MATCH

TSTMS3:	EXCTUX	<CAME T4,(T2)>	;CHECK THIS WORD
	POPJ	P,		;NO MATCH.  RETURN FAILURE
	AOS	T2		;MATCH.  INCREMENT POINTER FOR BLOCK 2
	SOJG	T3,TSTMSK	;CHECK NEXT WORD
	JRST	CPOPJ1
;SUBROUTINE TO COUNT USERS SHARING A LOCK
;
;CALL:
;	MOVE	T1,LOCK-BLOCK-ADDR
;	PUSHJ	P,COUNTQ
;	HERE WITH COUNT IN T2, LOCK-BLOCK-ADDR STILL IN T1
;

COUNTQ:	MOVE	T3,T1		;SAVE LOCK BLOCK ADDR
	SETZ	T2,		;INITIALIZE COUNTER
COUNT2:	LOAD.	T3,.LBNQ,(T3)	;GET ADDR OF NEXT Q-BLOCK
	CAIN	T3,(T1)		;BACK AT START?
	POPJ	P,		;YES, RETURN
	LOAD.	T4,.QBFLG,(T3)	;GET Q-BLOCK FLAGS
	TRNN	T4,QBOWNR	;IS HE THE OWNER?
	JRST	COUNT2		;NO, DON'T COUNT THIS ENTRY
	AOJA	T2,COUNT2	;TRY NEXT BLOCK
	SUBTTL	BLDLOK -- SUBROUTINE TO BUILD A LOCK BLOCK



;SUBROUTINE TO CREATE A LOCK-BLOCK
;
;CALL:
;	MOVE	T1,HASH-INDEX
;	MOVE	T2,FLAGS
;	P1-P4 SETUP BY GETLOK
;	PUSHJ	P,BLDLOK
;	  HERE IF ERROR
;	NORMAL RETURN
;
;ON RETURN, T1 = ADDRESS OF LOCK-BLOCK
;
BLDLOK:	MOVEM	T1,EQTMP1	;SAVE HASH INDEX
	MOVEM	T2,EQTMP2	;SAME FOR FLAGS
	MOVE	T1,%ENQNQ##	;GET THE NUMBER OF LOCK BLOCKS
	CAML	T1,%ENQMQ##	;ARE WE ALREADY AT MAXIMUM?
	JRST	DOERR6		;YES, DON'T EAT UP ALL OF FREE CORE
	MOVEI	T2,1		;ASSUME LENGTH OF CODE/TEXT IS ONE
	TLNE	P1,EN%UCW	;CODE WORD?
	JRST	GOTLNG		;YES, LENGTH IS 1
	LDB	T3,[POINT 3,P2,2]	;GET THE STRING IDENTIFIER
	CAIN	T3,5		;IS IT A USER CODE?
	JRST	GOTLNG		;YES, SO WE ALREADY HAVE THE LENGTH
	MOVE	T3,P2		;GET THE STRING POINTER
	MOVEI	T2,0		;INITIALIZE COUNTER
BUILD2:	EXCTUX	<ILDB T4,T3>
	AOS	T2		;BUMP CHARACTER COUNT
	JUMPN	T4,BUILD2	;LEAVE LOOP ONLY IF NULL
	LDB	T4,[POINT 6,P2,11] ;GET BYTE SIZE TO
	MOVEI	T3,^D36		;BITS PER WORD
	IDIV	T3,T4		;BYTES PER WORD
	IDIV	T2,T3		;FIND # OF WORDS NEEDED
	SKIPE	T3		;WAS THERE A REMAINDER?
	AOS	T2		;YES, WE NEED ANOTHER WORD
GOTLNG:	ADDI	T2,LBSIZE	;ADD LENGTH OF LOCK BLOCK
	MOVEM	T2,EQTMP4	;SAVE IT FOR NOW
	PUSHJ	P,GETZWD	;GET SOME FREE CORE FOR IT
	  JRST	DOERR6		;NONE AVAILABLE
	MOVE	T2,EQTMP1	;GET HASH INDEX BACK
	MOVEI	T2,HSHTAB##(T2)	; AND ACTUAL ADDRESS OF ENTRY
	STOR.	T2,.LBLHS,(T1)	;STORE IT IN HASH CHAIN
	HRRZ	T3,(T2)		;GET FORWARD POINTER FOR HASH CHAIN
	HRRM	T1,0(T2)
	STOR.	T3,.LBNHS,(T1)	;NEXT HASH POINTER
	STOR.	T1,.LBLHS,(T3)	;MAKE NEXT ENTRY POINT TO US
	STOR.	T1,.LBNQ,(T1)	;MAKE THIS A NULL-QUEUE
	STOR.	T1,.LBLQ,(T1)	;...SAME

;CONTINUED ON THE NEXT PAGE...
;CONTINUED FROM THE PREVIOUS PAGE

	LDB	T2,[POINT 9,P1,17]	;GET LEVEL #
	STOR.	T2,.LBLVL,(T1)	;PUT IT INTO LOCK BLOCK
	HLRZ	T2,P3		;GET # OF RESOURCES IN REQUEST
	STOR.	T2,.LBPUL,(T1)	;PUT IT IN BLOCK
	STOR.	T2,.LBAVL,(T1)	;WITH SAME AMOUNT AS "AVAILABLE"
	STOR.	P4,.LBACC,(T1)	;STORE ACCESS TABLE ADDRESS
	MOVE	T3,EQTMP4	;GET LENGTH BACK AGAIN
	STOR.	T3,.LBLEN,(T1)	;LENGTH OF LOCK BLOCK
	SETZ	T2,		;CLEAR TIME STAMP
	STOR.	T2,.LBTIM,(T1)	;STORE IT
	PUSHJ	P,BLDTBL	;BUILD LOCK-ASSOCIATED TABLE, IF NEEDED
	  JRST	DOERR6		;INSUFFICIENT FREE CORE
	MOVE	T2,EQTMP2	;GET FLAGS AGAIN
	STOR.	T2,.LBFLG,(T1)	;PUT THEM AWAY
	TRNN	T2,LBTEXT	;IS THERE TEXT?
	JRST	[STOR.	P2,.LBTXT,(T1)	;NO, STORE USER CODE
		 JRST	CPOPJ1##]
	MOVE	T2,[POINT 7,O.TEXT(T1)]
	LDB	T3,[POINT 6,P2,11]
	DPB	T3,[POINT 6,T2,11]
BLD1:	EXCTUX	<ILDB T3,P2>	;START MOVING TEXT INTO BLOCK
	IDPB	T3,T2		;KEEP GOING
	JUMPN	T3,BLD1		;LOOP UNTIL NULL BYTE
	JRST	CPOPJ1##	;GIVE SKIP RETURN
	SUBTTL	ROUTINES TO BUILD AND DELETE A LOCK-ASSOCIATED TABLE.

;BLDTBL -- ROUTINE TO BUILD A LOCK-ASSOCIATED TABLE
;CALL:
;	MOVE	T1,LOCK BLOCK ADDRESS
;	PUSHJ	P,BLDTBL
;	  NO CORE RETURN
;	TABLE POINTER SETUP IN THE LOCK BLOCK

BLDTBL:	HLRZ	T2,EQLTBL	;LENGTH OF TABLE TO GET SPACE FOR
	JUMPE	T2,CPOPJ1##	;IF ZERO LENGTH, JUST RETURN
	PUSHJ	P,SAVE1##	;SAVE A REGISTER
	MOVE	P1,T1		;COPY POINTER TO LOCK BLOCK
	STOR.	T2,.LBTLN,(P1)	;SAVE LENGTH OF THE TABLE TOO
	PUSHJ	P,GETZWD	;GET THE SPACE
	  POPJ	P,		;NO SPACE
	STOR.	T1,.LBTBL,(P1)	;SAVE ADDRESS OF THE TABLE
	MOVE	T1,P1		;RESTORE T1
	JRST	CPOPJ1##	;AND GIVE GOOD RETURN


;ROUTINE TO DELETE A TABLE (IF ANY) ASSOCIATED WITH A LOCK BLOCK.
;CALL WITH
;	MOVE	T1,LOCK BLOCK ADDRESS
;	PUSHJ	P,DLTTBL
;	ALWAYS RETURNS HERE WITH TABLE DELETED
;USES T2-T4

DLTTBL:	LOAD.	T2,.LBTBL,(T1)	;ADDRESS OF THE TABLE
	JUMPE	T2,CPOPJ##	;DONE IF NONE
	PUSH	P,T1		;SAVE ADDRESS OF LOCK BLOCK
	LOAD.	T1,.LBTLN,(T1)	;LENGTH OF LOCK-ASSOCIATED TABLE
	PUSHJ	P,GIVWDS##	;RETURN THE SPACE TO THE MONITOR
	POP	P,T1		;RESTORE LOCK BLOCK ADDRESS
	SETZ	T2,		;ZERO
	STOR.	T2,.LBTBL,(T1)	;CLEAR TABLE ADDRESS
	STOR.	T2,.LBTLN,(T1)	;AND THE LENGTH
	POPJ	P,		;RETURN
	SUBTTL	ROUTINES TO MANAGE THE USER TABLE

;ROUTINE TO COPY INFORMATION FROM THE USER TO THE LOCK-TABLE

STOTBL:	SKIPN	EQLTBL		;ANY DATA GIVEN
	POPJ	P,		;NO, JUST RETURN
	LOAD.	T2,.QBFLG,(T1)	;GET FLAGS
	TRNE	T2,QBOWNR	;IS THE QUEUE ENTRY THE OWNER
	TRNN	T2,QBEXCL	;AND EXCLUSIVELY SO?
	POPJ	P,		;EITHER NOT OWNER OR NOT EXCLUSIVE
	PUSH	P,T1		;SAVE ADDRESS OF Q-BLOCK
	LOAD.	T1,.QBLB,(T1)	;ADDRESS OF LOCK BLOCK
	LOAD.	T2,.LBTBL,(T1)	;ADDRESS OF LOCK-ASSOCIATED TABLE
	JUMPE	T2,TPOPJ##
	HRL	T2,EQLTBL	;ADDRESS OF USERS DATA
	LOAD.	T3,.LBTLN,(T1)	;LENGTH OF MONITORS TABLE
	HLRZ	T4,EQLTBL	;LENGTH OF USERS DATA
	CAML	T4,T3		;USERS SHORTER?
	JRST	STOTB1		;NO, COPY AS MUCH AS MONITOR CAN HOLD
	PUSHJ	P,CLRTBL	;CLEAR MONITORS TABLE (SO ZERO FILL)
	HLRZ	T3,EQLTBL	;AND USE USERS LENGTH (WHICH IS SHORTER)
STOTB1:	ADDI	T3,-1(T2)	;LAST WORD ADDRESS
	EXCTUX	<BLT T2,(T3)>	;COPY USERS DATA
	JRST	TPOPJ##		;RESTORE T1 AND RETURN


;ROUTINE TO CLEAR THE CONTENTS OF THE TABLE IN MONITOR FREE SPACE
;TO ZEROS
;CALL WITH T1 POINTING TO LOCK BLOCK.

CLRTBL:	PUSHJ	P,SAVE2##	;PRESERVE P1-P2
	LOAD.	P1,.LBTBL,(T1)	;LOAD ADDRESS OF TABLE
	JUMPE	P1,CPOPJ##	;IF ZERO, TABLE IS ALREADY ZEROS
	HRL	P1,P1		;COPY ADDRESS TO LEFT HALF
	SETZM	(P1)		;CLEAR FIRST WORD
	LOAD.	P2,.LBTLN,(T1)	;LENGTH OF THE TABLE
	ADDI	P2,-1(P1)	;COMPUTE LAST WORD OF TABLE
	ADDI	P1,1		;MAKE PROPAGATING BLT POINTER
	CAIL	P2,(P1)		;LENGTH GREATER THAN ONE?
	BLT	P1,(P2)		;YES, CLEAR REST OF TABLE
	POPJ	P,		;AND RETURN
	SUBTTL	ROUTINES TO BUILD AND DELETE THE MASK BLOCK

BLDMSK:	SKIPN	EQLMSK		;DOES HE NEED THIS?
	JRST	CPOPJ1##	;NO, GIVE GOOD RETURN
	PUSHJ	P,SAVE1##	;SAVE P1
	MOVE	P1,T1		;ADDRESS OF QUEUE BLOCK
	HLRZ	T2,EQLMSK	;LENGTH OF USERS MASK
	LOAD.	T3,.QBLB,(P1)	;ADDRESS OF LOCK BLOCK
	LOAD.	T4,.LBNMS,(T3)	;LENGTH OF EXISTING MASKS
	JUMPN	T4,BLDMS1	;ALREADY HAVE SOME, CHECK FOR MATCH
	STOR.	T2,.LBNMS,(T3)	;STORE LENGTH IN LOCK BLOCK
	JRST	BLDMS2		;JOIN PROCESSING

BLDMS1:	CAME	T4,T2		;ARE THEY BOTH THE SAME?
	JRST	MWLERR		;NO, THIS IS AN ERROR
BLDMS2:	PUSHJ	P,GETZWD	;GET THE SPACE
	  JRST	DOERR6		;NO MORE FREE CORE
	STOR.	T1,.QBMSK,(P1)	;IN THE QUEUE BLOCK
	ADDI	T2,-1(T1)	;COMPUTE LAST WORD TO BE COPIED
	HRL	T1,EQLMSK	;SOURCE ADDRESS FOR COPY
	EXCTUX	<BLT T1,(T2)>	;COPY THE MASK
	MOVE	T1,P1		;RESTORE T1
	JRST	CPOPJ1##	;AND RETURN


;DLTMSK -- ROUTINE TO RETURN A MULTI-RESOURCE LOCK MASK BLOCK
;CALL:
;	MOVEI	T2,Q-BLOCK ADDRESS
;	PUSHJ	P,DLTMSK
;	<ALWAYS RETURNS HERE WITH MASK BLOCK DELETED>
;PRESERVES ALL AC'S

DLTMSK:	PUSHJ	P,SAVT##	;PRESERVE T1-T4
	LOAD.	T3,.QBLB,(T2)	;LOCATION OF THE LOCK BLOCK
	LOAD.	T2,.QBMSK,(T2)	;GET ADDRESS
	JUMPE	T2,CPOPJ##	;IF NONE
	LOAD.	T1,.LBNMS,(T3)	;THEN LENGTH OF MASK BLOCK
	PJRST	GIVWDS##	;RETURN SPACE TO MONITOR
	SUBTTL	QHIM -- SUBROUTINE TO BUILD A Q-BLOCK

;SUBROUTINE TO CREATE A QUEUE-BLOCK AND LINK IT
; INTO THE CHAIN OF QUEUED REQUESTS
;
;CALL:
;	MOVE	T1,LOCK-BLOCK-ADDR
;	MOVE	T2,ADDR-OF-Q-BLOCK-IN-MULTIPLE-REQUEST
;	MOVE	T3,FLAGS
;	PUSHJ	P,QHIM
;	  ERROR RETURN
;	NORMAL RETURN
;
;ON RETURN, T1 = ADDRESS OF NEW Q-BLOCK ENTRY
;
QHIM:	MOVEM	T1,EQTMP1	;SAVE LOCK-BLOCK ADDRESS
	MOVEM	T2,EQTMP2	;SAME FOR MULTIPLE REQUEST ADDR
	MOVEM	T3,EQTMP3	; AND FOR FLAGS
	MOVEI	T2,QBSIZE	;GET LENGTH OF Q-BLOCK
	PUSHJ	P,GETZWD	;GET SOME FREE SPACE FOR IT
	  POPJ	P,		; NO MORE LEFT!
	HRRZI	T2,.PDEQJ##(W)	;ADDRESS OF JOB-Q BEGINNING
	STOR.	T2,.QBLJQ,(T1)	;LINK INTO JOB CHAIN
	HRRZ	T3,(T2)		;GET FORWARD POINTER
	HRRM	T1,(T2)		;MAKE JOB Q POINT TO US
	STOR.	T3,.QBNJQ,(T1)	;MAKE US POINT TO START
	SKIPE	T3		;CHECK FOR END OF CHAIN
	STOR.	T1,.QBLJQ,(T3)	;ELSE, RELINK BACKWARD POINTERS
	MOVE	T2,EQTMP1	;GET LOCK BLOCK ADDR
	STOR.	T2,.QBNQ,(T1)	;PUT US INTO REGULAR CHAIN OF Q-BLOCKS
	LOAD.	T3,.LBLQ,(T2)	;GET POINTER TO LAST Q ENTRY
	STOR.	T3,.QBLQ,(T1)	;PUT US AFTER IT
	STOR.	T1,.QBNQ,(T3)	;MAKE LOCK BLOCK POINT TO US
	STOR.	T1,.LBLQ,(T2)	;WE ARE THE LAST ENTRY IN QUEUE
	STOR.	T2,.QBLB,(T1)	;MAKE POINTER TO LOCK BLOCK
	MOVE	T4,REQID	;GET THE REQUEST ID
	STOR.	T4,.QBRID,(T1)	;STORE ID IN Q-BLOCK
	MOVE	T3,EQTMP3	;FETCH FLAGS
	TLNE	P3,-1		;IS THIS A POOLED RESOURCE?
	TRZ	T3,QBEXCL	;YES, CLEAR THE EXCLUSIVE BIT
	STOR.	T3,.QBFLG,(T1)	;PUT THEM AWAY
	STOR.	P1,.QBCHN,(T1)	;PUT IN Q-BLOCK
	SKIPN	T2,EQTMP2	;IS THIS A MULTIPLE REQUEST?
	JRST	[STOR.	T1,.QBNQR,(T1)	;YES, SET UP LINKS FOR CHAIN
		 STOR.	T1,.QBLQR,(T1)	;MAKE US POINT TO OURSELVES
		 JRST QHIM2]		;AND FILL IN REST OF BLOCK
	LOAD.	T3,.QBNQR,(T2)	;GET ADDR OF NEXT ENTRY IN CHAIN
	STOR.	T1,.QBNQR,(T2)	;MAKE HIM POINT TO US
	STOR.	T1,.QBLQR,(T3)	;...
	STOR.	T3,.QBNQR,(T1)	;MAKE US POINT TO HIM
	STOR.	T2,.QBLQR,(T1)	;...
QHIM2:	MOVE	T2,.CPJCH##	;GET OUR JOB/CONTEXT HANDLE
	STOR.	T2,.QBJCH,(T1)	;STORE JOB/CONTEXT HANDLE

;CONTINUED ON THE NEXT PAGE
;CONTINUED FROM THE PREVIOUS PAGE

	SETZ	T2,		;SET UP TO CLEAR SOME STRUCTURES
	TLNN	P3,-1		;POOLED?
	EXCH	T2,P3		;NO
	STOR.	P3,.QBNRP,(T1)	;YES, STORE REQUESTED NUMBER
	STOR.	T2,.QBGRP,(T1)	;SET GROUP NUMBER
	JRST	CPOPJ1##	; AND EXIT
	SUBTTL	GENPSI AND CHKCNT

;SUBROUTINE TO GENERATE AN INTERRUPT FOR A USER
;
;CALL:
;	MOVE	T1,Q-BLOCK ADDRESS
;	MOVE	T2,FLAG		;0 FOR NORMAL, -1 FOR ABORTED REQUEST
;	PUSHJ	P,GENPSI
;	RETURN HERE ALWAYS
;
;
IFN FTPI,<
GENPSI:
	MOVE	T3,T2		;SAVE THE FLAG
	LOAD.	T2,.QBRID,(T1)	;GET REQUEST ID FROM Q-BLOCK
	SKIPE	T3		;WAS THIS AN ABORTED REQUEST?
	TLO	T2,(1B0)	;YES, LIGHT BIT IN STATUS WORD
	SIGNAL	C$QUE		;SIGNAL INTERRUPT
	  JFCL
	POPJ	P,		;RETURN
>;END IFN FTPI





;SUBROUTINE TO UPDATE THE CURRENT LOCK COUNTER AND
; DETERMINE IF THERE ARE MORE LOCKS TO BE PROCESSED
;
;CALL:	
;	PUSHJ	P,CHKCNT
;	  HERE IF THERE ARE MORE LOCKS
;	HERE IF ALL THRU
;
;ON ENTRY, THE WORD "LOCKCT" MUST BE SET UP AS FOLLOWS:
;	XWD	# OF LOCKS,,# OF THE CURRENT LOCK
;
CHKCNT:	AOS	T3,LOCKCT	;BUMP COUNTER
	TLZ	T3,-1		;CLEAR # LOCKS
	HLRZ	T2,LOCKCT	;GET NUMBER OF TOTAL LOCKS
	CAILE	T2,(T3)		;MORE TO GO?
	POPJ	P,		;YES
	JRST	CPOPJ1##	;NO, GIVE SKIP RETURN
	SUBTTL	ENQMIN -- ONCE A MINUTE CODE FOR QUESER

ENQMIN::PUSHJ	P,SAVE4##	;PRESERVE P1-P4
	MOVEI	P1,HSHLEN##-1	;START AT TOP OF HASH HEADER TABLE
ENQMN1:	MOVEI	P2,HSHTAB##(P1)	;START POINTER
ENQMN2:	LOAD.	P2,.LBNHS,(P2)	;POINT TO NEXT LOCK BLOCK ON CHAIN
	CAIN	P2,HSHTAB##(P1)	;REACHED END?
	JRST	ENQMN4		;YES, CHECK NEXT CHAIN
	LOAD.	T2,.LBFLG,(P2)	;GET FLAGS
	TRNN	T2,LBLLTL	;IS THIS A LONG-TERM-LOCK?
	JRST	ENQMN2		;NO, GET NEXT LOCK BLOCK
	LOAD.	T3,.LBNQ,(P2)	;YES, LOAD THE Q-BLOCK CHAIN POINTER
	CAIE	T3,(P2)		;IS THIS CHAIN EMPTY?
	JRST	ENQMN2		;NO, CHECK NEXT BLOCK
	LOAD.	T3,.LBPLT,(P2)	;FETCH TIMER VALUE
	SOJL	T3,ENQMN3	;COUNT DOWN, JUMP IF EXPIRED
	STOR.	T3,.LBPLT,(P2)	;RESTORE COUNT
	JRST	ENQMN2		;CHECK NEXT BLOCK

ENQMN3:	MOVEI	T1,(P2)		;ADDRESS OF THIS LOCK BLOCK
	LOAD.	P2,.LBLHS,(P2)	;BACKUP POINTER NOW, SINCE DELETING
	PUSHJ	P,DLTLOK	;DELETE THE LOCK BLOCK
	JRST	ENQMN2		;SEARCH REST OF CHAIN

;HERE TO ADVANCE TO THE NEXT HASH CHAIN

ENQMN4:	SOJGE	P1,ENQMN1	;DECREMENT POINTER, LOOP OVER ALL
	POPJ	P,		;RETURN AFTER CHECKING ALL LOCK BLOCKS
	SUBTTL	ENQSDT -- SET DAYTIME CODE FOR QUESER

;SUBROUTINE TO FIX UP DATE-TIME STAMPS IN ALL LOCK BLOCKS
;
;CALL:
;	MOVE	T1,DATE-TIME-OFFSET
;	PUSHJ	P,ENQSDT
;	RETURN HERE ALWAYS
;
;PRESERVES ALL

ENQSDT::PUSHJ	P,SAVE4##	;PRESERVE P1-P4
	MOVEI	P1,HSHLEN##-1	;START AT TOP OF HASH HEADER TABLE

ENQSD1:	MOVEI	P2,HSHTAB##(P1)	;START POINTER

ENQSD2:	LOAD.	P2,.LBNHS,(P2)	;POINT TO NEXT LOCK BLOCK ON CHAIN
	CAIN	P2,HSHTAB##(P1)	;REACHED END?
	JRST	ENQSD3		;YES, CHECK NEXT CHAIN
	LOAD.	P3,.LBTIM,(P2)	;GET TIME-STAMP OF LOCK
	JUMPE	P3,ENQSD2	;IF 0, NO TIME-STAMP WAS SET
	ADD	P3,T1		;FUDGE UP TIME-STAMP
	STOR.	P3,.LBTIM,(P2)	;STORE CORRECTED VALUE
	JRST	ENQSD2		;CHECK NEXT BLOCK

;HERE TO ADVANCE TO THE NEXT HASH CHAIN

ENQSD3:	SOJGE	P1,ENQSD1	;DECREMENT POINTER, LOOP OVER ALL
	POPJ	P,		;RETURN AFTER CHECKING ALL LOCK BLOCKS
	SUBTTL	MHASH -- SUBROUTINE TO HASH TWO NUMBERS TOGETHER

;SUBROUTINE TO HASH TWO NUMBERS TOGETHER
;
;CALL:	
;	MOVE	T1,NUMBER
;	MOVE	T2,NUMBER
;	PUSHJ	P,MHASH
;	RETURN HERE ALWAYS WITH HASH IN T1
;
MHASH:	XOR	T1,RANDOM	;GUARD AGAINST 0 IN T1
	XOR	T2,RANDOM	;SAME
	MUL	T2,RANDOM	;SCRAMBLE THINGS UP A LITTLE
	MUL	T1,T2		;MAKE IT REALLY RANDOM
	POPJ	P,

RANDOM:	EXP	5*5*5*5*5*5*5*5*5*5*5*5*5*5*5	;THIS IS 5^15
	SUBTTL	HASH -- SUBROUTINE TO HASH A STRING

;SUBROUTINE TO HASH A STRING
;
;CALL:
;	MOVE	T2,STRING-POINTER
;	PUSHJ	P,STHASH
;	RETURN HERE WITH HASH IN T1
;

STHASH:	PUSHJ	P,SAVE1##	;PRESERVE P1
	MOVEM	T2,EQTMP1	;SAVE POINTER
	SETZM	EQTMP2		;CLEAR ANSWER REGISTER
	LDB	T4,[POINT 6,T2,11] ;GET BYTE SIZE
	MOVEI	T3,44		;COMPUTE BYTES/WORD
	IDIV	T3,T4		;BYTE/WORD IN T3
	MOVE	P1,T3		; INTO P1
STHSH1:	MOVE	T4,P1		;GET BYTES/WORD FOR COUNT
	MOVE	T3,[POINT 7,T2]
	LDB	T2,[POINT 6,EQTMP1,11]
	DPB	T2,[POINT 6,T3,11]
	SETZ	T2,		;CLEAR RECEIVER AC
STHSH2:	EXCTUX	<ILDB T1,EQTMP1>;GET A BYTE FROM USER'S STRING
	JUMPE	T1,STHSH3	;END OF STRING?
	IDPB	T1,T3		;NO, STORE CHARACTER IN T2
	SOJG	T4,STHSH2	;LOOP BACK FOR CHARACTERS
	XOR	T2,EQTMP2	;XOR THIS INTO ANSWER WORD
	ROT	T2,1		;ROTATE TO MUSH BITS A LITTLE MORE
	MOVEM	T2,EQTMP2	;AND RE-STORE THE WORD
	JRST	STHSH1		;LOOP BACK UNTIL END OF STRING

STHSH3:	XORM	T2,EQTMP2	;STORE PARTIAL WORD TOO
	MOVE	T1,EQTMP2	;GET ANSWER
	POPJ	P,		;RETURN
	SUBTTL	HASH -- SUBROUTINE TO HASH A LOCK NAME

;SUBROUTINE TO CALCULATE AN INDEX INTO THE HASH TABLE
;
;CALL:
;	MOVE	P1,FLAGS,,ACCESS-TABLE-ADDR/-2/-3/400000+JOB #
;	MOVE	P2,USER CODE OR STRING POINTER
;	PUSHJ	P,HASH
;	NORMAL RETURN
;

HASH:	HRRE	T1,P4		;GET ACCESS-TABLE-ADDR/-2/-3/400000+JOB #
	MOVE	T2,P2		; AND STRING POINTER
	TLNE	P1,EN%UCW	;CODE WORD?
	JRST	HASH2		;YES, DON'T HASH IT
	LDB	T3,[POINT 3,T2,2] ;GET THE IDENTIFIER
	CAIN	T3,5		;IS IT A USER CODE?
	JRST	HASH2		;YES, DON'T HASH IT
	PUSH	P,T1		;SAVE CHANNEL #
	PUSHJ	P,STHASH	;HASH THE STRING
	POP	P,T2		;GET IT BACK AGAIN
HASH2:	PUSHJ	P,MHASH		;HASH THE ACCESS TABLE AND CODE/STRING
	MOVMS	T1		;MAKE IT POSITIVE
	IDIVI	T1,HSHLEN##	;DIVIDE BY SIZE OF HASH TABLE
	MOVE	T1,T2		;USE THE REMAINDER
	POPJ	P,		;RETURN
	SUBTTL	FNDLOK -- SUBROUTINE TO FIND A LOCK BLOCK

;SUBROUTINE TO FIND A LOCK-BLOCK
;
;CALL:
;	SET UP P1-P4 BY GETLOK
;	PUSHJ	P,FNDLOK
;
;	  RETURN HERE IF NOT FOUND
;	HERE IF FOUND, ADDRESS OF LOCK-BLOCK IN T1
;
;
FNDLOK:	PUSHJ	P,HASH		;HASH LOCK NAME
	MOVEM	T1,EQTMP4	;SAVE IT FOR LATER
	MOVEI	T1,HSHTAB##(T1)	;GET ADDRESS OF ENTRY
	MOVEM	T1,EQTMP5	;SAVE T1
FNDLK1:	LOAD.	T1,.LBNHS,(T1)	;ADVANCE TO NEXT LOCK-BLOCK
	CAMN	T1,EQTMP5	;HAVE WE EXHAUSTED LIST?
	POPJ	P,		;YES, BLOCK WAS NOT FOUND
	MOVEM	T1,EQTMP1	;REMEMBER THIS ADDRESS
	LOAD.	T2,.LBACC,(T1)	;GET ACCESS TABLE ADDRESS
	CAIE	T2,(P4)		;IS THIS A MATCH?
	JRST	FNDLK2		;NO, TRY NEXT ENTRY
	PUSHJ	P,STRCMP	;COMPARE THE STRINGS
	  JRST	FNDLK2		;NO MATCH
	MOVE	T1,EQTMP1	;GET BACK LOCK-BLOCK ADDRESS
	  PJRST	CPOPJ1##	;SKIP RETURN

FNDLK2:	MOVE	T1,EQTMP1	;GET LOCK-BLOCK ADDRESS
	JRST	FNDLK1		;GO ON
	SUBTTL	STRCMP -- SUBROUTINE TO COMPARE TWO STRINGS

;SUBROUTINE TO COMPARE STRINGS OR USER CODES
;
;CALL:
;	MOVE	T1,LOCK-BLOCK-ADDRESS
;	P1-P4 SET UP BY GETLOK
;	PUSHJ	P,STRCMP
;	  HERE IF NO MATCH
;	RETURN HERE IF MATCH
;
;

STRCMP:	TLNE	P1,EN%UCW	;CODE WORD?
	JRST	STRCMC		;YES
	LDB	T3,[POINT 3,P2,2]
	CAIN	T3,5		;IS THIS A USER CODE?
	JRST	STRCMC		;YES
	LOAD.	T3,.LBFLG,(T1)	;GET THE FLAGS
	TRNN	T3,LBTEXT	;TEXT?
	POPJ	P,		;NO
	PUSHJ	P,SAVE1##	;SAVE P1
	MOVE	P1,[POINT 7,O.TEXT(T1)]	;SET UP PTR TO TEXT
	LDB	T3,[POINT 6,P2,11]	;BYTE SIZE
	DPB	T3,[POINT 6,P1,11]
	MOVE	T2,P2		;COPY STRING POINTER
STRCM0:	EXCTUX	<ILDB T3,T2>	;GET A BYTE FROM USER
	ILDB	T4,P1		;AND ONE FROM LOCK BLOCK
	CAME	T3,T4		;A MATCH?
	POPJ	P,		;NO, RETURN
	JUMPN	T3,STRCM0	;YES, KEEP GOING IF NOT NULL
	PJRST	CPOPJ1##	;SKIP RETURN

STRCMC:	LOAD.	T3,.LBFLG,(T1)	;GET THE FLAGS FOR THE LOCK BLOCK
	TRNE	T3,LBTEXT	;IS THIS USER CODE?
	POPJ	P,		;NO
	LOAD.	T3,.LBTXT,(T1)	;GET USER CODE
	CAME	T3,P2		;MATCH?
	POPJ	P,		;NO
	PJRST	CPOPJ1##	;YES, SKIP RETURN
	SUBTTL	GETLVL -- SUBROUTINE TO FIND THE HIGHEST  LEVEL # FOR A GIVEN USER

;SUBROUTINE TO FIND THE HIGHEST LEVEL # FOR A GIVEN USER
;
;CALL:
;	MOVE	W,PDB-ADDRESS
;	PUSHJ	P,GETLVL
;	RETURN HERE ALWAYS
;
;
;ON RETURN, T1 = HIGHEST MONITOR LEVEL,,HIGHEST USER LEVEL
;	REQCNT = # OF OUTSTANDING REQUESTS FOR THIS USER
;
;
GETLVL:	
IFN FTMP,<
	PUSHJ	P,EQLOCK
>
	SETO	T1,		;ASSUME THE QUEUE IS EMPTY
	SETZM	REQCNT		;CLEAR REQUEST COUNT
	HRRZ	T2,.PDEQJ##(W)	;GET START OF JOB Q
GTLVL2:	JUMPE	T2,CPOPJ##	;EXIT IF NO Q
	LOAD.	T3,.QBLB,(T2)	;GET LOCK BLOCK
	LOAD.	T4,.LBLVL,(T3)	; AND ITS LEVEL #
	AOS	REQCNT		;BUMP COUNT OF REQUESTS
	LOAD.	P2,.LBACC,(T3)	;YES, GET FILE ID/-2/-3/400000+JOB #
	CAIE	P2,-3		;IS THIS A PRIVILEGED LOCK?
	JRST	GTLVL3		;NO, CHECK IT AGAINST HIGH USER LEVEL
	HLRE	T3,T1		;GET HIGHEST MONITOR LEVEL #
	CAMLE	T4,T3		;A NEW HIGH?
	HRL	T1,T4		;YES, UPDATE MONITOR LEVEL #
	JRST	GTLVL4		; AND KEEP GOING

GTLVL3:	HRRE	T3,T1		;GET HIGHEST USER'S LEVEL
	CAMLE	T4,T3		;A NEW HIGH?
	HRR	T1,T4		;YES
GTLVL4:	LOAD.	T2,.QBNJQ,(T2)	;RETURN ADDRESS OF Q-BLOCK
	JRST	GTLVL2		;NO, MORE TO GO
	SUBTTL	-- MISCELLANEOUS SUBROUTINES

;SUBROUTINE TO PERFORM COMMON SETUP FUNCTIONS
;
;CALL:
;	MOVE	T2,MAX-FUNCTION-CODE
;
;	PUSHJ	P,SETUP
;	  ERROR RETURN (ERROR CODE IN T1)
;	NORMAL RETURN (FUNCTION CODE IN T3)
;
;P4 IS RETURNED CLEAR
; AND THE FUNCTION CODE IS STORED IN THE RIGHT HALF OF "FCODE"
;
SETUP:	SETZB	P4,NQERRF	;INITIALIZE FLAGS
	HRR	M,T1		;GET PARAMETER BLOCK ADDR
	HRRZM	M,RBLOCK	;SAVE THIS ADDRESS FOR LATER
	HLRZ	T3,T1		;GET FUNCTION FROM CALL
	MOVEM	T3,FCODE	; AND SAVE IT FOR USE LATER
	CAILE	T3,(T2)		;IS IT WITHIN RANGE?
	JRST	NDUERR		;NO, ERROR RETURN
	TLO	M,400000	;SO ERRORS RETURN INSTEAD OF EXIT 
	JRST	CPOPJ1##	; TO STOTAC.  THEN GIVE GOOD RETURN


;ROUTINE TO ALLOCATE FREE CORE
;
;CALL:
;	MOVEI	T2,NUMBER OF WORDS
;	PUSHJ	P,GETZWD
;	  ERROR	RETURN
;	NORMAL	RETURN
;USES T1,T3,T4

GETZWD:	PUSH	P,T2		;
	PUSHJ	P,GETWDS##	;GET BLOCK
	PJRST	T2POPJ##
	MOVE	T2,(P)		;LENGTH
	ADDI	T2,-1(T1)	;LAST WORD
	SETZM	(T1)		;CLEAR FIRST
	MOVEI	T3,1(T1)	;DESTINATION
	HRL	T3,T1		;SOURCE
	BLT	T3,(T2)		;CLEAR BLOCK
	JRST	T2POJ1##	;
;SUBROUTINE TO GET THE EQ RESOURCE TO INTERLOCK SMP SYSTEMS
;
;CALL:
;	PUSHJ	P,EQLOCK
;	<RETURN HERE WITH INTERLOCK>
;
;AUTOMATICALLY FREES LOCK WHEN CALLER OF EQLOCK RETURNS

IFN FTMP,<
EQLOCK:	PUSH	P,NQERRF
	PUSH	P,LOKSIZ
	PUSH	P,HDRSIZ
	PUSH	P,RBLOCK
	PUSH	P,FCODE
	PUSH	P,LOCKCT
	PUSH	P,REQID
	PUSH	P,TIMLIM
	PUSH	P,HILEVL
	PUSH	P,SBLOCK
	PUSHJ	P,UPEQ		;WAIT FOR RESOURCE
	POP	P,SBLOCK
	POP	P,HILEVL
	POP	P,TIMLIM
	POP	P,REQID
	POP	P,LOCKCT
	POP	P,FCODE
	POP	P,RBLOCK
	POP	P,HDRSIZ
	POP	P,LOKSIZ
	POP	P,NQERRF
	PUSHJ	P,@0(P)		;RETURN TO CALLER OF EQLOCK
	  CAIA			;IF NON-SKIP RETURN
	AOS	-1(P)		;ADVANCE RETURN
	POP	P,(P)		;CLEAR EQLOCK CALLER'S ADDRESS
	PJRST	DWNEQ		;FREE RESOURCE AND RETURN

;SUBROUTINE TO CHECK TO SEE IF THE CURRENT JOB OWNS THE EQ
;RESOURCE
;CALL:
;	PUSHJ	P,HAVEQ
;	  <NO>
;	<YES>
;PRESERVES ALL

HAVEQ::	PUSH	P,T1
	HRRZ	T1,EQUSER##
	CAIE	T1,(J)
	JRST	TPOPJ##
	JRST	TPOPJ1##

UPEQ:	PUSH	P,F		;SAVE F
	SETZ	F,		;DON'T TOUCH EVM
	PUSHJ	P,EQWAIT##	;GET THE EQ
	JRST	FPOPJ##		;AND RESTORE F
DWNEQ:	PUSH	P,F		;SAVE F
	SETZ	F,		;DON'T TOUCH EVM
	PUSHJ	P,EQFREE##	;GIVE UP THE EQ
	JRST	FPOPJ##		;AND RESTORE F
>
;SUBROUTINE TO PERFORM CLEAN-UP ON A RESET (OR LOGOUT, OR POP)
;
;CALL:							HRRZ T1,NEWJCH
;							XMOVEI T2,NEWJQ
;	PUSHJ	P,ENQRST  -OR-	PUSHJ	P,ENQLGO  -OR-	PUSHJ P,ENQPOP
;	RETURN HERE ALWAYS
;
ENQRST::TDZA	T4,T4		;INDICATE RESET  (DEQ ALL BUT NDR)
ENQLGO::MOVEI	T4,1		;INDICATE LOGOUT (DEQ ALL)
	CAIA
ENQPOP::MOVEI	T4,2		;INDICATE POP	 (RENAME TO NEW JCH)
	PUSHJ	P,FNDPDS##	;FIND HIS PDB
	SKIPN	.PDEQJ##(W)	;DOES HE HAVE A QUEUE
	POPJ	P,		;IF NULL QUEUE
	PUSHJ	P,SAVE3##	;SAVE SOME ACS
	MOVE	P1,T4		;SAVE THE ENTRY CODE
	MOVE	P2,T1		;SAVE NEW JCH (FOR POPIT)
	MOVE	P3,T2		;SAVE NEW JOB QUEUE ADDRESS (FOR POPIT)
IFN FTMP,<
	PUSHJ	P,EQLOCK	;INTERLOCK THE ENQ/DEQ DATABASE
>
	MOVE	T2,.PDEQJ##(W)	;GET START OF JOB QUEUE
RESET2:	MOVE	T1,T2		;FETCH ADDRESS OF CURRENT BLOCK
	JUMPE	T1,CPOPJ##	;EXIT IF NO JOB QUEUE
	LOAD.	T2,.QBNJQ,(T1)	;GET NEXT ENTRY IN JOB Q
	PUSHJ	P,@[RSTIT		;RESET
		    DEQIT		;LOGOUT ALWAYS DEQUEUES
		    POPIT](P1)		;POP
	JRST	RESET2		;GO BACK TO START OF LOOP

RSTIT:	LOAD.	T4,.QBFLG,(T1)	;GET FLAGS FOR THIS Q-BLOCK
	TRNN	T4,QBOWNR	;IS THIS THE OWNER OF THE LOCK?
	PJRST	DEQIT		;NO, DON'T CHECK ETERNAL FLAG
	TRNN	T4,QBLNDR	;NOT TO BE DELETED ON RESET?
	PJRST	DEQIT		;IF NDR OPTION NOT SELECTED.
	SETO	T4,		;CHANNEL OF 777
	STOR.	T4,.QBCHN,(T1)	;WON'T MATCH ON NEXT FILE OPERATION
	PUSHJ	P,TSTAAC	;DO WE NEED TO INCRMENT THE A.T. COUNT?
	  POPJ	P,		;NO.  GO LOOK AT NEXT REQUEST
	LOAD.	T3,.QBLB,(T1)	;LOCK BLOCK ADDRESS
	LOAD.	T4,.LBFLG,(T3)	;YES, GET FLAGS FOR THIS LOCK BLOCK
	TRO	T4,LBLAUC	;NOTE THAT USE COUNT IS INCREMENTED
	STOR.	T4,.LBFLG,(T3)	;RESET FLAGS IN LOCK BLOCK
	LOAD.	T1,.LBACC,(T3)	;GET ACCESS TABLE ADDRESS
	CAIGE	T1,CODMIN	;IS IT REALLY A SPECIAL CODE?
	PUSHJ	P,FILIRC##	;NO, INCREMENT READER COUNT
	POPJ	P,		;LOOK FOR NEXT Q-BLOCK

POPIT:	PUSHJ	P,[PUSHJ P,SAVT##	;SAVE ALL TEMP ACS
		LOAD.	T1,.QBLB,(T1)	;GET LOCK BLOCK ADDRESS
		MOVE	T2,P2
		PJRST	CHECKQ]	;SEE IF USER IS ALREADY QUEUED
	  PJRST	DEQIT		;YES, DEQUEUE THIS LOCK
	LOAD.	T4,.QBLJQ,(T1)	;GET ADDRESS OF JOB QUEUE HEADER
	STOR.	T2,.QBNJQ,(T4)	;POINT JOB QUEUE TO NEXT Q-BLOCK
	SKIPE	T2		;IS THERE REALLY A NEXT Q-BLOCK?
	STOR.	T4,.QBLJQ,(T2)	;YES, POINT IT BACK TO JOB QUEUE HEADER
	STOR.	P2,.QBJCH,(T1)	;STORE NEW JCH IN Q-BLOCK
SE1XCT<	LOAD.	T3,.QBNJQ,(P3)	;GET FIRST Q-BLOCK IN NEW JOB QUEUE
	STOR.	T1,.QBNJQ,(P3)>	;POINT NEW JOB QUEUE HEADER TO Q-BLOCK
	STOR.	T3,.QBNJQ,(T1)	;POINT NEW FIRST Q-BLOCK TO OLD Q-BLOCK
	SKIPE	T3		;IS THERE REALLY AN OLD FIRST Q-BLOCK?
	STOR.	T1,.QBLJQ,(T3)	;YES, POINT IT BACK TO THE NEW Q-BLOCK
	POPJ	P,		;LOOK FOR NEXT Q-BLOCK
;TSTAAC -- ROUTINE TO DETERMINE IF WE NEED TO INCREMENT THE
;	   READ COUNT IN THE ACCESS TABLE TO MAKE IT STAY
;	   AROUND.
;CALL:
;	MOVE	T1,Q-BLOCK ADDRESS
;	LOAD.	T3,.QBLB,(T1)  ;LOCK BLOCK ADDRESS
;	PUSHJ	P,TSTAAC
;	  <NO NEED TO INCREMENT>
;	<MUST INCREMENT>

TSTAAC:	PUSHJ	P,SAVE4##	;SAVE P1-P4
	HRRZ	P1,.PDEQJ##(W)	;START OF JOBS QUEUE LIST
	LOAD.	P4,.LBACC,(T3)	;ACCESS TABLE TO BE CHECKED
TSTAA1:	LOAD.	P2,.QBLB,(P1)	;LOCK BLOCK ADDRESS OF THIS ENTRY
	LOAD.	P3,.LBACC,(P2)	;ACCESS TABLE FOR THIS LOCK
	CAIE	P3,(P4)		;SAME?
	JRST	TSTAA2		;NO, LOOK AT NEXT
	LOAD.	P3,.LBFLG,(P2)	;YES, SEE IF INCREMENTED
	TRNE	P3,LBLAUC	; BY CHECKING FLAG
	POPJ	P,		;YES. NO NEED TO INCREMENT AGAIN
TSTAA2:	CAIN	P1,(T1)		;SEARCHED TO CURRENT Q-BLOCK
	JRST	CPOPJ1##	;YES.  NOT YET INCREMENTED
	LOAD.	P1,.QBNJQ,(P1)	;ADVANCE TO NEXT Q-BLOCK
	JUMPN	P1,TSTAA1	;AND CHECK IT TOO
	STOPCD	.+1,JOB,ENQQFU	;++Q-BLOCKS FOULED UP
	JRST	CPOPJ1##	;INCRMENT AND HOPE
;SUBROUTINE TO PERFORM "CLOSE" ON FILE-LOCKS
; THIS ROUTINE IS CALLED ON EVERY CLOSE, OPEN, INIT,
; LOOKUP, AND ENTER  WHICH OCCURS.
;IT CHECKS TO SEE IF THERE ARE
; ANY OUTSTANDING LOCKS FOR A GIVEN CHANNEL.
; HOWEVER, IT PERFORMS NO ACTION IF IT FINDS ANY.
;
;CALL:
;	HRRI	P1,CHANNEL #
;	PUSHJ	P,ENQCLS##
;	  HERE IF THERE WERE LOCKS STILL OUT
;	HERE IF USER HAD NO LOCKS OUTSTANDING
;PRESERVES ALL
;
ENQCLS::PUSHJ	P,SAVJW##	;SAVE JOB/CONTEXT HANDLE
	MOVE	J,.CPJOB##	;GET USER'S JOB #
	PUSHJ	P,FNDPDS##	;GET HIS PDB
	SKIPN	.PDEQJ##(W)	;ANY LOCKS FOR THIS JOB?
	JRST	CPOPJ1##	;NO, LEAVE QUICKLY
	PUSHJ	P,SAVT##	;PRESERVE SOME AC'S
	HRRZ	T2,.PDEQJ##(W)	;GET START OF JOB Q
ENQCL2:	JUMPE	T2,CPOPJ1##	;LEAVE NOW IF HE HAS NO LOCKS
	MOVE	T1,T2		;UPDATE CURRENT BLOCK POINTER
	LOAD.	T3,.QBCHN,(T1)	;GET CHANNEL # FOR THIS LOCK
	LOAD.	T2,.QBNJQ,(T1)	;GET NEXT BLOCK IN JOB Q
	CAIN	T3,(P1)		;MATCHES THE CLOSED CHANNEL?
	POPJ	P,		;YES, GIVE ERROR RETURN
	JRST	ENQCL2		;NO, GET NEXT Q-BLOCK IN JOB QUEUE


;ROUTINE TO VERIFY THAT AN NDR LOCK IS PERMISSIBLE ON A SPECIFIED FILE.
;CALL:
;	MOVE	T1,LOCK-BLOCK ADDRESS
;	PUSHJ	P,OKNDR
;	  ERROR
;	OK
;PRESERVES ALL IF NO ERROR.  ELSE ERROR CODE IN T1

OKNDR:	PUSH	P,T1		;SAVE T1
	LOAD.	T1,.LBACC,(T1)	;GET ACCESS TABLE ADDRESS
	CAIL	T1,CODMIN	;IS IT REALLY A SPECIAL CODE?
	JRST	OKNDR1		;YES, OK
	PUSHJ	P,FILGFC##	;GHOST FILE? (BEING CREATED OR SUPERSEDED)
	  JRST	OKNDR2		;YES, ERROR
OKNDR1:	MOVSI	T1,JP.ENQ	;ENQ PRIVILEGE BIT
	PUSHJ	P,[PUSHJ P,SAVT##	;PRESERVE THE TEMP ACS
		   PJRST PRVBIT##]	;CHECK PRIVILEGE
	  JRST	TPOPJ1##	;OK, GIVE GOOD RETURN
	POP	P,T1		;RESTORE T1
	MOVEI	T1,ENQED%	;GIVE INSUFFICIENT PRIVILEGES ERROR
	POPJ	P,		;GIVE ERROR RETURN

OKNDR2:	POP	P,T1		;RESTORE T1
	MOVEI	T1,ENQGF%	;CANNOT USE EQ.FEL ON A GHOST FILE
	POPJ	P,		;ERROR RETURN

;ENQNDR -- CHECK FOR OUTSTANDING LOCKS WITH 'NO
;	   DELETE ON RESET' SET.
;
;CALL:
;	MOVE	T1,ACCESS TABLE ADDRESS
;	PUSHJ	P,ENQNDR
;	  <IF ANY SET>
;	 <IF NONE SET>
;PRESERVES ALL

ENQNDR::PUSHJ	P,SAVE4##	;SAVE P1-P4
	MOVEI	P1,HSHLEN##-1	;TABLE LENGTH
ENQND1:	MOVEI	P2,HSHTAB##(P1)	;
ENQND2:	LOAD.	P2,.LBNHS,(P2)	;GET NEXT
	CAIN	P2,HSHTAB##(P1)	;END OF CHAIN?
	JRST	ENQND4		;YES, CHECK NEXT HEADER
	LOAD.	P3,.LBACC,(P2)	;GET A.T. ADDRESS
	CAIE	P3,(T1)		;TARGET?
	JRST	ENQND2		;NO, LOOK AT NEXT LOCK BLOCK
	LOAD.	P3,.LBNQ,(P2)	;GET Q-BLOCK LIST
ENQND3:	CAIN	P3,(P2)		;END OF Q-BLOCK CHAIN?
	JRST	ENQND2		;YES, CHECK NEXT LOCK BLOCK
	LOAD.	P4,.QBFLG,(P3)	;Q-BLOCK FLAGS
	TRNE	P4,QBLNDR	;NDR REQUEST
	POPJ	P,		;YES, FAIL NOW
	LOAD.	P3,.QBNQ,(P3)	;NO. GET NEXT Q-BLOCK
	JRST	ENQND3		;LOOP OVER Q-BLOCK CHAIN

ENQND4:	SOJGE	P1,ENQND1	;ADVANCE TO NEXT BIN HEADER
	JRST	CPOPJ1##	;IF DONE


;ENQINI -- INITIALIZE THE QUEUEING SYSTEM (CALLED FROM SYSINI)
;
;CALL:
;	PUSHJ	P,ENQINI
;	RETURN HERE ALWAYS

ENQINI::SKIPL	T1,[-HSHLEN##,,HSHTAB##] ;AOBJN POINTER TO HASH TABLE
	POPJ	P,		;DONE IF NO ENQ/DEQ
ENQIN1:	HRRM	T1,(T1)		;MAKE RIGHT HALF POINT TO ITSELF
	HRLM	T1,(T1)		;AND LEFT HALF TOO
	AOBJN	T1,ENQIN1	;BUMP POINTER AND COUNTER
	POPJ	P,		;TABLE ALL DONE

;ENQJBI -- INITIALIZE THE PROCESS DATA BLOCK (FOR LOGIN OR PUSH)
;
;CALL:
;	PUSHJ	P,ENQJBI
;	RETURN HERE ALWAYS

ENQJBI::SETZM	.PDEQJ##(W)	;ZERO THE JOB QUEUE
	POPJ	P,		;DONE
	SUBTTL	ERROR MESSAGES AND RETURNS

;ERROR RETURNS
; SOME OF THESE RETURN TO THE USER (JRST STOTAC), AND SOME
; OF THEM MERELY RETURN FROM THE CURRENTLY ACTIVE SUBROUTINE

;	ERCODE	ERRTAB,ENQRU%	;(1) SOME RESOURCES UNAVAILABLE
	ERCODE	HDRER8,ENQBP%	;(2) ILLEGAL # OF RESOURCES REQUESTED.
	ERCODE	QCERR1,ENQBJ%	;(3) BAD JOB NUMBER
	ERCODE	HDRER1,ENQBB%	;(4) BAD BYTE SIZE
	ERCODE	HDRER2,ENQST%	;(5) STRING TOO LONG
	ERCODE	NDUERR,ENQBF%	;(6) BAD FUNCTION CODE
	ERCODE	BFERR,ENQBL%	;(7) ILLEGAL ARGUMENT BLOCK LENGTH
	ERCODE	HDRERB,ENQIC%	;(10) ILLEGAL # OF LOCKS SPECIFIED
	ERCODE	HDRER6,ENQBC%	;(11) BAD CHANNEL NUMBER
	ERCODE	HDRER7,ENQPI%	;(12) OPERATOR/JACCT PRIVILEGE REQUIRED
	ERCODE	DOERR6,ENQNC%	;(13) NO CORE AVAILABLE
	ERCODE	HDRER9,ENQFN%	;(14) FILE NOT OPEN, OR DEVICE NOT A DISK
	ERCODE	HDRERA,ENQIN%	;(15) INDIRECT BYTE POINTER
;	ERCODE	DERNJQ,ENQNO%	;(16) NO RESOURCES WERE OWNED
	ERCODE	ENQER1,ENQLS%	;(17) LEVEL # TOO LOW
;	ERCODE	DOERR5,ENQCC%	;(20) CAN'T CHANGE ACCESS
;	ERCODE	ENQER3,ENQQE%	;(21) QUOTA EXCEEDED
	ERCODE	DOERR2,ENQPD%	;(22) POOL COUNT DISCREPANCY
	ERCODE	DOERR3,ENQDR%	;(23) LOCK ALREADY REQUESTED
;	ERCODE	DOERR4,ENQNE%	;(24) NOT ENQ'D ON THIS LOCK
	ERCODE	DOERR0,ENQLD%	;(25) LEVEL # DESCREPANCY
	ERCODE	PRVERR,ENQED%	;(26) ENQ/DEQ PRIVILEGES REQUIRED
	ERCODE	MWLERR,ENQME%	;(27) MASK WORD LENGTH ERROR
	ERCODE	TBLERR,ENQTE%	;(30) TABLE ERROR
	ERCODE	ABTERR,ENQAB%	;(31) ATTEMPT TO ENQ. AN ABORTED LOCK
;	ERCODE	NDRERC,ENQGF%	;(32) ATTEMPT TO LOCK WITH NDR ON A 'GHOST FILE'
;	ERCODE	DEDERR,ENQDD%	;(33) DEADLOCK DETECTED
;	ERCODE	TLEERR,ENQTL%	;(34) TIME LIMIT EXCEEDED


;PRIVILEGES INSUFFICIENT (AND EXIT TO USER)
HDRERC:	MOVEI	T1,ENQPI%
	JRST	STOTAC##


;QUOTA EXCEEDED
ENQER3:	MOVEI	T1,ENQQE%	;QUOTA EXCEEDED
	JRST	STOTAC##


;BAD LENGTH GIVEN IN ARGUMENT BLOCK (FOR DUMP)
QCERR2:	MOVEI	T1,ENQBL%	;BAD DUMP BLOCK LENGTH
	JRST	STOTAC##

;NO REQUEST FOUND FOR LOCK
DOERR4:	MOVEI	T1,ENQNE%	;NO PENDING REQUEST FOUND
	JRST	MARETN		;GO STORE CODE

;CAN'T CHANGE ACCESS
DOERR5:	MOVEI	T1,ENQCC%	;CAN'T CHANGE ACCESS
MARETN:	HRRM	T1,NQERRF	;SAVE CODE
	JRST	CPOPJ1##	;RETURN FROM "ENQIT"


;RESOURCE NOT FOUND (DEQ ALL OR DEQ REQ:ID)
DERNJQ:	MOVEI	T1,ENQNO%	;NO RESOURCES FOUND
	JRST	STOTAC##	;GIVE USER THE ERROR CODE



;COME HERE TO MAKE A GOOD EXIT BACK TO THE USER 
;THE CONTENTS OF T1 ARE RETURNED IN THE UUO AC.
GUDXIT:	HRRZ	T1,NQERRF	;RETRIEVE ERROR CODE(FROM ENQ)
GUDXT2:	AOS	(P)		;BUMP RETURN ADDRESS
	JRST	STOTAC##	;AND GIVE IT BACK TO HIM
	SUBTTL	TEMPORARY STORAGE LOCATIONS FOR ENQ/DEQ
	$LOW

;
;NOTE THAT THESE LOCATIONS ARE NOT SAVED IF A JOB BECOMES
; BLOCKED. THEREFORE, THEY SHOULD BE USED ONLY FOR
; TEMPORARY STORAGE FOR THE DURATION OF THE PROCESSING OF
; THE UUO, EXCLUDING ANY SCHEDULING WHICH MAY OCCUR.
;
;WHEN ADDING OR DELETING VARIABLES
;FROM THIS AREA, BE SURE TO
;UPDATE THE VALUE OF QDBLEN IN
;COMMON SO IT IS THE SAME AS
;THE VALUE COMPUTED HERE
;


ENQ...==0		;VARIABLE FOR WORD MACRO

DEFINE WORD(SYM),<
SYM==.CPQTS##+ENQ...
ENQ...==ENQ...+1
>

DEFINE BLOK(SYM,N),<
SYM==.CPQTS##+ENQ...
ENQ...==ENQ...+N
>

BLOK(ENQFLG,1)		;-1 IF ENQ., ELSE 0
;***DO NOT SEPARATE OR REORDER THE FOLLOWING WORDS***
BLOK(USRLOK,3)		;COPY OF USERS LOCK BLOCK ARGUMENT
BLOK(EQLMSK,1)		;COPY OF MASK WORD FROM USER
BLOK(EQLTBL,1)		;COPY OF TABLE POINTER FROM USER
;***THE PREVIOUS GROUP MUST BE LKMAX WORDS LONG***
WORD(LOKSIZ)		;SIZE OF LOCK ENTRY FOR THIS CALL
WORD(HDRSIZ)		;SIZE OF HEADER FOR THIS CALL
WORD(FCODE)		;KEEP THE USER'S FUNCTION CODE HERE
WORD(HILEVL)		;HIGHEST LEVEL # ISSUED BY USER
WORD(RBLOCK)		;ADDRESS OF USER'S PARAMETER BLOCK
WORD(SBLOCK)		;ADDRESS OF USER'S STATUS BLOCK
WORD(LOCKCT)		;# OF LOCKS IN REQUEST,,# OF CURRENT LOCK
WORD(REQID)		;STORAGE FOR REQUEST ID
WORD(TIMLIM)		;TIME LIMIT TO OBTAIN LOCKS
WORD(NQERRF)		;NON-ZERO IF ERROR OCCURED
WORD(REQCNT)		;COUNT OF OUTSTANDING RESOURCE REQUESTS FOR THIS USER
WORD(LSTLOK)		;LAST LOCK-BLOCK CREATED (FOR DEBUGGING)
WORD(LASTQ)		;LAST Q-BLOCK CREATED (FOR DEBUGGING)
QDBLEN==:ENQ...		;DEFINE LENGTH OF DATABASE

;
; THESE ARE ALL SUPER-TEMPORARY VARIABLES ONLY USED WITH EQ RESOURCE
;

DQFLAG:	BLOCK	1	;NON:ZERO IF A LOCK WAS DEQ'D
EQTMP1:	BLOCK	1	;RANDOM TEMPORARIES
EQTMP2:	BLOCK	1	;..SAME
EQTMP3:	BLOCK	1	;..SAME
EQTMP4:	BLOCK	1	;..SAME
EQTMP5:	BLOCK	1	
LKTMP1:	BLOCK	1	;STORAGE FOR LOCK-SCHEDULER
LKTMP2:	BLOCK	1	;..SAME
LKTMP3:	BLOCK	1	;..SAME
LKTMP4:	BLOCK	1	;..SAME
QSKDF:	BLOCK	1	;STORAGE FOR Q-SCHEDULER
QSKDG:	BLOCK	1	;..SAME
QSKDT:	BLOCK	1	;..SAME
QSKDQ:	BLOCK	1	;..SAME
QSKDM:	BLOCK	1	;..SAME
QSKDN:	BLOCK	1	;..SAME
ENQTBC:	BLOCK	1	;POINTER TO NEXT Q-BLOCK TO BE DEADLOCK CHECKED
ENQHBC:	BLOCK	1	;POINTER TO NEXT Q-BLOCK ALREADY CHECKED

	$LIT

	END
############################################################