OpenVMS Source Code Demos
basic_timed_spawn
1000 %title "basic-timed-spawn_xxx.bas" !
%ident "version 100.2" ! <<<---+---
declare string constant k_version = "100.2" , ! <<<---+ &
k_program = "basic-timed-spawn" !
!=======================================================================
! title : basic-timed-spawn.bas
! author : Neil Rieck ( https://neilrieck.net/ )
! created: 2017-09-11
! purpose: We have batch jobs which spawn thousands of times a day. However, every ~200 days a
! spawned job will hang. This demo will show how to detect and correct that situation
! ver who when what
! --- --- -------- -----------------------------------------------------
! 100 NSR 20170911 1. original effort
! NSR 20170912 2. added code to delete a hung process
!=======================================================================
option type=explicit !
set no prompt !
declare string constant k_script = "basic-timed-spawn-junk.com", &
k_output = "basic-timed-spawn-junk.out", &
k_lines = "---------------------------------" !
!
%include "starlet" %from %library "sys$library:basic$starlet" ! system services
%include "$ssdef" %from %library "sys$library:basic$starlet" ! ss$
%include "lib$routines" %from %library "sys$library:basic$starlet" ! lib$
%include "$clidef" %from %library "sys$library:basic$starlet" ! cli$
!
external long function get_timer_bit_vector(long) ! required for used with SYS$WFLOR
!
declare long ef_timer ,&
long ef_spawn ,&
byte ef_byte ,&
long ef_timer_state ,&
long ef_spawn_state ,&
long spawn_flags ,&
long rc1 ,&
long rc2 ,&
long pid ,&
long mask ,&
long junk ,&
long time0, time1 ,&
long test ,&
basic$quadword DeltaQuad1 !
declare string delay$ ,&
string cmd$ ,&
string junk$ !
!=======================================================================
! main
!=======================================================================
2000 main:
print k_program +"_"+ k_version !
print string$(len(k_program +"_"+ k_version), asc("=")) !
!
input "-?-create a command file? (y/N) ";junk$ !
if edit$(junk$,32)="Y" then
print "-i-creating DCL script for test purposes"
when error in
open k_script for output as #99 &
,organization sequential &
,recordsize 99
print #99, '$! title: '; k_script
print #99, '$ say :== write sys$output'
print #99, '$ say "-i-start-script:", f$environment("procedure")'
print #99, '$ say "-i-starting 4 second delay at: ",f$cvtime()'
print #99, '$ say "-i-note: if you do not see an exit message,"'
print #99, '$ say " then the spawned process was killed before exit"'
print #99, '$ wait 0:0:04'
print #99, '$ say "-i-exiting at: ", f$cvtime()'
close #99
use
print #99, "-i-error:";err;"during script creation"
end when
else !
print "-i-did not create a DCL test script" !
end if !
print k_lines !
!
! demo-01
!
print "-i-will arm the timer for 6-seconds" !
delay$ = "0 00:00:06.0" ! ddd hh:mm:ss.t
test = 1 !
gosub demo !
print k_lines !
!
! demo-02
!
print "-i-will arm the timer for 2-seconds"
delay$ = "0 00:00:02.0" ! ddd hh:mm:ss.t
test = test + 1 !
gosub demo !
print k_lines !
!
! demo-03
!
print "-i-will arm the timer for 2-seconds"
delay$ = "0 00:00:02.0" ! ddd hh:mm:ss.t
test = test + 1 !
gosub demo !
print k_lines !
!
goto fini !
!=======================================================================
! attempt to place a time limit on spawn
! entry: delay$ = string holding desired delta time
!=======================================================================
3000 demo:
time0 = TIME(0) !
!
! we need two event flags for this demo
!
rc1 = lib$get_EF(ef_timer by ref) !
if rc1 <> ss$_normal then !
print "-e-oops, couldn't allocate a TIMER event flag" !
goto fini !
end if !
!
rc1 = lib$get_EF(ef_spawn by ref) !
if rc1 <> ss$_normal then !
print "-e-oops, couldn't allocate a SPAWN event flag" !
goto fini !
end if !
!
! this mask is used by sys$wflor (wait for logical or)
!
mask = 0 ! init
mask = get_timer_bit_vector(ef_spawn) ! insert vector 1 into mask
mask = mask or get_timer_bit_vector(ef_timer) ! insert vector 2 into mask
!
print "-i-ef_timer :";ef_timer ! debug for hack
print "-i-ef_spawn :";ef_spawn !
print "-i-mask bits:";mask !
!
! set a time delay
!
rc1 = sys$bintim(delay$, DeltaQuad1 ) ! convert delay to 64-bit binary
print "-e-sys$bintim rc1: "+ str$(rc1) if ((rc1 and 1%) <> 1%) !
!
! set a timer
!
rc1 = sys$setimr(ef_timer,DeltaQuad1 by ref,,,) ! now use it to schedule a wake up
print "-e-sys$setimr rc1: "+ str$(rc1) if ((rc1 and 1%) <> 1%) !
!
! LIB$SPAWN [command-string] [,input-file] [,output-file] [,flags]
! [,process-name] [,process-id] [,completion-status-address]
! [,byte-integer-event-flag-num] [,AST-address] [,varying-AST-argument]
! [,prompt-string] [,cli] [,table]
!
%let %hack=0 !
%if %hack=1 %then !
rc1 = sys$clref(ef_spawn) ! incase we want to preclear the EF
print "-e-sys$clref-rc1: "+ str$(rc1) if ((rc1 and 1%) <> 1%) !
%end %if !
!
ef_byte = ef_spawn ! convert long to byte for lib$spawn
rc2 = 0 ! init secondary rc
spawn_flags = cli$m_nowait ! init to an asychronous spawn
select test !
case 1 to 2 !
cmd$ = "@"+ k_script !
case else
cmd$ = "@"+ k_script +"-this-will-fail"
end select
print "-i-cmd: ";cmd$
print "-i-calling spawn (running a job containing a 4-second delay)" !
!
! note: parameter 7 (address by value) looks strange but agrees with documentation
! parameter 8 (byte by ref) looks strange but agrees with documentation
!
rc1 = lib$spawn(cmd$ by desc , ! [command-string] &
, ! [,input-file] &
k_output by desc , ! [,output-file] &
spawn_flags by ref , ! [,flags] &
, ! [,process-name] &
pid by ref , ! [,process-id] &
loc(rc2) by value, ! [,completion-status-address] &
ef_byte by ref, ! [,byte-integer-event-flag-num] &
,,,,,) ! remainder
print "-e-lib$spawn rc1: "+ str$(rc1) if ((rc1 and 1%) <> 1%) !
!
if ((spawn_flags and cli$m_nowait) = cli$m_nowait) then ! if asychronous
print "-i-pid:";pid ! then let's see the pid
else ! else synchronous
print "-i-rc1:";rc1 ! then let's see the final rc
!~~~ print "-i-rc2:";rc2 x not valid
end if !
!
! wait for one of the event flags to change state
! note: 'mask' is what we are testing but 'ef_timer' is used to indicate the correct cluster
!
rc1 = sys$wflor(ef_timer, mask ) ! wait for a response from either flag in mask
print "-e-sys$waitfr rc1: "+ str$(rc1) if ((rc1 and 1%) <> 1%) !
time1 = TIME(0) !
print "-i-measured sleep:";time1-time0;"seconds" !
!
! which event flag got us here? TIMER or SPAWN?
! note: 'ef_timer' is what we are testing (value goes into rc);
! 'junk' will receive all 32-bits of the associated ef cluster
!
rc1 = sys$readef(ef_timer, junk) ! test ef_timer
print "-i-view all flags:";junk ! see 32-bits just for this demo
select rc1 !
case SS$_WASCLR !
ef_timer_state = 0 !
case SS$_WASSET !
ef_timer_state = 1 ! timer expired (oops)
case else !
print "-e-sys$readef-rc:";rc1 !
ef_timer_state = 0 !
end select !
!
rc1 = sys$readef(ef_spawn, junk) ! test ef_spawn
select rc1 !
case SS$_WASCLR !
ef_spawn_state = 0 !
case SS$_WASSET !
ef_spawn_state = 1 ! spawn finished (good)
case else !
print "-e-sys$readef-rc:";rc1 !
ef_spawn_state = 0 !
end select !
print "-i-ef-states: ef_spawn_state=";str$(ef_spawn_state);" ef_timer_state=";str$(ef_timer_state)
!
if ef_spawn_state = 0 then ! spawn didn't finish so...
print "-i-stopping hung process:";pid !
junk = sys$delprc(pid,) !
print "-e-sys$cantim-rc: "+ str$(junk) if ((junk and 1%) <> 1%) !
else !
if ((spawn_flags and cli$m_nowait) = cli$m_nowait) then ! if asychronous
print "-i-rc2:";rc2 ! then let's see the rc
end if !
end if
!
! cancel timer requests (if any)
!
junk = sys$cantim(,) ! cancel all timer requests (probably not req'd)
print "-e-sys$cantim-rc: "+ str$(junk) if ((junk and 1%) <> 1%) !
!
! cleanup (optional)
!
junk = lib$free_EF(ef_timer by ref) !
print "-e-lib$free(1)-rc: "+ str$(junk) if ((junk and 1%) <> 1%) !
junk = lib$free_EF(ef_spawn by ref) !
print "-e-lib$free(2)-rc: "+ str$(junk) if ((junk and 1%) <> 1%) !
!
return
!=======================================================================
! that's all folks
!=======================================================================
32000 fini: !
print "-i-program exit"
end !
!$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
! external functions and subs
!$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
!=======================================================================
! get timer bit vector
! (see OpenVMS system services documentation for "sys$wflor")
!
! notes: cluster event flags notes
! 0 00- 31 local flags
! 1 32- 63
! 2 64- 95 common flags
! 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 !
!
! remember that BASIC has no unsigned integers
!
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 ! 0->30
get_timer_bit_vector = (2% ^ temp) ! else return this
end select !
!
end function ! get_timer_bit_vector
Back to
Home
Neil Rieck
Waterloo, Ontario, Canada.