OpenVMS Source Code Demos
demo_lock_dlm_103.bas
1000 %title "DEMO_LOCK_DLM_xxx.BAS"
%ident "version_103.2" !
declare string constant k_version = "103.2" !
!========================================================================================================================
! Title : DEMO_LOCK_DLM_xxx.BAS
! Author : Neil Rieck (https://neilrieck.net)
! Created: 00.04.03
! Purpose: to demonstrate the use of the DISTRIBUTED LOCK MANAGER method to control access to a shared resource
! Notes : to see this program in action, run it three or more sessions each one started 2 seconds later in time
!
! Ver Who When What
! --- --- ------ --------------------------------------------------------------------------------------------------------
! 100 NSR 000403 1. original work
! NSR 050901 2. modified to compile correctly without needing "[.inc]VMS_Externals.inc"
! 101 NSR 051003 1. changed the $enqw calls to $enq (no wait)
! 2. simplified original example but now call $getlki to test the lock status (polling loop)
! 102 NSR 051006 1. modified for use with event flags (see lexical: %method)
! NSR 051008 2. minor tweak in lexical logic
! 103 NSR 051114 1. added support for a system-wide lock (requires priv: SYSLCK) bf_103.1
! 2. added code to view OpenVMS error text bf_103.2
!========================================================================================================================
! calls: $enq enqueue (async)
! $enqw enqueue wait (sync)
! $deq dequeue
! $getlki get lock info
!
! lock modes: lck$m_nlmode null
! lck$m_crmode concurrent read allows shared reading
! lck$m_cwmode concurrent write allows shared writing
! lck$m_prmode protected read allows shared read but no writers
! lck$m_pwmode protected write allows shared read but no other writers (other than self)
! lck$m_exmode exclusive allows no sharing with others
!========================================================================================================================
option type=explicit ! no kid stuff
declare string constant k_program = "DEMO_LOCK_DLM"
!
%include "starlet" %from %library "sys$library:basic$starlet" ! system services
%include "$ssdef" %from %library "sys$library:basic$starlet" ! ss$
%include "$lckdef" %from %library "sys$library:basic$starlet" ! lck$
%include "$lkidef" %from %library "sys$library:basic$starlet" ! lki$
%include "lib$routines" %from %library "sys$library:basic$starlet" ! lib$
!
external long function get_timer_bit_vector(long) ! required for used with SYS$WFLOR
external string function wcsm_dt_stamp ! returns time in this format: ccyymmddhhmmss
!
map (ErrorMsg) string vms_error_msg_buf = 256 ! for VMS error text
declare word vms_error_msg_len
!
declare long rc% ,! &
debug% ,! &
loop_counter% ,! &
dlm_event_flag% ,! dlm event flag &
timer_event_flag% ,! timer event flag &
dlm_ef_state% ,! &
timer_ef_state% ,! &
mask% ,! &
junk% ,! &
basic$quadword deltaQuad ,! &
string junk$ !
!
! define stuff to be used during $enq and $deq
!
record lock_block_rec ! define a new data structure
word lock_condition , ! &
reserved , ! &
long lock_ident , ! lock id# &
byte lock_value(16) ! (only required with flag: lck$m_valblk)
end record lock_block_rec !
declare lock_block_rec lock_block ! declare a variable
!
%let %method=1% ! 1=event flag method, 0=poll method
%if %method=0% %then ! poll method -------------------------------------
!
! define stuff to be used in a general purpose item list
!
record ItemRec
variant
case
group one
word Buf_Len ! buffer size (in bytes)
word Code ! desired operation
long Buf_Addr ! buffer address
long Rtrn_Len_Addr ! addr of bytes returned
end group one
case
group two
long list_term ! mark end-of-list
end group two
end variant
end record
!
! define stuff to be used in testing the status of a queue lock
!
record LkiRec ! structure of Lki Record
ItemRec ItemVar(9) ! 0 -> 9 items
end record LkiRec !
declare LkiRec LkiVar ! declare a variable
!
! define stuff to be used in testing the status of a queue lock
!
record LockStatusRec ! structure of a lock status record
byte byte0 ! LKI$B_STATE_RQMODE
byte byte1 ! LKI$B_STATE_GRMODE
byte byte2 ! LKI$B_STATE_QUEUE
end record LockStatusRec !
declare LockStatusRec LockStatus ! declare a variable
%end %if ! -------------------------------------------------
!
!========================================================================================================================
! main
!========================================================================================================================
main:
print k_program +"_"+ k_version !
print string$(len(k_program +"_"+ k_version), asc("=")) ! what will the opimtizer do with this?
debug% = 1 ! this should be set by logical name translation
%if %method=0% %then ! poll method -------------------------------------
print "-i-method = POLL"
%else
print "-i-method = EVENT FLAG"
%end %if
!
! allocate some event flags if not already done
! (this logic makes more sense in a loop or subroutine)
!
if dlm_event_flag% = 0 then ! if flag not yet allocated
rc% = lib$get_EF( dlm_event_flag% ) ! get an event flag
print "-e- lib$get-EF-dlm rc: "+str$(rc%) if ((rc% and 7%) <> 1)
end if
!
if timer_event_flag% = 0 then ! if flag not yet allocated
rc% = lib$get_EF( timer_event_flag% ) ! get an event flag
print "-e- lib$get-EF-timer rc: "+str$(rc%) if ((rc% and 7%) <> 1)
end if
!
! <<< request an exclusive lock on a named resource >>>
!
! SYS$ENQ [efn] ,lkmode ,lksb ,[flags] ,[resnam] ,[parid] ,[astadr] ,[astprm] ,[blkast] ,[acmode] ,[rsdm_id]
!
print "-i-enq ex"
rc% = sys$enq( dlm_event_flag% ,! efn: &
lck$k_exmode ,! lkmode: exclusive &
lock_block ,! lksb: &
lck$m_system ,! flags: system-wide lock bf_103.1 &
"NEIL_DEMO_9876" ,! resname: name of the protected resource &
,,,,,,, )
!
if (rc% and 7%) <> 1% then ! if not success
print "-e-enq ex rc: "+ str$(rc%) !
junk% = sys$getmsg(rc%, vms_error_msg_len, vms_error_msg_buf, 15%,) ! bf_103.2
if ((junk% and 7%) = 1) then !
print left$(vms_error_msg_buf, vms_error_msg_len) ! display possible priv problem
end if !
goto cleanup !
end if !
!
!
%if %method=0% %then ! poll method -------------------------------------
!
! <<< now test our lock status (because we might not have exclusive access) >>>
!
loop_counter% = 0 ! init counter
get_lock_status:
LkiVar::ItemVar(0)::Buf_Len = 3 ! buffer size (in bytes)
LkiVar::ItemVar(0)::Code = lki$_state ! desired operation
LkiVar::ItemVar(0)::Buf_Addr = loc(LockStatus) ! buffer address
LkiVar::ItemVar(0)::Rtrn_Len_Addr = 0 !
LkiVar::ItemVar(1)::list_term = 0 ! terminate the list
!
! SYS$GETLKI [efn] ,lkidadr ,itmlst [,iosb] [,astadr] [,astprm] [,nullarg]
!
rc% = sys$getlki( &
,! efn: &
lock_block::lock_ident ,! lkiadr: &
LkiVar::ItemVar(0)::Buf_Len ,! itmlst &
,! iosb &
,,)
print "-e-getlki rc: "+ str$(rc%) if (rc% and 7%) <> 1%
!
print "-i-Requested: ";LockStatus::byte0 !
print "-i-Granted : ";LockStatus::byte1 !
print "-i-Queue : ";LockStatus::byte2 !
if LockStatus::byte0 <> LockStatus::byte1 then
loop_counter% = loop_counter% + 1
print "-w- waiting for grant. Count: "+ str$(loop_counter%)
sleep 1
goto get_lock_status
end if
%else ! event flag method -------------------------------
!
! <<< wait here until we get exclusive access via DLM (or we time out)
!
! <<< arm a timer to expire 1 minute from now >>>
!
declare string constant k_delay1000 = "0 00:01:00" ! delay time 1 minute from now
rc% = sys$bintim(k_delay1000, DeltaQuad ) ! init delta time ('x' time from now)
print "-e- sys$bintim rc: "+ str$(rc%) if ((rc% and 7%) <> 1%) !
!
rc% = sys$setimr(timer_event_flag%,DeltaQuad by ref,,,) ! now use it to schedule a wake up
print "-e- sys$setimr rc: "+ str$(rc%) if ((rc% and 7%) <> 1%) !
!
! note: for the SYS$WFLOR call to work, both event flags must be in the same event flag cluster.
! The first parameter (on of the flags) is only used to determine which event flag cluster to test.
! The second parameter (mask) contains bits representing event flags within that cluster
!
mask% = get_timer_bit_vector( dlm_event_flag%) ! insert vector 1 into mask
mask% = mask% or get_timer_bit_vector(timer_event_flag%) ! insert vector 2 into mask
!
! <<< wait for either the 'DLM event flag' or the 'TIMER event flag' to change state >>>
!
print "-i-waiting for flag ";dlm_event_flag%;" or flag ";timer_event_flag%
junk$ = wcsm_dt_stamp ! get snap shot of current time
print "-i-sleep begin: ";left$(junk$,8)+"."+right$(junk$,9) !
rc% = sys$wflor( dlm_event_flag%, mask%) ! wait for a response from one of the event flags
print "-e- sys$waitfr rc: "+ str$(rc%) if ((rc% and 7%) <> 1%) !
junk$ = wcsm_dt_stamp ! get snap shot of current time
print "-i-sleep end: ";left$(junk$,8)+"."+right$(junk$,9) !
!
! <<< cancel all timer requests (if any) >>>
!
print "-i-Calling $CanTim" !
rc% = sys$cantim(,) ! cancel all timer requests
print "-e- sys$cantim rc: "+ str$(rc%) if ((rc% and 7%) <> 1%) !
!
! which event flag is set? DLM or TIMER?
!
rc% = sys$readEF(dlm_event_flag%, junk%) ! test DLM event flag
select rc%
case SS$_WASCLR
dlm_ef_state% = 0
case SS$_WASSET
dlm_ef_state% = 1
case else
print "-e- sys$readef-dlm rc: "+ str$(rc%)
end select
print "-i-DLM-EF-State:";str$(dlm_ef_state%) if debug% >= 1% !
!
rc% = sys$readEF(timer_event_flag%, junk%) ! test TIMER event flag
select rc%
case SS$_WASCLR
timer_ef_state% = 0
case SS$_WASSET
timer_ef_state% = 1
case else
print "-e- sys$readef-timer rc: "+ str$(rc%)
end select
print "-i-Timer-EF-State:";str$(timer_ef_state%) if debug% >= 1% !
!
! at this point either the DLM-EF is set
! or the TIMER-EF is set
! or BOTH are set
!
if ( dlm_ef_state% = 0) ! if the DLM-EF didn't get set
then ! oops
print "-e- Error, an exclusive lock on the resource was not received within 60 seconds"
goto dequeue !
end if
%end %if ! -------------------------------------------------
!
print "-i-starting fake work (15 seconds)"
sleep 15 ! do some work
print "-i-finished fake work"
!
! remove "our interest" in this resource
!
! SYS$DEQ [lkid] ,[valblk] ,[acmode] ,[flags]
!
dequeue:
print "-i-deq"
rc% = sys$deq( &
lock_block::lock_ident ,! lkid: &
,! valblk: &
,! acmode: &
LCK$M_DEQALL ! flags: &
)
print "-e-deq rc: "+ str$(rc%) if (rc% and 7%) <> 1%
!
!========================================================================================================================
! cleanup
! since we might want to pass rc% back to the END statement, don't use rc% here
!========================================================================================================================
cleanup:
if dlm_event_flag% <> 0 then ! if dlm EF is allocated...
junk% = lib$free_EF( dlm_event_flag% ) ! then free it
print "-e- lib$free-EF-dlm rc: "+str$(junk%) if ((junk% and 7%) <> 1)
end if !
if timer_event_flag% <> 0 then ! if timer EF is allocated...
junk% = lib$free_EF( timer_event_flag% ) ! then free it
print "-e- lib$free-EF-timer rc: "+str$(junk%) if ((junk% and 7%) <> 1)
end if !
!
! that's all folks
!
30000 print "adios..."
!~~~ end rc% x pass rc% back to DCL
end ! <<<---***
!########################################################################################################################
!
! external functions
!
!========================================================================================================================
! get timer bit vector
! (see OpenVMS system systevices documentation for "sys$wflor")
!
! notes: cluster event flags
! 0 00- 31
! 1 32- 63
! 2 64- 95
! 3 96-127
!========================================================================================================================
32010 function long get_timer_bit_vector(long event_flag)
option type = explicit
declare long temp
!
select event_flag
case <= 31
temp = event_flag
case <= 63
temp = event_flag - 32
case <= 95
temp = event_flag - 64
case else
temp = event_flag -96
end select
!
select temp ! this code will avoid an integer overflow
case 31 ! need to set bit #31
! 33222222222211111111110000000000
! 10987654321098765432109876543210
get_timer_bit_vector = B"10000000000000000000000000000000"L ! so return this
case else !
get_timer_bit_vector = (2% ^ temp) ! else return this
end select
!
end function ! get_timer_bit_vector
!
!========================================================================================================================
!
32020 function string Wcsm_DT_Stamp
!===================================================================================================================
! Title : Wcsm_DT_Stamp.inc
! Author : Neil S. Rieck
! Purpose: an external function to return a y2k compliant system time in the form ccyymmddhhmmss (14 chars)
! Notes : all our programs call this function so optimizations here will speed up the whole system
! History: deleted
!===================================================================================================================
option type=explicit ! cuz tricks are for kids...
!
%include "starlet" %from %library "sys$library:basic$starlet" ! system services
!
declare long sys_status
!
! this map is required for the call to sys$asctim (format: 19-JUN-1998 23:59:59.1)
!
map (WcsmDTStamp0) string Sys_buf_22 = 22
map (WcsmDTStamp0) string Sys_day = 2, ! &
Sys_dash1 = 1, !- &
Sys_month = 3, ! &
Sys_dash2 = 1, !- &
Sys_year = 4, ! &
Sys_space = 1, ! &
Sys_Hour = 2, ! &
Sys_colon1 = 1, !: &
Sys_Minute = 2, ! &
Sys_colon2 = 1, !: &
Sys_Second = 2, ! &
Sys_period = 1, !. &
Sys_Tenth = 1 !
!
! map for Wcsm date (output)
!
map (WcsmDTStamp1) string Wcsm_buf_14 = 14 !
map (WcsmDTStamp1) string Wcsm_year = 4, ! &
Wcsm_month = 2, ! &
Wcsm_day = 2, ! &
Wcsm_Hour = 2, ! &
Wcsm_Minute = 2, ! &
Wcsm_Second = 2
map (WcsmDTStamp1) string Wcsm_year = 4, ! &
Wcsm_month_tens = 1, ! &
Wcsm_month_ones = 1, ! &
Wcsm_day_tens = 1, ! &
Wcsm_day_ones = 1, ! &
Wcsm_Hour = 2, ! &
Wcsm_Minute = 2, ! &
Wcsm_Second = 2
!
! string constants
! 00000000011111111112222222222333333333
! 12345678901234567890123456789012345678
declare string constant k_month_names$ = "XXJANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"
! ||
! ++-- so I don't have to provide an offset in pos()
declare string constant my_space = '32'C
!
! <<< function 'code' starts here >>>
!
when error in
!
sys_status = sys$asctim(,Sys_buf_22,,) ! get ASCII time into sys_buf_22
!~~~ if (sys_status and 7%) <> 1% then cause error 11 x not required - call will never fail
!
! transfer data from one map to the other
!
Wcsm_year = Sys_year !
!~~~ rset Wcsm_month = str$( pos(k_month_names$,Sys_Month,1%) / 3%) x bf_100f
Wcsm_day = Sys_day !
Wcsm_hour = Sys_hour !
Wcsm_minute = Sys_minute !
Wcsm_second = Sys_second !
!
declare long temp% ! bf_100f
temp% = pos(k_month_names$,Sys_Month,1%) / 3% ! compute month number bf_100f
if temp% < 10% then ! if less than 10... bf_100f
Wcsm_month_ones = str$(temp%) ! ...then this goes into ONES bf_100f
Wcsm_month_tens = "0" ! ...and this goes into TENS bf_100f
else ! else >= 10 bf_100f
Wcsm_month = str$(temp%) ! bf_100f
end if
!
! make sure there are no spaces in the TENS area of our mapped variables (pad with '0' if necessary)
!
!~~~ Wcsm_month_tens = "0" if Wcsm_month_tens = my_space x disabled - see above code bf_100f
Wcsm_day_tens = "0" if Wcsm_day_tens = my_space !
!
! now pass result back to caller
!
Wcsm_DT_Stamp = Wcsm_Buf_14 ! this is it folks
use
Wcsm_DT_Stamp = "" ! error so return blank
end when
!
END Function
!
Back to
Home
Neil Rieck
Waterloo, Ontario, Canada.