Operating System - OpenVMS
1752758 Members
5314 Online
108789 Solutions
New Discussion

Re: READ/TIME_OUT in PIPE Stream

 
Jack Trachtman
Super Advisor

READ/TIME_OUT in PIPE Stream

I'm trying to create a simple process to monitor records being added to a file.   I've simplified the code to show the problem.

 

$ PIPE TYPE/CONTINUOUS/INT=5 filename | @TEST

 

where TEST.COM is

 

$ LOOP:
$       READ/TIME_OUT=3/ERROR=ERR SYS$PIPE X
$       WRITE   SYS$OUTPUT      X
$       GOTO    LOOP
$ ERR:
$       WRITE   SYS$OUTPUT      "No input"
$       GOTO    LOOP

 

When I issue the PIPE cmd, I see displayed the present contents of "filename", but when the end of the file is reached, the cmd hangs, as if the READ/TIME_OUT is not firing.  I'm expecting to see the line "No input" appear every 3 seconds.

 

I've spent a lot of time on various versions of the code and did a GOOGLE search, but don't see why this won't work.

 

Suggestions?

 

TIA

6 REPLIES 6
Jur van der Burg
Respected Contributor

Re: READ/TIME_OUT in PIPE Stream

from memory, read/time_out will only work in a couple of cases:

 

1. reading from a terminal device

2. timeout for reading with a locked record

3. reading from a mailbox with timeout=0 to return immediate even if there's no data.

 

Since you're reading from a mailbox case 3 applies and only a zero timeout has a meaning.

 

So it does not work the way you would think.

 

Jur.

 

Hein van den Heuvel
Honored Contributor

Re: READ/TIME_OUT in PIPE Stream

 

 

Is the file writing process cooperating? Ideally it opened the file with full sharing.

That is open access=write, share=read (or more)

 

In the case you can just write a DCL poll-loop as the EOF will be live in the shared file lock (all transparent)

 

$my_wait:

$ wait 0:0:3

$ loop:

$ read /end=my_wait file record

$ ! process record

$ goto loop

 

For a slightly less cooperative process you may need to open the file over and over to pick up the latest EOF.

DCL does not have tell/seek but several tools do, notably perl

 

So a quick perl program could

- get the last position (if any) from a symbol or logical, or command line argument

- open the file

- seek to last

- read

- perhaps process the data or share out for a next step

- tell the last postion.

- close

 

Good luck!

Hein

 

John Gillings
Honored Contributor

Re: READ/TIME_OUT in PIPE Stream

Jack,

   Put another way... /TIMEOUT just adds modifier SS$_TIMEOUT to the $QIO issed for the read. It will only work if the driver for the target device supports that modifier. The mailbox driver doesn't do timeouts. You can simulate them using "crossed ASTs". That is, you issue a timer AST which will $CANCEL on the input channel, you then issue a READ QIO on that channel, and on completion cancel the timer. Obviously not something you can do from DCL, but maybe with a small program reading the pipe?

 

If today is slow enough I'll see what I can come up with...

A crucible of informative mistakes
John Gillings
Honored Contributor

Re: READ/TIME_OUT in PIPE Stream

Jack,

   I think this does what you want. Running this batch job

 

 mybatch.com
$ SET VERIFY
$ WRITE SYS$OUTPUT "Batch job running"
$ loop:
$   WAIT 00:00:01
$   WRITE SYS$OUTPUT "After 1 sec ",F$TIME()
$   WAIT 00:00:01
$   WRITE SYS$OUTPUT "After 2 sec ",F$TIME()
$   WAIT 00:00:10
$   WRITE SYS$OUTPUT "After 10 sec ",F$TIME()
$ GOTO loop

 

and executing

 

$ pipe type/tail/cont/int=5 MYBATCH.LOG | @mywait

 

output looks like this

 

  INPUT = "$   WAIT 00:00:01"
  INPUT = "$   WRITE SYS$OUTPUT "After 1 sec ",F$TIME()"
  INPUT = "After 1 sec 27-DEC-2013 11:42:10.36"
  INPUT = "$   WAIT 00:00:01"
  INPUT = "$   WRITE SYS$OUTPUT "After 2 sec ",F$TIME()"
  INPUT = "After 2 sec 27-DEC-2013 11:42:11.36"
  INPUT = "$   WAIT 00:00:10"
Input timed out %X1000022C
Input timed out %X1000022C
Input timed out %X1000022C
  INPUT = "$   WRITE SYS$OUTPUT "After 10 sec ",F$TIME()"
  INPUT = "After 10 sec 27-DEC-2013 11:42:21.36"
  INPUT = "$ GOTO loop"
  INPUT = "$ loop:"
  INPUT = "$   WAIT 00:00:01"
Input timed out %X1000022C
  INPUT = "$   WRITE SYS$OUTPUT "After 1 sec ",F$TIME()"
  INPUT = "After 1 sec 27-DEC-2013 11:42:22.36"
  INPUT = "$   WAIT 00:00:01"
  INPUT = "$   WRITE SYS$OUTPUT "After 2 sec ",F$TIME()"
  INPUT = "After 2 sec 27-DEC-2013 11:42:23.37"
  INPUT = "$   WAIT 00:00:10"
Input timed out %X1000022C
Input timed out %X1000022C
Input timed out %X1000022C
  INPUT = "$   WRITE SYS$OUTPUT "After 10 sec ",F$TIME()"
  INPUT = "After 10 sec 27-DEC-2013 11:42:33.37"
  INPUT = "$ GOTO loop"
  INPUT = "$ loop:"
  INPUT = "$   WAIT 00:00:01"
  INPUT = "$   WRITE SYS$OUTPUT "After 1 sec ",F$TIME()"
  INPUT = "After 1 sec 27-DEC-2013 11:42:34.37"
  INPUT = "$   WAIT 00:00:01"

 

MYWAIT.COM looks like

 

  

$ SET NOON
$ waitloop:
$   MCR  DISK_USERS:[GILLINGS_J]TIMEOUT INPUT 3 SYS$PIPE
$   stat=$STATUS
$   IF stat
$   THEN
$     SHOW SYM INPUT
$   ELSE
$     WRITE SYS$OUTPUT "Input timed out ''stat'"
$   ENDIF
$ GOTO waitloop

 

The TIMEOUT program syntax is

 

$ TIMEOUT <symbol> [<timeout in seconds> D=5] [<input device> D=SYS$PIPE]

Reads input from the input device. If no input is seen within the timeout period, status SS$_TIMEOUT is returned (with SS$M_INHIB_MSG set)

Default timeout is 5 seconds. Default input device is SYS$PIPE

 

As usual with this type of program, most of the work is in parsing and checking input parameters.

 

Written in MACRO32 so it compiles and links on all OpenVMS systems, regardless of compilers and licenses (and also because I like MACRO32 ;-) Invoke as a foreign command.

 

TIMEOUT.MAR

        .TITLE TimeoutRead
$EFNDEF
$DSCDEF
$LIBDEF
$SSDEF

        .PSECT data,rd,wrt,noexe,quad
bintim: .QUAD
IOSB:
 iostat: .WORD
 iochars:.WORD
 ioterm: .WORD
 iotrmsz:.WORD
timeout:  .LONG 5 ; seconds
cvtop:    .LONG LIB$K_DELTA_SECONDS
stat:     .LONG 0
Chan:     .WORD

DefTMO: .ASCID  /5/
DefDev: .ASCID  /SYS$PIPE/
Prompt: .ASCID  /Symbol: /
Blank:  .ASCID  //

MaxLine=512
line_buf: .BLKB MaxLine
input_buf:.BLKB MaxLine

        .MACRO Desc nam
                .ALIGN QUAD
                'nam':
                'nam'_len: .WORD 0
                  .BYTE DSC$K_DTYPE_T
                  .BYTE DSC$K_CLASS_S
                'nam'_ptr: .LONG
        .ENDM Desc

.ALIGN QUAD
Desc line
Desc sym
Desc TMO
Desc Dev
Desc input

        .PSECT code,rd,nowrt,exe
        .MACRO ParseToken,tok,def,err,?lab
        ;   parses a space delimited token from the command line,
        ;   or default value if line has been consumed.
        ;   or error status if resulting string is null
          MOVAB def,R4             ; set output string to default value
          MOVW (R4),'tok'_len
          MOVL 4(R4),'tok'_ptr
          CMPL R2,R3               ; any more string to parse?
          BGEQ eos_'lab'
          MOVW #0,'tok'_len        ; yes, start length at 0
          MOVL R2,'tok'_ptr        ; and string pointing to start
next_'lab': CMPB (R2)+,#^A/ /      ; look for a space
          BLEQ eos_'lab'           ; reached end of string
          CMPL R2,R3               ; check for end of string
          BGTR eos_'lab'
          INCL 'tok'_len           ; increment length
          BRB  next_'lab'
eos_'lab': TSTW 'tok'_len          ; check that string is non-null
          BGTR ok_'lab'
          MOVL err,R0
        RET
ok_'lab':
skip_'lab':
          CMPB (R2),#^A/ /         ; step over any blanks
          BGTR eob_'lab'
          CMPL R2,R3              ; don't go past end of string
          BGEQ eob_'lab'
          INCL R2
          BRB skip_'lab'
eob_'lab':
        .ENDM ParseToken


        .PSECT code,rd,nowrt,exe

        .ENTRY canio,^M<> ; AST routine to cancel an I/O on input channel
                $CANCEL_S chan=chan
                MOVL #SS$_TIMEOUT,stat
        RET

        .ENTRY start,^M<R2,R3,R4,R5,R6,R7,R8>

          MOVAB line_buf,line_ptr   ; set up descriptors for command line and input
          MOVW #MaxLine,line_len
          MOVAB input_buf,input_ptr
          MOVW #MaxLine,input_len

          PUSHAW line_len       ; read and trim command line
          PUSHAB Prompt
          PUSHAB line
          CALLS #3,G^LIB$GET_FOREIGN
          PUSHAB line
          PUSHAB line
          CALLS #2,G^STR$TRIM

          MOVAB   line_buf,R2  ; set start and end pointers for parsing string
          MOVZWL  line_len,R3
          ADDL2 R2,R3

          ParseToken sym,Blank,#^X38080 ; get parameters
          ParseToken TMO,DefTMO,#20
          ParseToken Dev,DefDev,#2312

          PUSHAL timeout    ; convert timeout parameter from string to binary seconds
          PUSHAB TMO
          CALLS #2,G^OTS$CVT_TI_L
          BLBC R0,Bad

          PUSHAB bintim    ; convert binary seconds to delta time
          PUSHAB timeout
          PUSHAB cvtop
          CALLS #3,G^LIB$CVT_TO_INTERNAL_TIME
          BLBC R0,Bad

          PUSHAB Blank     ; set symbol to null value in case of errors
          PUSHAB sym
          CALLS #2,G^LIB$SET_SYMBOL

          $ASSIGN_S devnam=dev chan=chan  ; open I/O channel

          $SETIMR_S daytim=bintim astadr=canio reqidt=canio  ; schedule timeout

          $QIOW_S efn=#EFN$C_ENF chan=Chan iosb=IOSB -       ; read the input device
                  func=#IO$_READVBLK p1=input_buf p2=#MaxLine

          BLBC R0,Bad  ; check return and IOSB status values
          MOVZWL iostat,R0
          BLBC R0,Bad

          $CANTIM_S reqidt=canio ; cancel the AST (not really necessary, but let's do the right thing)

          MOVW iochars,input_len ; set length of descriptor for input line

          PUSHAB input  ; set symbol to input value
          PUSHAB sym
          CALLS #2,G^LIB$SET_SYMBOL

Bad:      BLBS R0,Good  ; sort out return status. If success, just return it
          TSTL stat     ; if stat is non-zero, the AST fired
          BEQL keep     ; is zero, keep the existing R0
          MOVL stat,R0  ; overwrite with stat
keep:
          BISL #STS$M_INHIB_MSG,R0 ; set INHIBIT bit so DCL won't write an error message
Good:
        RET
        .END Start

 

$ MACRO TIMEOUT

$ LINK TIMEOUT

$ TIMEOUT="$dev:[dir]TIMEOUT"

 

(remember full filespec required for foreign commands)

 

Test run

 

$
$  ! valid input
$ timeout testsym 5 sys$input
some input
$ show sym $status
  $STATUS == "%X00000001"
$ show sym testsym
  TESTSYM = "some input"
$
$  ! Allow it to timeout
$ timeout testsym 5 sys$input
$ show sym $status
  $STATUS == "%X1000022C"
$ show sym testsym
  TESTSYM = ""

 

 

A crucible of informative mistakes
Phil.Howell
Honored Contributor

Re: READ/TIME_OUT in PIPE Stream

If today is slow enough I'll see what I can come up with...

 

No doubt you were referring to the cricket :)

Jack Trachtman
Super Advisor

Re: READ/TIME_OUT in PIPE Stream

Thanks for the responses!

 

Looks like my original "simple" idea won't work!

 

Hein: I tried the first DCL solution you suggested, but nothing was displayed after new records were appended to the original file.  I guess that whatever mode the file is being opened in, the EOF marker is not getting updated somewhere where the DCL can see it.  (I don't know what parameters the third party product is using to open the file to append records.  I can see that the file doesn't have a channel open so the app must just open it when records need to be added.)

 

John: thanks very much for going out of you way to provide a Macro solution!  Interesting reading for me, since I haven't used Macro in a way long time!  Unfortunately, this solution seems too complex for what I had hoped could be solved with some DCL.