	.TITLE LOCK
	.IDENT /17JA87/
;
;	File:[22,310]LOCK.MAC
;	Author: Philip Hannay.  17-Jan-87.  Derived from Pasutl LOCKER
;	History: Philip Hannay.  17-Jan-87.  Created.
;
;  Phil Hannay  5-Jun-84  Modified UNLOCK procedure to wait 5 ticks after
;  unlocking the file (setting the flag) before exiting.  The reason for
;  this is to give another program waiting for this file a chance to get
;  in and lock the file.  Otherwise, this caller can relock the file
;  right away again if it desires and effectivly monopolize the file
;  until it blocks for some other reason.  Note that we use a WTSE for
;  the mark time so that we will not checkpoint unless absolutely 
;  required.
;
;	Last Edit: 19-JAN-1987 15:05:23 

.rem |

Function LOCK ( EFN:Event_Flag; TMO: integer): boolean; External;

{*USER*

LOCK a resource(s) associated with the global event flag EFN.  If the
resource is not available, continue trying for TMO seconds.  The function
LOCK returns a boolean value where TRUE is "resource locked for caller's use",
and FALSE is "resource locked by another program and could not be locked for
caller use within the TMO seconds"

The LOCK function is used for interprogram coordination, permitting multiple
programs to run and yet insure that only one among them can use a certain
resource at a time.  This function is useful for locking shared files,
regions and ports in a system.  The basic mechanism for signaling among
programs is global flags. Both global and group global flags can
be used.  

For all global flags that will be used, the lowest numbered
flag in each DEC defined 16 flag group is reserved for use by this
funtion for timeout handling.  This means that if we used flags 43 and
44 for locking, then flag 33 is also used.  If we use the group global
group 65 thru 80, we can use any of the flag from 66 thru 80 for 
locking, but we cannot use flag 65, since this routine will use it.

The function value returned is a boolean TRUE or FALSE.  There is no 
other status.  Any errors return the value of FALSE, giving little
info on the type of error.  For example, if you specify a flag out of
range or one of the reserve "timeout flags", it will be an error, and
a FALSE will be returned emulating a timeout.  It is up to the caller 
to do validity checks if need be or to trace input values.

Be sure group global flags are created before attempting to use them.
If the group globals do not exist, a directive error will occur in the
function, and the value of FALSE will be returned emulating a timeout.

Note:  This module uses a different locking scheme than normally used.
A set flag denotes an unlocked file, region or port,
while a clear flag denotes a locked file, region or port.
This procedure enables us to use waits triggered by flag
sets to make for instant response to a changed lock state while remaining
inactive during the wait interval.  This allows for the most efficient
interactive file, region and port sharing possible by multiple programs.

}

{*WIZARD*

The LOCK function sets up a WTLO wait for the specified resource associated
flag EFN and the "timeout flag".  The "timeout flag" is the lowest flag
in the 16 flag group.  It has been arbitrarily chosen as the lowest flag
of a flag group.

We want to wait for the "resource flag" to be set.  At the same time, we
don't want to wait forever, so we have to have a way to get going, so
we use another flag and do a Wait Logical Or.  We are constrained to use
another flag within the flag group, so that is why we use the lowest 
one.  

When the wait is satisfied, but are still unable to clear the
"resource flag", we cannot tell if we had a timeout or should
wait longer.  This is becuase the "timeout flag" is used by other 
programs also, and their timeouts might be happening and setting
the "timeout flag".  In other words, if the wait is satisfied, and yet
we cannot get the "resource flag", then we know it was the "timeout flag"
that satisfied the wait.  However, the program does not know if it
set the "timeout flag" or if another program set it.  That is, we know
it was a timeout, we just don't know who's timeout it was.  

To settle this timeout determination problem, we use an AST triggered by
a marktime.  Now we can use the AST to not only set the "timeout flag"
to wake us up, but also to put a 1 in a register that we cleared before
entering the wait.  The 1 in the register will signal us that it was 
our AST and thus our timeout that set the "timeout flag".

There is no provision to automatically reset a locked resource if the
caller fails to unlock it (program error or abnormal end).  If it is
required, the caller should look at an outside method (watchdog program)
that will unlock the resources automatically in the event of an error.

Under normal circumstances, if programs are solid, and error messages
are printed when timeouts occur, the user or tech can use the EFN program
to evalute the state of a resouce flag and force the unlock (set flag).

} 

|

;
; Assemble with PASMAC.MAC as prefix file.
;
;

	.ENABL	LSB
	.MCALL	CLEF$S
	.MCALL	MRKT$S
	.MCALL	ASTX$S
	.MCALL	SETF$S
	.MCALL	WTLO$S
	.MCALL	CMKT$S
	
	FUNC LOCK, STAT, BOOLEAN
	PARAM EFN, CHAR
	PARAM TMO, INTEGER
	SAVE <R0,R1,R2,R3,R4>
	BEGIN
	MOVB EFN(SP),R0			;MOVE EFN NUMBER TO R0
	CMP	R0,#33.			;CHECK TO SEE IF RESOURCE LOCK FLAG IS
					;WITHIN THE CORRECT RANGE 33 THRU 96
	BLT	19$			;TIMEOUT IF FLAG IS LESS THAN 34
	CMP	R0,#96.			;CHECK THE UPPER LIMIT
	BGT	19$			;EMULATE TIMEOUT IF FLAG GT 96
	CMP	R0,#57.			;CHECK TO SEE IF RESOURCE LOCK FLAG IS
					;WITHIN RESERVE SYS RANGE 57 THRU 64
	BLT	1$			;BRANCH IF FLAG IS LESS THAN 57
	CMP	R0,#64.			;CHECK THE UPPER LIMIT
	BLE	19$			;EMULATE TIMEOUT IF FLAG IN SYS RANGE
1$:	CMP	R0,#33.			;33 IS NOT A VALID FLAG FOR USE
	BEQ	19$			;EMULATE TIMEOUT IF FLAG IS 33
	CMP	R0,#49.			;49 IS NOT A VALID FLAG FOR USE
	BEQ	19$			;EMULATE TIMEOUT IF FLAG IS 49
	CMP	R0,#65.			;65 IS NOT A VALID FLAG FOR USE
	BEQ	19$			;EMULATE TIMEOUT IF FLAG IS 65
	CMP	R0,#81.			;81 IS NOT A VALID FLAG FOR USE
	BEQ	19$			;EMULATE TIMEOUT IF FLAG IS 81

					;VALID LOCK FLAG
	CLEF$S	R0			;TRY TO CLEAR (LOCK) RESOURCE FLAG
	CMP	$DSW,#IS.SET		;CHECK RETURN STATUS OF CLEAR COMMAND
	BEQ	20$			;BRANCH IF "WAS SET" - RESOURCE IS OURS
					;NO BRANCH - "WAS CLR", RESOURCE HELD
					;BY ANOTHER PROGRAM, SO WE SET UP A WAIT

					;FIGURE OUT THE GROUP WE ARE IN

 	MOV	#81.,R1			;ASSUME WE ARE IN GROUP 5, TMO FLAG 81
	CMP	R0,R1			;SEE IF WE ARE IN GROUP 5
	BGT	7$			;BRANCH IF WE ARE IN GROUP 5
 	MOV	#65.,R1			;ASSUME WE ARE IN GROUP 4, TMO FLAG 65
	CMP	R0,R1			;SEE IF WE ARE IN GROUP 4
	BGT	7$			;BRANCH IF WE ARE IN GROUP 4
 	MOV	#49.,R1			;ASSUME WE ARE IN GROUP 3, TMO FLAG 49
	CMP	R0,R1			;SEE IF WE ARE IN GROUP 3
	BGT	7$			;BRANCH IF WE ARE IN GROUP 3
 	MOV	#33.,R1			;MUST BE GROUP 2, TMO FLAG 33

					;TMO FLAG IS IN R1, NOW CREATE MASK
7$:	MOV	R0,R3			;R3 WILL BE A COUNTER MADE EQUAL TO
	SUB	R1,R3			;DIFFERENCE BETWEEN RESOURCE FLAG 
					;TIMEOUT FLAG
	CLR	R2			;WE WILL GENERATE AN "OR" MASK
					;IN R2 FOR USE IN A WTLO DIRECTIVE
	SEC				;SET THE C BIT
5$:	ROL	R2			;C BIT TO LOW BIT, HIGH BIT TO C BIT
	SOB	R3,5$			;LOOP UNTIL R3 BECOMES ZERO
	SEC				;SET THE C BIT AGAIN
	ROL	R2			;MOVE C BIT TO LOW BIT
					;WE NOW HAVE A MASK IN R2 FOR OUR WAIT
					;TO USE - THE TWO BITS CORRESSPONDING 
					;TO THE RESOURCE FLAG AND TMO FLAG ARE
					;SET, AND ALL OTHERS ARE CLEAR
	CLEF$S	R1			;CLEAR THE TIMEOUT FLAG
	MOV	TMO(SP),R4		;TIME DELAY MAGNITUDE IN R4 (SECONDS)
	CLR	R3			;R3 IS THE TIMEOUT BOOLEAN SET BY AST
					;IF SET AFTER WAIT, A TIMEOUT OCCURRED
	MRKT$S	,R4,#2,#PPLOCK		;WE SET UP A TIMEOUT AST TO PPLOCK
	CMP	$DSW,#IS.SUC		;CHECK THE DIRECTIVE STATUS
	BNE	19$			;EMULATE TIMEOUT IF UNABLE TO SETUP AST
	;
	; DUE TO SOME ODDITY IN THE WTLO DIRECTIVE, WE CANNOT DYNAMICALLY
	; GENERATE THE GROUP NUMBER.  (CANNOT USE MOV FORM).  SINCE WE WANT
	; TO BE ABLE TO PUT THIS IN A RO RESLIB, WE WILL GENERATE ALL 4
	; POSSIBLE DIRECTIVES.
	;
6$:	CMP	R1,#49.			;SEE WHICH GROUP WE ARE IN
	BLT	2$			;BRANCH IF IN GROUP 2
	BEQ	3$			;BRANCH IF IN GROUP 3
	CMP	R1,#81.			;GROUP 4 AND 5 ARE LEFT
	BLT	4$			;BRANCH IF IN GROUP 4
					;GROUP 5
	WTLO$S	5,R2			;WAIT UNTIL LOCK FLAG OR TMO FLAG SET
	BCS	19$			;EMULATE TIMEOUT ON DIRECTIVE ERROR
	BR	9$			;GO AROUND GROUP 2 WTLO
4$:	WTLO$S	4,R2			;WAIT UNTIL LOCK FLAG OR TMO FLAG SET
	BCS	19$			;EMULATE TIMEOUT ON DIRECTIVE ERROR
	BR	9$			;GO AROUND GROUP 2 WTLO
3$:	WTLO$S	3,R2			;WAIT UNTIL LOCK FLAG OR TMO FLAG SET
	BCS	19$			;EMULATE TIMEOUT ON DIRECTIVE ERROR
	BR	9$			;GO AROUND GROUP 2 WTLO
2$:	WTLO$S	2,R2			;WAIT UNTIL LOCK FLAG OR TMO FLAG SET
	BCS	19$			;EMULATE TIMEOUT ON DIRECTIVE ERROR

9$:	CLEF$S	R0			;CHECK TO SEE IF RESOURCE AVAILABLE
	CMP	$DSW,#IS.SET		;IT IS IF "WAS SET" IS RETURNED
	BEQ	20$			;BRANCH IF RESOURCE IS NOW OURS

					;NO RESOURCE, MAYBE OUR TIMEOUT WENT OFF
	TST	R3			;SEE IF THE TIMEOUT WOKE US UP
	BEQ	6$			;BRANCH IF IT WAS NOT THE TIMEOUT YET

19$:	CLRB	STAT(SP)		;SET STATUS FALSE TO SHOW TIMEOUT OR ERR
	BR	21$			;BRANCH AROUND SUCCESS PORTION

20$:	MOVB	#1,STAT(SP)		;SET STATUS TRUE TO SHOW RESOURCE OURS

21$:	CMKT$S				;CANCEL ANY OUTSTANDING MARK TIMES
;
;
;  HERE IS THE PPLOCK AST - SETS THE TIMEOUT BOOLEAN AND THE MASTER FLAG
;    TO WAKE UP WAITING ROUTINE
;
	.PSECT	PPLOCK,I
;
PPLOCK:	INC	R3			;SET THE TIMEOUT BOOLEAN (R3)
	MRKT$S	,#2,#2,#PPLOCK		;WE SET UP A SAFETY AST TO PPLOCK
					;THIS IS BECAUSE IT IS POSSIBLE THAT
                                        ;SOMEONE ELSE COULD CLEAR THE MASTER
                                        ;JUST AFTER WE SET IT AND BEFORE WE
                                        ;POPPED OUT OF THE AST.  THIS WILL
                                        ;INSURE THAT WE WON'T GET STUCK 
	SETF$S	R1			;SET THE MASTER FLAG TO WAKE UP JOB
	TST	(SP)+			;POP THE EFN AST EXCESS OFF STACK
	ASTX$S				;EXIT THIS AST, RETURN TO MAIN
	MOV	#5,R0			;AST EXIT FAILURE, MARK R0 WITH AN ID
	EMT 1				;BLOW UP THE PROGRAM
;
;  END OF AST
;
;
	.PSECT				;BACK TO THE BLANK PSECT
;



	ENDPR
	.END

