	!STROBE.BAS -- Cause a blocking AST to be delivered on
	!	       LATCHed (or other) locks.
	!
	%TITLE "STROBE"
	%IDENT "V 2.10"
	! 2.10	13-JAN 91 Cleaned up and commented for publication
	! 2.00  25-MAY-89 Added CLI interface.
	!
	! 1.21	05-SEP-85 Moved call to SYS$ASCTIM to just before strobe
	!		  instead of just before prompt.
	! 1.20	22-AUG-85 Added ability to supress automatic prefixing of user
	!		  supplied lock resource name.
	! 1.10	11-JUN-85 Added $GETLKI for CSID and lock count.
	!
	PRINT "	STROBE V 2.10"
	%PAGE
	%SBTTL "RECORD declarations"
	!
	!Lock Status Block (lksb) RECORD definition
	!
	RECORD	LKSB
		WORD	L_STATUS
		WORD	FILL
		LONG	LOCK_ID
		BYTE	LVB(15)		!May need redefinition for any
					! given application. Always
					! 16 bytes.
	END RECORD

	!
	!Item list (itmlst) entry data type definition
	!
	RECORD ITMLST
		WORD	BUFF_LEN		!Target BUFFer LENgth
		WORD	ITEM_CODE		!Requested ITEM CODE
		LONG	BUFF_ADDR		!Target BUFFer ADDRess
		LONG	RET_LEN_ADDR		!RETurn actual LENgth 
						! into this ADDRess 
	END RECORD

	!
	!Create an IOSB data type
	!
	RECORD IOSB	!Special for $GETLKI
		LONG	GETLKI_STATUS
		LONG	XX_UNUSED_XX
	END RECORD
	%PAGE
	%SBTTL "Function declarations"
	!Local
	!	-none-
	!External
	EXTERNAL LONG	FUNCTION	SYS$ASCTIM,		&
					SYS$DEQ,		&
					SYS$ENQW,		&
					SYS$GETLKIW,		&
					LIB$SYS_FAO,		&
					CLI$PRESENT,		&
					CLI$GET_VALUE
	%PAGE
	%SBTTL "Constant declarations"
	!Local
	DECLARE LONG	CONSTANT	TRUE = -1,		&
					FALSE = 0

	!External
	EXTERNAL LONG	CONSTANT	SS$_NORMAL,		&
					LCK$K_CRMODE,		&
					LCK$K_EXMODE,		&
					LCK$M_CONVERT,		&
					LKI$_LCKCOUNT,		&
					LKI$_SYSTEM,		&
					CLI$_PRESENT,		&
					CLI$_ABSENT,		&
					CLI$_NEGATED,		&
					CLI$_DEFAULTED

	%PAGE
	%SBTTL "MAP/COMMON definitions"
	!
	!Item list and returned info buffer for $GETLKI
	!
	MAP	(LOCK_STUFF)				&
		ITMLST	LKCSID_IL,		!Entry 1 - CSID	&
		ITMLST	LKCOUNT_IL,		!Entry 2 - Lock Count	&
		LONG	END_OF_ITEM_LIST,	!End of list	&
		LONG	LKCSID,			!Returned CSID	&
		LONG	LKCOUNT                 !Returned Lock Count

	%PAGE
	%SBTTL "Variable declarations"
	!Local
	DECLARE	LONG	EV_FL,		!Event flag number	&
			I,		!Utillity variables	&
			SHOW_FLAG,	!/SHOW qualifier present	&
			NOW_FLAG,	!/NOW qualifier present	&
			RET_STAT,	!Return status		&
			RET_STAT2,	!Return status, the sequel&
			LCK_FLAGS,	!OR of lock flags	&
			EXIT_STATUS,	!Returned to DCL,	&
		STRING	LCK_FAO_CTRL,	!FAO control string	&
			TIME_STRING,				&
			RESOURCE,	!Resource name		&
			PREFIX,		!Resource name prefix	&
			ANS,		!User's answer		&
			TMP_STR,	!Temporary string	&
		LKSB	SBLK,		!Lock Status Block	&
		IOSB	GL_IOSB         !Status block for $GETLKI
	!External
	! -none-

	%PAGE
	%SBTTL "Initilization"

	ON ERROR GOTO CONDITION_HANDLER
	!Errors generated or trapped by VAX BASIC that is

	EV_FL = 4%	!Pick a flag, any flag -- event flag that is.
			!In a more complex program we would have to
			!exercise more care.

	EXIT_STATUS = 1%	!Assume everything will go well.  Problems
				!will change this value so that DCL can
				!do the right thing when we exit.

	!
	!Initialize the item list
	!
		!
		!  Item 1 -- Master system CSID 
	LKCSID_IL::BUFF_LEN = 4%	   !4 bytes = 1 longword
 	LKCSID_IL::ITEM_CODE = LKI$_SYSTEM !System mastering lock
	LKCSID_IL::BUFF_ADDR = LOC(LKCSID) !Address of LKCSID
	LKCSID_IL::RET_LEN_ADDR = 0%	   !Don't care about return length
		!
		!  Item 2 -- Count of locks granted
	LKCOUNT_IL::BUFF_LEN = 4%	      !4 bytes = 1 longword
	LKCOUNT_IL::ITEM_CODE = LKI$_LCKCOUNT !Count of locks granted for
					      ! for this resource
	LKCOUNT_IL::BUFF_ADDR = LOC(LKCOUNT)  !Address of LKCOUNT
	LKCOUNT_IL::RET_LEN_ADDR = 0%	      !Don't care about return length
		!
		!  Item 3 -- End of list
	END_OF_ITEM_LIST = 0%

	!
	!Various strings
	!
	TIME_STRING = SPACE$(23%)	!Buffer for time stamp
	LCK_FAO_CTRL = "Lock ID = !8XL    CSID = !8XL     Holder count = !SL"
					!FAO format for pertinent info
	%PAGE
	%SBTTL "Get information from command line."

	!Definitions for the CLI call backs.
	!**************  Resource Name
	RET_STAT = CLI$GET_VALUE("RESOURCE", RESOURCE)
	SELECT RET_STAT
	    CASE CLI$_PRESENT, SS$_NORMAL
		!PRINT " Resource name is '";RESOURCE;"'"     
	    CASE ELSE
		PRINT " Unexpected P1 status =";RET_STAT
	END SELECT

	!************** /PREFIX=[string]

	RET_STAT = CLI$PRESENT("Q1")
	SELECT RET_STAT
	    CASE CLI$_ABSENT
		!Prefix character unspecified, therefore...
		PREFIX = "LATCH_WAIT_FOR_"
	    CASE CLI$_NEGATED
		!No prefix is to be used, therefore...
		PREFIX = ""
	    CASE CLI$_PRESENT
		!A prefix was specified.  Go get it.
		RET_STAT2 = CLI$GET_VALUE("Q1", TMP_STR)
		SELECT RET_STAT2
		    CASE CLI$_PRESENT, SS$_NORMAL
			PREFIX = TMP_STR
		    CASE ELSE
			PRINT " Unexpected /PREFIX value. Status =";RET_STAT2
		END SELECT !RET_STAT2
	    CASE ELSE
		PRINT " Unexpected Q1 status =";RET_STAT
	END SELECT !RET_STAT

	RESOURCE = PREFIX + RESOURCE
		! ** Needs a check for 
		! ** resource name length not more than 32.]
	PRINT 'Lock resource name is "';RESOURCE;'"'

	%PAGE

	!************** /SHOW

	RET_STAT = CLI$PRESENT("Q2")
	SELECT RET_STAT
	    CASE CLI$_ABSENT, CLI$_NEGATED
		!SHOW mode has not been requested
		SHOW_FLAG = FALSE
	    CASE CLI$_PRESENT
		!GETLKI info will be shown
		SHOW_FLAG = TRUE
	    CASE ELSE
		PRINT " Unexpected /SHOW status =";RET_STAT
	END SELECT !RET_STAT

	!************** /NOW

	RET_STAT = CLI$PRESENT("Q3")
	SELECT RET_STAT
	    CASE CLI$_ABSENT, CLI$_NEGATED
		!Ask user whether to strobe
		NOW_FLAG = FALSE
	    CASE CLI$_PRESENT, CLI$_DEFAULTED
		!STROBE will be done without asking
		NOW_FLAG = TRUE
	    CASE ELSE
		PRINT " Unexpected /NOW status =";RET_STAT
	END SELECT !RET_STAT

	%PAGE
	%SBTTL "Get NL mode lock"
	!
	!SYS$ENQ system service to get compatible lock.
	!
	LCK_FLAGS = 0%	!No flags for this call
	RET_STAT = SYS$ENQW(					&
				EV_FL BY VALUE,		![efn]		&
				LCK$K_CRMODE BY VALUE,	!lkmode		&
				SBLK::L_STATUS,		!lksb		&
				LCK_FLAGS BY VALUE,	![flags]	&
				RESOURCE,		![resnam]	&
				,			![parid]	&
				,			![astpar]	&
				,			![astprm]	&
				,			![blkast]	&
				,			![acmode]	&
							!nullarg	&
				)
	! Did the call succeed?
	IF RET_STAT <> SS$_NORMAL THEN
		PRINT "%STROBE --    CR MODE lock SYS$ENQ failure"
		PRINT "%STROBE --    RETURN STATUS =";RET_STAT
		EXIT_STATUS = 4%	!FATAL error
		GOTO ALL_DONE
	END IF
	! Did the request succeed
	IF SBLK::L_STATUS <> SS$_NORMAL THEN
		PRINT "%STROBE --    CR MODE lock aquisition failure"
		PRINT "%STROBE --    LKSB status =";SBLK::L_STATUS
		EXIT_STATUS = 4%	!FATAL error
		GOTO ALL_DONE
	END IF

	%PAGE
	%SBTTL "Call $GETLKI system service"

 GET_INFO:
	!
	!SYS$GETLKIW system service to get master CSID and lock count
	!
	RET_STAT = SYS$GETLKIW(				&
				EV_FL BY VALUE,	![efn]		&
				SBLK::LOCK_ID,	!lkidadr	&
				LKCSID_IL,	!itmlst		&
				GL_IOSB,	![iosb]		&
				,		![astadr]	&
				,		![astprm]	&
						!nullarg	&
				)
	! Did the call succeed?
	IF RET_STAT <> SS$_NORMAL THEN
		PRINT "%STROBE --    $GETLKI failure"
		PRINT "%STROBE --    RETURN STATUS =";RET_STAT
		EXIT_STATUS = 4%	!FATAL error
		GOTO ALL_DONE
	END IF
	! Did the $GETLKI service succeed?
	IF GL_IOSB::GETLKI_STATUS <> SS$_NORMAL THEN
		PRINT "%STROBE --    $GETLKI failure"
		PRINT "%STROBE --    IOSB status =";GL_IOSB::GETLKI_STATUS
		EXIT_STATUS = 4%	!FATAL error
		GOTO ALL_DONE
	END IF

	!USER NOTE:
	!Holder count will always be at least one because this program
	!holds a null lock on the resource.  Would it lessen the confusion
	!display LKCOUNT-1 and call it "Other holders"?
	!
	%PAGE
	%SBTTL "Show what's happenin'"
	! Let SYS$FAO format the info.  Use LIB$ jacket because LIB$
	! knows how to handle dynamic strings.
	RET_STAT =  LIB$SYS_FAO(				&
				LCK_FAO_CTRL,		&
				,			&
				ANS,			&
				SBLK::LOCK_ID BY VALUE,	&
				LKCSID BY VALUE,	&
				LKCOUNT BY VALUE	&
				)

	IF (RET_STAT AND 1%) <> 1%
	THEN
		PRINT "%STROBE --    LIB$SYS_FAO failure"
		PRINT "%STROBE --    RETURN STATUS =";RET_STAT
		EXIT_STATUS = 0%	!WARNING 
	END IF

	PRINT		!blank line
	PRINT ANS
	GOTO ALL_DONE IF SHOW_FLAG = TRUE	!Only lock info was wanted
	GOTO DO_STROBE IF NOW_FLAG = TRUE	!Don't ask, just do it.
	%PAGE
	%SBTTL "User input"

 PROMPT:
	!User knows the score now.  Ask what to do.
	INPUT " Continue [(Y),N,E]";TMP_STR
	TMP_STR = "Y" IF TMP_STR = ""		!Default is "Y"
	TMP_STR = EDIT$(TMP_STR,34%)		!Make it uppercase
	I = ASCII(TMP_STR)			!Look only at first letter
	SELECT I
		CASE 89%	!Answer was "Y".
			GOTO DO_STROBE
		CASE 78%	!Answer was "N".
			GOTO GET_INFO
		CASE 69%	!Answer was "E".
			GOTO ALL_DONE
		CASE ELSE	!Answer was not "Y","N" or "E".
				! so give a little help.
			PRINT "Invalid response. Choices are:"
			PRINT "	Y  Strobe resource name and exit program."
			PRINT "	N  Do not strobe, print holder info again."
			PRINT "	E  Do not strobe, exit program."
			PRINT
			GOTO PROMPT	!Ask again
	END SELECT

 DO_STROBE:
	RET_STAT = SYS$ASCTIM(,TIME_STRING,,)	!Get current time
	IF (RET_STAT AND 1%) <> 1%
	THEN
		PRINT "%STROBE --    SYS$ASCTIM failure"
		PRINT "%STROBE --    RETURN STATUS =";RET_STAT
		PRINT "% Progam continuing.  Time values will be meaningless."
		EXIT_STATUS = 0%	!WARNING
	END IF



	PRINT "%STROBE --   Strobing ";RESOURCE;".   ";TIME_STRING
				!Announce what we're doing and when.
	%PAGE
	%SBTTL "Get incompatible LATCH lock"
	!
	!SYS$ENQ system service to get EX mode lock
	!
	LCK_FLAGS = LCK$M_CONVERT	!Set flag for conversion
	RET_STAT = SYS$ENQW(					&
				EV_FL BY VALUE,		![efn]		&
				LCK$K_EXMODE BY VALUE,	!lkmode		&
				SBLK::L_STATUS,		!lksb		&
				LCK_FLAGS BY VALUE,	![flags]	&
				,			![resnam]	&
				,			![parid]	&
				,			![astpar]	&
				,			![astprm]	&
				,			![blkast]	&
				,			![acmode]	&
							!nullarg	&
				)
	!Did call succeed?
	IF RET_STAT <> SS$_NORMAL THEN
		PRINT "%STROBE --    EX MODE lock aquisition failure"
		PRINT "%STROBE --    RETURN STATUS =";RET_STAT
		EXIT_STATUS = 4%	!FATAL error
		GOTO ALL_DONE
	END IF
	!Did request succeed?
	IF SBLK::L_STATUS <> SS$_NORMAL THEN
		PRINT "%STROBE --    EX MODE lock aquisition failure"
		PRINT "%STROBE --    LKSB status =";SBLK::L_STATUS
		EXIT_STATUS = 4%	!FATAL error
		GOTO ALL_DONE
	END IF
	RET_STAT = SYS$ASCTIM(,TIME_STRING,,)	!Get current time
	IF (RET_STAT AND 1%) <> 1%
	THEN
		PRINT " %Error getting current time."
		PRINT " %Status ="; RET_STAT
		PRINT " % Progam continuing.  Time values will be meaningless."
		EXIT_STATUS = 0%	!WARNING
	END IF

	!When we get here we have acquired an EX mode lock.  This implies that
	!all LATCH programs have released their CR mode locks and exited.

	PRINT "%STROBE --   ";RESOURCE;" strobes delivered.  ";TIME_STRING
				!Announce what was done and when.
	%PAGE
	%SBTTL "Cancel the lock"
	!
	! SYS$DEQ system service to explicitly dequeue the lock
	! using value in the lock status block of the $ENQ
	!
	RET_STAT = SYS$DEQ(SBLK::LOCK_ID BY VALUE,,,)
				!Explicitly try to dequeue the lock
	IF RET_STAT <> SS$_NORMAL THEN
		PRINT "%STROBE --    $DEQ failure"
		PRINT "%STROBE --    RETURN STATUS =";RET_STAT
		!Exiting the image will
		! release the (user mode) lock.
		EXIT_STATUS = 0%	!WARNING
	END IF

	GOTO ALL_DONE

	%PAGE
	%SBTTL "Condition handler."
 CONDITION_HANDLER:
	SELECT ERR
	   CASE 11	!In case user answers with ^Z
		PRINT "%STROBE --   EOF signaled"
		RESUME ALL_DONE
	   CASE ELSE	!All others, complain and bail out.
		PRINT "**ERR =";ERR
		PRINT "**";ERT$(ERR)
		PRINT "**From ";ERN$
		EXIT_STATUS = 4%	!FATAL error
		RESUME ALL_DONE
	END SELECT
 ALL_DONE:
	EXIT PROGRAM EXIT_STATUS	!Tell DCL what happened
	END PROGRAM




	!STROBE.CLD - Command Language Definition for STROBE

DEFINE VERB STROBE

IMAGE "VPRO:STROBE"

	PARAMETER P1, LABEL=RESOURCE, VALUE(REQUIRED),
		  PROMPT="Resource name to strobe? "

	QUALIFIER PREFIX, LABEL = Q1, PLACEMENT=GLOBAL, VALUE
	!Default value will be supplied by program.  Could have been done here

	QUALIFIER SHOW,   LABEL = Q2, PLACEMENT=GLOBAL

	QUALIFIER NOW,    LABEL = Q3, PLACEMENT=GLOBAL, BATCH
	!Note that this qualifier is on by default in BATCH




	!LATCH.BAS -- LATCH on to a lock.  Release when blocking.
	!
	%TITLE "LATCH"
	%IDENT "V 2.10"
	! 2.10	13-JAN-91	Cleaned up and commented for publication
	! 2.00	30-MAY-89	Replace LIB$GET_FOREIGN with CLI$ interface
	! 1.10	11-JUN-85	Added explicit $DEQ
	!
	PRINT "	LATCH V2.10"
	%PAGE
	%SBTTL "RECORD definition"
	!
	!Lock Status Block (lksb) RECORD definition
	!
	RECORD	LKSB
		WORD	L_STATUS
		WORD	FILL
		LONG	LOCK_ID
		BYTE	LVB(15)		!May need redefinition for any
					! given application. Always
					! 16 bytes.
	END RECORD

	%PAGE
	%SBTTL "Constant definitions"
	!Local
	! -none-
	!External

	EXTERNAL LONG CONSTANT		SS$_NORMAL,		&
					LCK$K_CRMODE,		&
					CLI$_PRESENT,		&
					CLI$_ABSENT,		&
					CLI$_NEGATED,		&
					BLK_AST_H	!Address of blocking
							!AST in a module
							!to be LINKed
							!with this program.
	%PAGE
	%SBTTL "Function definitions"
	!Local
	! -none-
	!External
	EXTERNAL LONG	FUNCTION	SYS$ASCTIM,		&
					SYS$DEQ,		&
					SYS$ENQW,		&
					SYS$HIBER,		&
					LIB$SYS_FAO,		&
					CLI$PRESENT,		&
					CLI$GET_VALUE

	%PAGE
	%SBTTL "MAP/COMMON definitions"

	MAP (AST_COMMON)		&
		LONG	EXIT_STATUS

	!This is in a MAP so that it can be shared with the AST routine.
	!Standard (unMAPped) VAX BASIC variables are allocated on the
	!stack and are unavailable to AST routines.

	%PAGE
	%SBTTL "Variable definitions"
	!Local
	DECLARE	LONG						&
			RET_STAT,	!Return status from system services&
			RET_STAT2,	!Return status, the sequel	&
			EV_FL,		!Event flag			&
		STRING	FAO_OUT,	!Output from $FAO		&
			LCK_FAO_CTRL,	!FAO control string		&
			TIME_STRING,				&
			RESOURCE,	!Resource name		&
			PREFIX,		!Prefix from command line	&
					! or default value		&
			TMP_STR,	!Utility string			&
		LKSB	SBLK		!Status block

	!External
	! -none-
	%PAGE
	%SBTTL "Initialization"

	ON ERROR GOTO CONDITION_HANDLER
	!VAX BASIC detected errors, that is

	EV_FL = 4%	!Pick an event flag.  A more complex program would
			!require more care in selecting a value.


	EXIT_STATUS = 1%	!Assume everything will go well.  Problems
				!will change this value so that DCL can
				!do the right thing when we exit.

	TIME_STRING = SPACE$(23%)	!Pre-extend the string
	LCK_FAO_CTRL = "Lock ID = !8XL"
	%PAGE
	%SBTTL "Get information from command line."

	!**************  Resource Name
	RET_STAT = CLI$GET_VALUE("RESOURCE", RESOURCE)
	SELECT RET_STAT
	    CASE CLI$_PRESENT, SS$_NORMAL
		!PRINT " Resource name is '";RESOURCE;"'"     
	    CASE ELSE
		PRINT " Unexpected P1 status =";RET_STAT
	END SELECT

	!************** /[NO]PREFIX=[string]

	RET_STAT = CLI$PRESENT("Q1")
	SELECT RET_STAT
	    CASE CLI$_ABSENT
		!PRINT " Prefix character unspecified."
		PREFIX = "LATCH_WAIT_FOR_"
	    CASE CLI$_NEGATED
		!PRINT " No prefix will be used."
		PREFIX = ""
	    CASE CLI$_PRESENT
		RET_STAT2 = CLI$GET_VALUE("Q1", TMP_STR)
		SELECT RET_STAT2
		    CASE CLI$_PRESENT, SS$_NORMAL
			!PRINT " Prefix will be '";TMP_STR;"'"
			PREFIX = TMP_STR
		    CASE ELSE
			!PRINT " Unexpected Q1 VALUE status =";RET_STAT2
		END SELECT !RET_STAT2
	    CASE ELSE
		PRINT " Unexpected Q1 status =";RET_STAT
	END SELECT !RET_STAT

		!** Needs a check for
		!** resource name length not more than 32.
	RESOURCE = PREFIX + RESOURCE
	PRINT 'LATCH resource name is "';RESOURCE;'"'
	PRINT	!Blank line
	%PAGE
	%SBTTL "Get LATCH lock"
	!
	!SYS$ENQ system service to get CR mode lock
	!	...with a blocking AST declared
	!
	RET_STAT = SYS$ENQW(					&
				EV_FL BY VALUE,		![efn]		&
				LCK$K_CRMODE BY VALUE,	!lkmode		&
				SBLK::L_STATUS,		!lksb		&
				,			![flags]	&
				RESOURCE,		![resnam]	&
				,			![parid]	&
				,			![astpar]	&
				,			![astprm]	&
				BLK_AST_H BY VALUE,	![blkast]	&
				,			![acmode]	&
							!nullarg	&
				)
	! Did the call succeed?                         
	IF RET_STAT <> SS$_NORMAL THEN
		PRINT "%LATCH --    Lock aquisition CALL failure"
		PRINT "%LATCH --    RETURN STATUS =";RET_STAT
		EXIT_STATUS = 4%	!FATAL error
		GOTO ALL_DONE
	END IF
	! Did the $ENQ succeed
	IF SBLK::L_STATUS <> SS$_NORMAL THEN
		PRINT "%LATCH --    Lock aquisition SERVICE failure"
		PRINT "%LATCH --    LKSB status =";SBLK::L_STATUS
		EXIT_STATUS = 4%	!FATAL error
		GOTO ALL_DONE
	END IF

	RET_STAT = SYS$ASCTIM(,TIME_STRING,,)	!Get current time
	IF RET_STAT <> SS$_NORMAL
	THEN
		PRINT "%LATCH --    Pre hibernation $ASCTIM failure"
		PRINT "%LATCH --    RETURN STATUS =";RET_STAT
		PRINT "%LATCH --    Program continuing.  Time values will be meaningless."
		EXIT_STATUS = 0%	!WARNING
	END IF

	PRINT	!blank line
	!Announce who, what and when.
	PRINT "%LATCH --   Waiting for ";RESOURCE;" to be strobed."
	PRINT HT + TIME_STRING

	RET_STAT = LIB$SYS_FAO(LCK_FAO_CTRL, ,FAO_OUT,SBLK::LOCK_ID BY VALUE)
		!Convert lock ID to hex string
	IF RET_STAT <> SS$_NORMAL
	THEN
		PRINT "%LATCH --    $FAO failure"
		PRINT "%LATCH --    RETURN STATUS =";RET_STAT
		PRINT "%LATCH --    Program continuing." +	&
		      "No lock ID information will be displayed."
		EXIT_STATUS = 0%	!WARNING
	ELSE
		PRINT FAO_OUT	!Formated information
	END IF

	!
	!Now hibernate ...
	!	Blocking AST will wake us up.
	!
	RET_STAT = SYS$HIBER
	IF RET_STAT <> SS$_NORMAL
	THEN
		PRINT "%LATCH --    $HIBER failure"
		PRINT "%LATCH --    RETURN STATUS =";RET_STAT
		EXIT_STATUS = 4%	!FATAL error
		GOTO ALL_DONE
	END IF
	!
	!...blocking AST has been delivered.  Release the lock and exit.
	!

	RET_STAT = SYS$ASCTIM(,TIME_STRING,,)	!Get current time
	IF RET_STAT <> SS$_NORMAL
	THEN
		PRINT "%LATCH --    Post lock $ASCTIM failure"
		PRINT "%LATCH --    RETURN STATUS =";RET_STAT
		PRINT "%LATCH --    Program continuing.  Time values will be meaningless."
		EXIT_STATUS = 0%	!WARNING
	END IF
	!
	!Announce lock release
	!
	PRINT "%LATCH --   ";RESOURCE;" strobed."
	PRINT HT + TIME_STRING
	PRINT
	!
	! SYS$DEQ system service to explicitly dequeue the lock
	! using value in the lock status block of the $ENQ
	!
	RET_STAT = SYS$DEQ(SBLK::LOCK_ID BY VALUE,,,)
				!Explicitly try to dequeue the lock
	IF RET_STAT <> SS$_NORMAL THEN
		PRINT "%LATCH --    $DEQ failure"
		PRINT "%LATCH --    RETURN STATUS =";RET_STAT
		!Exiting the image will
		! release the (user mode) lock.
		EXIT_STATUS = 0%	!WARNING
	END IF
	GOTO ALL_DONE
	%PAGE
	%SBTTL "Condition handler."

 CONDITION_HANDLER:
		!No handleable errors are expected, ergo, complain and exit
		PRINT "**ERR =";ERR
		PRINT "**";ERT$(ERR)
		PRINT "**From ";ERN$
		EXIT_STATUS = 4%	!FATAL
		RESUME ALL_DONE

 ALL_DONE:
	EXIT PROGRAM EXIT_STATUS	!Tell DCL what happened
	END PROGRAM



	!LATCH.CLD - Command Language Definition for LATCH

DEFINE VERB LATCH

IMAGE "VPRO:LATCH"

	PARAMETER P1, LABEL=RESOURCE, VALUE(REQUIRED),
		  PROMPT="Resource name to latch? "

	QUALIFIER PREFIX, LABEL = Q1, PLACEMENT=GLOBAL, VALUE
	!Default is supplied by the program.  Could have been done here.




	!LCK_BAST_HIB.BAS -- LoCK Blocking AST handler.
	!		     It will WAKE a hibernating process
	!		      process.
	!Written as a VAX BASIC subroutine.
	SUB BLK_AST_H (LONG PARAM,PC,PSL,R0,R1)
			!Standard parameter list passed to any AST.
			!PC,PSL,R0 and R1 are not used here.
			!PARAM is used only in case of error.
	%TITLE "Lock blocking AST handler to wake main routine."
	%IDENT "V 1.10"

	EXTERNAL LONG	FUNCTION	SYS$WAKE

	MAP (AST_COMMON)  LONG EXIT_STATUS	!To communicate to the main
						!routine if somethng goes
						!wrong.

	DECLARE	LONG	RET_STAT

	RET_STAT = SYS$WAKE(,)		!Null arguments mean wake
					!this process.
	IF (RET_STAT AND 1%) <> 1% THEN
		PRINT "BAST***WAKE failure."
		PRINT "STATUS =";RET_STAT
		PRINT "AST parameter =";PARAM
		EXIT_STATUS = 4%	!FATAL
	END IF
	!As soon as this AST is dismissed, the main program will resume
	!at the next instruction after the $HIBER call.
	END SUB




	.TITLE LKIDEF
	.IDENT /V1.00/
	;Assembling this code will create an object file with the definitions
	;for the $GETLKI system service.  The object file can then be linked
	;with programs needing these definitions.

	$LKIDEF GLOBAL	;Make them global symbols

	.END



	.TITLE LCKDEF
	.IDENT /V1.00/
	;Assembling this code will create an object file with the definitions
	;for the $ENQ system service.  The object file can then be linked
	;with programs needing these definitions.

	$LCKDEF GLOBAL	;Make them global symbols

	.END



$	!STROBE_LATCH_BUILD.COM - Procedure to build STROBE and LATCH
$	!
$	!
$	WRITE SYS$OUTPUT "Assembling symbol definition files..."
$	MACRO LCKDEF	!Symbols used by $ENQ.  Needed for STROBE and LATCH
$	MACRO LKIDEF	!Symbols for $GETLKI.  Needed by STROBE
$	!
$	WRITE SYS$OUTPUT "Building STROBE..."
$	BASIC/LIST STROBE	!Compile main program
$	LINK/MAP  STROBE,LKIDEF,LCKDEF
$				!Link with symbol definitions
$	!
$	WRITE SYS$OUTPUT "Building LATCH..."
$	BASIC/LIST LCK_BAST_HIB
$				!Compile blocking AST routine
$	BASIC/LIST LATCH	!Compile main program
$	LINK/MAP  LATCH,LCK_BAST_HIB,LCKDEF
$				!Link with AST routine and symbol definitions
$	WRITE SYS$OUTPUT "STROBE and LATCH built..."
$	EXIT



                        STROBE and LATCH "User Guide"
		       ===============================

1.0 INTRODUCTION
    ------------

This document is a supplement to the article titled "LATCH and STROBE: Using
the VAX Distributed Lock Manager to Synchronize Processing Across a VAXcluster
System", published in VAX Professional (February 1991).

2.0 INSTALLATION
    ------------

Copy the files to a directory of your choice.  Edit the *.CLD files to reflect
the device and directory where the *.EXE files are located.

Execute BUILD.COM

You must use the SET COMMAND command before you can use LATCH or STROBE.

3.0 COMMAND DESCRIPTIONS
    --------------------

  STROBE

	This command causes an exclusive mode lock to be requested on
	the resource named in the command.  By default, the resource name
	will be modified by the program.  (See /PREFIX, below.)

	The command will display some information including the count of
	holders of locks on the resource.  This count includes the null
	mode lock held by STROBE at the time the command executes.

	After the information is displayed, you are asked about what STROBE
	should do next.  You have three choices:
		Y - Request the EX mode lock
		N - Display the holder count again
		E - Exit the program without requesting the EX mode lock.
	The prompt can be suppressed by the /NOW or the /SHOW qualifier.

	This command is designed to be used in conjunction with the LATCH
	program.

	Command syntax:

	STROBE resource_name

	Qualifiers:

	/NOW

	This qualifier causes the EX mode lock to be requested as soon as
	the holder information has been displayed.

	This qualifier is in effect by default in batch.

	/PREFIX

	STROBE normally appends the string "LATCH_WAIT_FOR_" to the beginning
	of the resource name.  Using /NOPREFIX will cause the resource name
	specified on the command line to be used unmodified.

	To specify a different prefix use /PREFIX=your_choice

	Keep in mind that the total resource name cannot exceed 32
	characters.  (STROBE does not currently check the length.)

	/SHOW

	This qualifier causes the program to exit after displaying the
	holder count.  There will be no prompt.  The /SHOW qualifier
	overrides the /NOW qualifier.


  LATCH

	This command causes a concurrent mode lock to be requested on
	the resource named in the command.  By default, the resource name
	will be modified by the program.  (See /PREFIX, below.)

	The program then hibernates until an incompatible lock is requested
	for the concurrent read mode lock it holds.

	This command is designed to be used in conjunction with the STROBE
	program.

	Command syntax:

	LATCH resource_name

	Qualifiers:

	/PREFIX

	LATCH normally appends the string "LATCH_WAIT_FOR_" to the beginning
	of the resource name.  Using /NOPREFIX will cause the resource name
	specified on the command line to be used unmodified.

	To specify a different prefix use /PREFIX=your_choice

	Keep in mind that the total resource name cannot exceed 32
	characters.  (LATCH does not currently check the length.)


4.0 PROGRAM DESIGN NOTES
    --------------------

Both STROBE and LATCH print their own error messages.  They should use the
message facility under VMS.  This is left as an exercise for the reader.

Both programs will exit with a warning or fatal error when a problem is
detected.  The severity of the error depends on the problem.  See the source
code for more information.   The DCL symbols $STATUS and $SEVERITY will 
contain this value. The choices were made to make batch operation, for LATCH 
in particular, function properly.  That is, ON...THEN can be used to handle 
errors and cause remedial action to take place instead of blindly moving on to 
the next command.

The /PREFIX behavior suits my purposes.  It can be changed fairly easily in the
program or moved into the .CLD files.

STROBE and LATCH do not take out a system lock (this requires SYSLCK privilege).
This means that they will only be effective within a given UIC group.

For those unfamiliar with VAX BASIC, the RECORD statement describes data
structures.  The RECORD statement does not allocate memory but it provides a
template for subsequent allocation in a DECLARE or MAP statement.

For example,
	RECORD LWW
		LONG VARL1
		WORD VARW1
		WORD VARW2
	END RECORD
does not allocate memory. It is when it is declared that memory is allocated,
as in -

	DECLARE LWW SBLOCK, IBLOCK

In this case each variable is 2 longwords.  RECORD essentially allows you to
design your own data types.


5.0  POSSIBLE VARIATIONS
     -------------------

Have STROBE use LIB$SET_SYMBOL to store the holder count in a DCL symbol.  This
could then be examined later in the command procedure.

Create an /EXPECTED=n qualifier.  STROBE would wait for this many holders
before requesting the exclusive lock.  This should probably be accompanied by an
appropriate timer mechanism to prevent STROBE from waiting too long.

Some applications may find it useful to have LATCH also display the holder
count.  It would be an indication of the sequence in which the locks were 
acquired.

A /SYSLCK qualifier could be created so locks would not be limited to a 
given UIC group.



$	!ST_TEST.COM -	BATCH job to test strobe and latch.  As many of these
$	!		as desired (but that don't exceed JOBLIM) may be
$	!		submitted to batch queues throughout the VAXcluster
$	!		system.
$	!
$	THIS_NODE = F$GETSYI("NODENAME")
$	SHO SYM THIS_NODE
$	TMP = F$GETJPI("","PID")
$	TMP = F$EXTRACT(4,4,TMP)
$	SET PROC/NAME="LATCH ''TMP'"	!Prevents duplicate process names
$	set command vpro:latch
$	latch testing
$	! **This is where something useful would happen.**
$	EXIT



