Here's some code to signal a long-running application whether it should continue or quit. It uses a value in a file which can be set externally. As Oleg comments at the bottom of this page, there may be a simpler way to do this.

Example Use

An example of using this code can be found here.

Complete Script

The lines in "setFlagFileName" are more complicated than they need to be as they are customized to work differently on different machines that I use. This code can be simplified even to the point of hard-coding it to return a specific filename.

NB.* semaphore.ijs: simple binary signalling via a flag file.

NB.* initFlagFile: initialize flag file for semaphoring.
NB.* setFlagFileName: Set name of file to use for semaphore flag.
NB.* lookAtLight: show value of light: Y=green.
NB.* initLight: initialize flag file.
NB.* switchLight: toggle light between Y (go) and N (stop).
NB.* setLight: set "light" (value in flag file) to specific value: Y or N.
NB.* greenLight: check semaphore file to see if we have green-light to proceed.
NB.* checkLight: check if light setting has been acknowledged; wait until
NB.* ackLight: acknowledge that we've seen the light

load 'filefns'
require 'logger'                         NB. Message display and logging
NB. FLAGFL=: 'C:\Temp\greenLight.flag'  NB. Default flag file

egUse=: 0 : 0
   setFlagFileName 'c:\amisc\txt\quote.flag'
   'Y' initFlagFile FLAGFL
   while. greenLight '' do. something end.
)

coclass 'semaphore'
coinsert 'base'

initFlagFile=: 3 : 0
NB.* initFlagFile: initialize flag file for semaphoring.
   '' initFlagFile y
:
   if. 0=#y do.
       if. 0>4!:0 <'FLAGFL' do.
           FLAGFL=: setFlagFileName ''
       end.
   else.   FLAGFL=: setFlagFileName y
   end.
   if. 0~:#x. do. x. setLight '' end.
NB.EG FLAGFL=: 'Y' initFlagFl '\Temp\loadsome.flag'   NB. Global set by caller.
)

setFlagFileName=: 3 : 0                 NB. Set name of file to use for
NB.* setFlagFileName: Set name of file to use for semaphore flag.
   fff=. '\FlagFl.NOW',~getTempDir ''   NB. Temp file to keep name in.
   if. 0=#y do.                         NB. Name is either non-empty argument
       if. 0>4!:0 <'FLAGFL' do.         NB.  or existing global var
           if. fexist fff do.           NB.  or in temp file
               FLAGFL=: 1!:1 <fff
           else.                        NB.  or default value(s).
               domain=. 0{whoami ''
               sharedisk=. 'F:'
               locals=. '[desktop name]';<'[laptop name]' NB. SET THESE: 2 machine names.
               if. domain e. locals do. NB. Local domain so no F:
                   sharedisk=. 'C:' end.
               FLAGFL=: <sharedisk,'\amisc\loadsome.flag'
           end.
       end.
   else. FLAGFL=: y                     NB. Argument is ultimate override.
   end.
   (openbox FLAGFL) 1!:2 boxopen fff    NB. Use temp file to pass arg.
   boxopen FLAGFL
NB.EG FLAGFL=: setFlagFileName '\Temp\loadsome.flag'   NB. Persistent global
)

lookAtLight=: 3 : 0
NB.* lookAtLight: show value of light: Y=green.
   initFlagFile ''
   if. 0=#y do. y=. FLAGFL end.
   1!:1 boxopen y
)

initLight=: 3 : 0
NB.* initLight: initialize flag file.
   '' initLight y
:
   initFlagFile ''
   signal=. <y
   if. 0=#y do.
       signal=. boxopen FLAGFL
   end.
   x. 1!:2 signal
NB.EG initLight '\somedir\somefile.flag'
)

switchLight=: 3 : 0
NB.* switchLight: toggle light between Y (go) and N (stop).
   '' switchLight y
:
   initFlagFile ''
   maxtry=. 99
   signal=. <y
   if. 0=#y do.
       signal=. FLAGFL
   end.
   signal=. boxopen signal
   val=. toupper {.x.
   if. 0=#x. do.
       sigctr=. 0                       NB. Check up to "maxtry" times in case
       while. 3>sigctr do.              NB.  of network or other flakiness.
           try. flag=. {. 1!:1 signal   NB. Will sometimes fail because of
                sigctr=. maxtry         NB.  file across network?
           catch. wait >:?5 [ sigctr=. >:sigctr
           end.
       end.
       if. sigctr<maxtry do.
           logMsg_logger_ 'Unable to switch light at ',showdate ''
           logMsg_logger_ 'for signal file ',(>signal),'.'
           flushLog_logger_ '' [ val=. 'X'   NB. Set arbitrary error value.
       else.
           if. 'Y'={.toupper flag do. val=. 'N'
           else. val=. 'Y' end.
       end.
   end.
   val 1!:2 signal            NB. Will this work if read had trouble?
   val                        NB. Return current value.
NB.EG switchLight ''          NB. Flip flag to opposite for default file.
NB.EG 'Y' switchLight 'C:\Temp\foo.flag'  NB. Set flag to 'Y' for foo file.
NB.EG 'N' switchLight ''      NB. Set flag to 'N' (halt) for default file.
)

setLight=: 3 : 0
NB.* setLight: set "light" (value in flag file) to specific value: Y or N.
   'Y' setLight y            NB. Set to go by default
:
   initFlagFile ''
   signalFl=. y
   if. 0=#y do.
       signalFl=. FLAGFL
   end.
   signalFl=. boxopen signalFl
   val=. toupper 1{.x.
   val 1!:2 signalFl
   val
NB.EG '' setLight ''          NB. Flip flag to go ('Y') for default file.
NB.EG 'Y' setLight 'foo.flag' NB. Set flag to 'Y' for foo file.
NB.EG 'N' setLight ''         NB. Set flag to 'N' (halt) for default file.
)

greenLight=: 3 : 0
NB.* greenLight: check semaphore file to see if we have green-light to proceed.
   signal=. y
   initFlagFile ''
   if. 0=#y do.
       signal=. FLAGFL
   end.
   signal=. boxopen signal
   flag=. 1{. 1!:1 signal
   if. 'Y'=toupper flag do.
       1
   else.
       0
   end.
NB.EG if. greenLight '' do. NB. proceed else. NB. halt end.
)

checkLight=: 3 : 0
NB.* checkLight: check if light setting has been acknowledged; wait until
NB. it has been or we lose patience (time out).
   (5,_) checkLight y    NB. Default to 5 seconds between checks, infinite patience.
:
   initFlagFile ''
   elapsed=. qai ''      NB. Track elapsed time.
   'waittm patience'=. x NB. Seconds to wait between checks, max seconds.
   signal=. <y
   if. 0=#y do.
       signal=. FLAGFL
   end.

   flag=. 3{. 1!:1 signal
   exit=. 0
   ack=. 0               NB. No acknowledgement to start.
NB. Loop until we get acknowledgement or we're out of patience.
   while. ('OK'-: _2{. flag) *. -.exit do.
       wait (waittm)     NB. Wait a little bit before checking again.
       if. patience<(qai '')-elapsed do.
           exit=. 1      NB. Drop out of loop if we're out of patience.
       end.
       flag=. 3{. 1!:1 signal
   end.
   ack=. 'OK'-: _2{. flag
)

ackLight=: 3 : 0
NB.* ackLight: acknowledge that we've seen the light
   signal=. <y
   initFlagFile ''
   if. 0=#y do.
       signal=. FLAGFL
   end.
   signal=. boxopen signal
   flag=. {. 1!:1 signal
   val=. (toupper flag),'OK'
   val 1!:2 signal
   val
NB.EG ackLight ''    NB. Acknowledge reading flag from default file.
NB.EG ackLight 'C:\temp\gnarly.flag'  NB. Acknowledge reading gnarly file flag.
)

coclass 'base'
coinsert 'semaphore'

Included Code

The requirement of "filefns" is probably only for the "fexist" function which is defined thusly:

NB.* fexist: 1 if named file exists, 0 otherwise.
fexist=: (1:@(1!:4) ::0:@((([:< 8 u:>) ::])&>)@(<^:(L.=0:)))

Also, "whoami" - used to distinguish between machines - is defined (for Windows):

whoami=: 3 : 0
   sess=. spawn 'net config workstation'
   sess=. dsp&.><;._1 LF,sess-.CR                 NB. Vec of lines
   sess=. ><;._1&.> ' ',&.>,sess                  NB. Mat of words
   strs=. 'COMPUTER';<'USER'                      NB. Strings to key on
   wh=. (<toupper&.>0{"1 sess)e.&.> <&.>strs      NB. Which lines start with strings
   wh=. ;b2i&.>(<(toupper&.>1{"1 sess)e. <'NAME')*.&.>wh
   wh{2{"1 sess
NB.EG 'machine userid'=. whoami ''
)

The code for "logger" is here.

Comment

DevonMcCormick/semaphore (last edited 2010-01-08 15:27:30 by DevonMcCormick)