REXX Data Queues

From EDM2
Jump to: navigation, search

By Richard K. Goran

REXX has an external data queue facility which can serve as an inter-process communication facility (a fancy term which means that two or more programs may communicate with each other). For simplicity, I'll refer to a REXX data queue simply as the queue. Conceptually, you can think of the queue as a virtual file. There is no restriction imposed upon the contents of the queue other than the fact that items are removed from the queue a line at a time.

There are two types of queues. One queue, available only within an OS/2 session, is named SESSION and is allocated whenever a REXX program is started within the session. This local queue is created the first time that data is placed on the queue. The second type of queue, known as a private or named queue (although all queues are named), may be referenced by any REXX program running in any session. This type of queue is private in that any program referencing the queue must know its name. The name given to a queue is subject to the same rules as the formation of a variable name in REXX and the actual name of the queue is the uppercase value of the chosen name. Only one queue, regardless of type, may be considered to be the active queue for a given session at any point in time.

The SESSION queue may be used without any special preparation on the part of the programmer. Data may be placed on the queue in multiple ways. The PUSH and QUEUE keyword instructions cause data to be placed on the active queue at the front (FIFO) or the rear (LIFO) of the queue, respectively. Also, output may be piped to the queue using the RXQUEUE subcommand shown in Definition 1. The RXQUEUE() built-in function, shown in Definition 2, is separate and distinct from the RXQUEUE subcommand.

RXQUEUE /FIFO  [queue_name]
RXQUEUE /LIFO  [queue_name]
RXQUEUE /CLEAR [queue_name]

Causes the data which follows it on STDIN (normally the keyboard, but may be the redirected output of a command) to be placed in the default REXX queue, SESSION, or queue_name.

If the source of the data is the keyboard,the data must be terminated with an end of file character, <Ctrl-Z> ('1A'x).

The default queue name may be altered by use of the OS/2 environment variable RXQUEUE.

The combined STDOUT and STDERR output may be redirected or "piped" to the REXX data queue.

Example:

DIR *.XYZ 2>&1 | RXQUEUE

Definition 1 - RxQueue subcommand

9501fg02.jpg

Data may be removed from the queue, a line at a time, with the LINEIN() built-in function using the reserved name of 'QUEUE:' or via the PULL keyword instruction. This is the same PULL instruction which may be used to read input from the standard input stream (stdin) which defaults to the keyboard. In actuality, PULL always reads from the active queue and, if there is no data present on the queue, it reverts to stdin.

[#Listing Listing 1] contains a short program which lists all of the environment variables in the current session like you would see if you issued SET from the command line. The difference is that each line is preceded by a two digit sequence line. My apologies to those of you with more than ninety-nine environment variables.

Listing 1 - Display all environment variables

   /* 9501LS01.CMD - RxQueue used as a filter */                     /* 0001 */
   'set | RxQueue /FIFO'               /* issue SET command */       /* 0002 */
                                                                     /* 0003 */
   number = 0                          /* use as line counter */     /* 0004 */
   do while QUEUED() > 0               /* test for lines in queue */ /* 0005 */
      parse pull set_data_line         /* get line from queue */     /* 0006 */
      number = number + 1              /* increment line number */   /* 0007 */
      say RIGHT( number, 2, '0' ) || ' ' ||,                         /* 0008 */
          set_data_line                /* echo line number & data */ /* 0009 */
   end                                                               /* 0010 */
                                                                     /* 0011 */
   say '0D0A'x || number || ' lines from SET command'                /* 0012 */
   exit                                                              /* 0013 */

The SET command is passed to CMD.EXE since it is an external command. The output from the SET command is sent to the session's currently active REXX data queue. (0002)

The QUEUED() function is used to limit the DO loop so that it will continue the iteration only while lines remain in the queue. (0005)

The PARSE instruction with the PULL option, or the shorthand PULL instruction is used to extract data from the queue. The difference between PARSE PULL and PULL is that the latter also translates the queue data to upper case. (0006)

Inter-process Communication

The REXX data queue provides a convenient mechanism to allow synchronization of multiple programs. For instance, if a tree list of all of the disk drives and directories of a system is needed within a REXX program, the main program could start a detached dir command for each directory after creating a unique queue for the specified drive letter and placing some data in the queue. The detached dir command could be followed by the RXQUEUE subcommand to clear the queue.

The main program would test for the absence of data in each queue to determine if each dir command had completed. If the queue used to synchronize the two sessions (as opposed to containing the output of the dir command) was named QUEUE_C for drive C:, etc; the program shown in Listing 2 could be used to start a detached task to write the contents of the directory command to a file and then clear the queue for each drive letter.

The program shown in Listing 2 can be [9501ls02.cmd downloaded].

Listing 2 - Using a detached task

    /*------------------------------------------------------------------------*\
    |                                                                          |
    |               9501LS02.CMD - Synchronize detached DIR command            |
    |                                                                          |
    \*------------------------------------------------------------------------*/
    if RxFuncQuery( 'SysLoadFuncs' ) then                             /* 0006 */
       do                                                             /* 0007 */
          call RxFuncAdd 'SysLoadFuncs', 'REXXUTIL', 'SysLoadFuncs'   /* 0008 */
          call SysLoadFuncs                                           /* 0009 */
       end                                                            /* 0010 */
                                                                      /* 0011 */
    /*-----------------------------------*\                           /* 0012 */
    |  Build list of drives (with colon)  |                           /* 0013 */
    \*-----------------------------------*/                           /* 0014 */
    drive_list = SysDriveMap()                                        /* 0015 */
                                                                      /* 0016 */
    /*-------------------------------------------*\                   /* 0017 */
    |  Start detached DIR command for each drive  |                   /* 0018 */
    \*-------------------------------------------*/                   /* 0019 */
    do drive_number = 1 to WORDS( drive_list )                        /* 0020 */
       drive_letter = LEFT( WORD( drive_list, drive_number ), 1 )     /* 0021 */
                                                                      /* 0022 */
       /* create output file & queue names */                         /* 0023 */
       output_file = 'DIR_'   || drive_letter || '.LST'               /* 0024 */
       queue_name  = 'QUEUE_' || drive_letter                         /* 0025 */
                                                                      /* 0026 */
       /* create & load REXX data queue */                            /* 0027 */
       call RXQUEUE 'DELETE', queue_name /* be sure queue didn't exist */
       call RXQUEUE 'CREATE', queue_name /* create queue */           /* 0029 */
       call RXQUEUE 'SET',    queue_name /* set named queue active */ /* 0030 */
       push DATE() TIME()               /* put something in queue */  /* 0031 */
                                                                      /* 0032 */
       /* detach dir >file & rxqueue name /clear */                   /* 0033 */
       external_command = '@(@DETACH DIR '                      ||,   /* 0034 */
                          drive_letter || ':\*.* /S 1>'         ||,   /* 0035 */
                          output_file                           ||,   /* 0036 */
                          ' 2>&1 & RXQUEUE /clear '             ||,   /* 0037 */
                          queue_name                            ||,   /* 0038 */
                          ') 1>nul 2>&1'                        ||,   /* 0039 */
                          ''                                          /* 0040 */
       interpret "'" || external_command || "'"                       /* 0041 */
    end                                                               /* 0042 */
                                                                      /* 0043 */
    /*-------------------------------------------------------*\       /* 0044 */
    |  Process the DIR output for each drive as it completes  |       /* 0045 */
    \*-------------------------------------------------------*/       /* 0046 */
    drive_number = 0                    /* used for wrap list */      /* 0047 */
    do while drive_list <> ''                                         /* 0048 */
       drive_number = drive_number + 1                                /* 0049 */
       if drive_number > WORDS( drive_list ) then                     /* 0050 */
          do                                                          /* 0051 */
             drive_number = 1           /* wrap to beginning of list */
          end                                                         /* 0053 */
       drive_letter = LEFT( WORD( drive_list, drive_number ), 1 )     /* 0054 */
                                                                      /* 0055 */
       /* see if DIR command complete - queue empty */                /* 0056 */
       queue_name  = 'QUEUE_' || drive_letter                         /* 0057 */
       call RXQUEUE 'SET', queue_name   /* make respective queue active */
       if QUEUED() = 0 then                                           /* 0059 */
          do                                                          /* 0060 */
             output_file = 'DIR_' || drive_letter || '.LST'           /* 0061 */
             /* ... process DIR output in .LST file */                /* 0062 */
             call SysFileDelete output_file                           /* 0063 */
             drive_list_entry = drive_letter || ':' /* append colon */
             parse value drive_list with,                             /* 0065 */
                prefix (drive_list_entry) suffix                      /* 0066 */
             drive_list = STRIP( prefix || ' ' || suffix )            /* 0067 */
          end                                                         /* 0068 */
                                                                      /* 0069 */
       if drive_number = WORDS( drive_list ) then                     /* 0070 */
          do                                                          /* 0071 */
             call SysSleep 2            /* quiesce this session */    /* 0072 */
          end                                                         /* 0073 */
    end                                                               /* 0074 */
    exit                                                              /* 0075 */