Kednos PL/I for OpenVMS Systems
User Manual


Previous Contents Index

11.8.2 Mailbox Services

A mailbox is a virtual I/O device that is used for communication among processes in the system. The routines CREATE_MAILBOX and DELETE_MAILBOX in the following example illustrate the creation and deletion, respectively, of a mailbox.

11.8.2.1 Creating the Mailbox

Example 11-3 illustrates a call to the Create Mailbox and Assign Channel (SYS$CREMBX) system service. This service returns the number of an I/O channel to the calling program. In PL/I, this number is not needed except for the deletion of the mailbox. You must, however, declare a FIXED BINARY(15) variable to receive the number returned by the system service. The following notes are keyed to Example 11-3:

  1. The reference to SYS$CREMBX specifies the permanent flag, output field for the channel number, maximum message size, protection mask, and mailbox logical name. Commas indicate arguments that are not specified; PL/I places zeros in the argument list in these places.
  2. The user privilege to create a permanent mailbox (PRMMBX) is required to call this service with the prmflg argument set to true. A permanent mailbox is not deleted when its creator exits. If this were not a permanent mailbox, the system would automatically delete the mailbox when this procedure is completed.
    SYSNAM privilege is required to place a logical name for a mailbox in the system logical name table.
  3. The MAXMSG argument to SYS$CREMBX specifies the maximum length of any message sent to the mailbox.
  4. The PROMSK argument specifies a 16-bit protection mask for the mailbox. The protection code 'FF00'B4 restricts access to the owner and the system and denies access to the group and the world. Note that the value specified as 'FF00' to PL/I actually sets the low 8 bits of the parameter given the internal representation of bit-strings in PL/I.

Example 11-3 Creating a Mailbox

CREATE_MAILBOX: PROCEDURE OPTIONS(MAIN) RETURNS(FIXED BINARY(31)); 
 
    %INCLUDE SYS$CREMBX; 
    %INCLUDE $STSDEF; 
    %REPLACE MESSAGE_SIZE BY 132; 
    %REPLACE PERMANENT BY '1'B; 
 
    DECLARE 
        CHANNEL FIXED BINARY(15), 
        MAILBOX_NAME CHARACTER(11) 
                    STATIC INITIAL('PLI_MAILBOX'); 
    /* 
     * Call SYS$CREMBX omitting optional arguments. 
     * (Note that trailing optional arguments cannot 
     * be omitted for system services unless specifically 
     * indicated in the service description.) 
     */ 
    STS$VALUE = SYS$CREMBX(                              (1)
                        PERMANENT,                       (2)
                        CHANNEL, 
                        MESSAGE_SIZE,                    (3)
                        'FF00'B4,,                       (4)
                        MAILBOX_NAME); 
    /* 
     * Return to command level with status. If SYS$CREMBX 
     * completed with an error, the appropriate message is 
     * displayed at the command level. 
     */ 
    RETURN(STS$VALUE); 
    END CREATE_MAILBOX; 

11.8.2.2 Deleting the Mailbox

Example 11-4 illustrates a call to the Delete Mailbox (SYS$DELMBX) system service. The procedure DELETE_MAILBOX deletes the mailbox PLI_MAILBOX. A mailbox is deleted when a channel number is specified; this program assigns a channel to the mailbox to obtain a number to be specified in deleting the mailbox. The following notes are keyed to Example 11-4:

  1. The procedure declares the system services Assign I/O Channel (SYS$ASSIGN) and Delete Mailbox (SYS$DELMBX). The channel number output by SYS$ASSIGN is used as input to SYS$DELMBX.
  2. The call to SYS$ASSIGN specifies the logical name of the mailbox and the variable CHANNEL to allow the system service to return a channel number. The optional arguments that are not specified in this call are represented by the required commas at the end of the argument list.
  3. The channel number output by SYS$ASSIGN is passed to SYS$DELMBX by value. The mailbox PLI_MAILBOX is deleted when all programs that have opened the mailbox have closed it.

Example 11-4 Deleting a Mailbox

DELETE_MAILBOX: PROCEDURE OPTIONS(MAIN) RETURNS(FIXED BINARY(31)); 
 
    %INCLUDE SYS$ASSIGN;                               (1)
    %INCLUDE SYS$DELMBX; 
    %INCLUDE $STSDEF; 
 
    DECLARE 
        MAILBOX_NAME CHARACTER(11) 
                STATIC READONLY INITIAL('PLI_MAILBOX') 
        CHANNEL FIXED BINARY(15); 
    /* 
     * Call SYS$ASSIGN and check return; if not successful exit 
     */ 
    STS$VALUE = SYS$ASSIGN(MAILBOX_NAME,CHANNEL,,,);   (2)
    IF ^STS$SUCCESS 
    THEN 
       RETURN(STS$VALUE); 
    /* 
     * Call SYS$DELMBX and check return 
     */ 
    STS$VALUE = SYS$DELMBX(CHANNEL);                   (3)
    RETURN(STS$VALUE); 
 
    END DELETE_MAILBOX; 

11.8.3 Timer and Time Conversion Routines

The system services that depend on time, either an absolute time or a delta time, refer to a time value that is maintained in a 64-bit field. There are system services that convert a character string that specifies a time to its binary equivalent and vice versa.

11.8.3.1 Obtaining a Time Value in System Format

The PL/I procedure GETBINTIM, shown in Example 11-5, accepts a character-string time value as a parameter and returns the binary time value to the point of the procedure's invocation. The following notes are keyed to Example 11-5:

  1. GETBINTIM declares the system service SYS$BINTIM, which converts an ASCII string to a binary time value.
  2. GETBINTIM invokes SYS$BINTIM as a function and tests the return status. An error results if the ASCII time value is not specified correctly. When an error is returned, GETBINTIM returns a zero to the point of the procedure's invocation.

This procedure may be invoked as follows to supply a date and time value for a file in an ENVIRONMENT option:


DECLARE GETBINTIM ENTRY( CHAR(*) ) RETURNS BIT(64) ALIGNED, 
        (CREATED_DATE,EXPIRE_DATE) BIT(64) ALIGNED; 
 
    CREATED_DATE = GETBINTIM('17-JUN-1985 00:00:00.00'); 
    EXPIRE_DATE = GETBINTIM('31-DEC-1991 00:00:00.00'); 
    OPEN FILE(TAPEFILE) ENVIRONMENT( 
 
           CREATION_DATE(CREATED_DATE), 
           EXPIRATION_DATE(EXPIRE_DATE)); 

Example 11-5 Obtaining a System Time Value

/* 
 *  This procedure converts a time given in ASCII format to a 
 *  64-bit time value that is used internally by OpenVMS. 
 *  Input strings must be of the form: 
 * 
 *  dd-mmm-yyyy-hh:mm:ss.cc (for an absolute date or time) 
 *  dddd hh:mm:ss.cc        (for a delta time) 
 */ 
GETBINTIM: PROCEDURE(ASCII_STRING) RETURNS(BIT(64) ALIGNED); 
 
    %INCLUDE SYS$BINTIM;                               (1)
    %INCLUDE $STSDEF; 
 
    DECLARE 
        ASCII_STRING CHARACTER(*), 
        BINARY_TIME BIT(64) ALIGNED; 
    /* 
     * If successful, return binary time to point of 
 
     * invocation. Otherwise, return 0 - this results 
     * in absolute time 17-NOV-1858. 
     */ 
    STS$VALUE = SYS$BINTIM(ASCII_STRING,BINARY_TIME);  (2)
    IF STS$SUCCESS 
    THEN 
        RETURN(BINARY_TIME); 
    ELSE 
        RETURN((64)'0'B); 
    END GETBINTIM; 

11.8.3.2 Setting the Timer

The procedure in Example 11-6 uses the Set Timer (SYS$SETIMR) system service. It issues a time request for some activity to occur in 10 seconds and specifies the number of an event flag to be set when the 10 seconds have elapsed. The following notes are keyed to Example 11-6:

  1. This procedure uses the GETBINTIM function to convert an ASCII time value to the system's 64-bit format.
  2. LIB$GET_EF allocates an event flag from a process-wide pool and returns the event flag number.
  3. The procedure invokes SYS$SETIMR, specifying by its first argument that SYS$SETIMR should set the specified event flag when the time expires. The argument list contains a reference to GETBINTIM, which returns the system time value for 10 seconds.
  4. The procedure uses the Wait for Event Flag (SYS$WAITFR) system service to wait for the event flag specified in the call to SYS$SETIMR. When the flag is set, the procedure displays a message and exits.

Example 11-6 Setting a Timer

SET_TIMER: PROCEDURE OPTIONS(MAIN) RETURNS(FIXED BINARY(31)); 
    %INCLUDE LIB$GET_EF; 
    %INCLUDE LIB$FREE_EF; 
    %INCLUDE SYS$SETIMR; 
    %INCLUDE SYS$WAITFR; 
    %INCLUDE $STSDEF; 
    DECLARE 
        GETBINTIM ENTRY(CHAR(*))  /* character string time */   (1)
            RETURNS(BIT(64) ALIGNED); 
    DECLARE EVENT_FLAG_NUM FIXED BIN(31); 
    /* 
     * Get an event flag to use. 
     */ 
    STS$VALUE = LIB$FREE_EF(EVENT_FLAG_NUM);                     (2)
    IF ^STS$SUCCESS 
    THEN RETURN(STS$VALUE); 
    /* 
     * Set the timer for 10 seconds. 
     */ 
    STS$VALUE = SYS$SETIMR(EVENT_FLAG_NUM, 
                    GETBINTIM('0 00:00:10'),,);                 (3)
    IF ^STS$SUCCESS 
    THEN RETURN(STS$VALUE); 
    /* 
     * Wait for the event flag, and display a 
     * message when the timer completes. 
     */ 
    STS$VALUE = SYS$WAITFR(EVENT_FLAG_NUM);                     (4)
    IF ^STS$SUCCESS 
    THEN RETURN(STS$VALUE); 
    PUT SKIP LIST('Timer up!'); 
    /* 
     * Release the event flag. 
     */ 
    STS$VALUE = LIB$GET_EF(EVENT_FLAG_NUM); 
    RETURN(STS$VALUE); 
    END SET_TIMER; 

11.8.4 A Ctrl/c-Handling Routine

A Ctrl/c routine is a subroutine that is given control when the execution of the program is interrupted externally by the Ctrl/c function. To enable a Ctrl/c routine, you must code a call to the SYS$QIO (Queue I/O Request) system service, which performs I/O. In this call to SYS$QIO, you specify the name of an external procedure that will be executed when the interruption occurs. This type of procedure is called an asynchronous system trap (AST) routine because it may be executed at any time.

The sample programs in this section interact as follows:

11.8.4.1 Establishing a Ctrl/c-Handling Routine

The following notes are keyed to Example 11-7:

  1. SET_CTRLC includes the declarations for the SYS$ASSIGN, SYS$QIO, and SYS$WAITFR system services. The SYS$QIO system service requires as an argument a channel number, that is, an I/O path to a device. The SYS$ASSIGN system service obtains a channel number.
  2. The I/O function codes IO$_SETMODE and IO$M_CTRLCAST are declared in $IODEF, which is obtained from PLI$STARLET.TLB. In a call to SYS$QIO, the specific I/O request is indicated by a symbolic name. These names have the following meanings:
    These function codes and modifiers must be ORed together to obtain the correct result, as shown in the invocation of SYS$QIO (Note 9).
  3. The variable TTCHAN receives the channel number assigned to the current terminal device.
  4. An I/O status block is an 8-byte structure that is filled with status information when an I/O request is completed. The first two bytes always contain the status of the I/O request.
  5. To define a Ctrl/c AST routine, the name of the entry is passed as an argument to SYS$QIO. In PL/I, this must be the name of an external entry, because it must be passed by immediate value.
    When an AST routine is specified in a system service, you have the option of specifying an argument to be passed as a parameter of the AST routine. AST parameters are always passed by value in the argument list. For an AST routine written in PL/I to correctly interpret the parameter, the AST routine must receive the parameter by reference-this means that the AST parameter must be passed by a pointer to its value.
  6. The CNTRL_C_INTER bit is declared and set to zero. This flag is used to call a procedure that signals that an interrupt has occurred.
  7. The variable IO_SUCCESS is used to ensure that the I/O completed successfully.
  8. The procedure calls SYS$ASSIGN to assign a channel to the current terminal. The simplest way to do this when the terminal may not always be the same physical device is to specify the logical name TT. When the service completes successfully, the variable TTCHAN contains the terminal channel number.
  9. The call to SYS$QIO specifies an event flag, the channel number, the I/O function to be performed for the device, the address of the I/O status block, and the name of the AST entry.
    SYS$QIO does not actually perform an I/O operation, but merely queues it, as its name suggests. A successful return from SYS$QIO indicates that the request is queued. Proper programming practice requires that the caller of SYS$QIO either wait until the I/O completes, or request notification of completion by the execution of an AST routine. In this example, the procedure waits for the event flag specified in the call to SYS$QIO. When the event flag is set, the I/O is completed. Note that it is generally advisable to use the RTL routines LIB$GET_EF and LIB$FREE_EF to avoid the overlap of event flags. Several other examples in this section show the use of these routines.
  10. Following the call to SYS$QIO, the procedure waits until the request is actually performed and then tests the status value in the I/O status block.

Example 11-7 Establishing a Ctrl/c Routine

SET_CTRLC: PROCEDURE RETURNS(FIXED BINARY(31)); 
    %INCLUDE SYS$ASSIGN;                                       (1)
    %INCLUDE SYS$QIO; 
    %INCLUDE SYS$WAITFR; 
    %INCLUDE $IODEF;                                           (2)
    %INCLUDE $STSDEF; 
 
    DECLARE TTCHAN FIXED BINARY(15);                           (3)
 
    DECLARE 
        1 IOSB,                                                (4)
          2 VALUE FIXED (15),         /* Return status */ 
          2 NOT_USED(3) FIXED (15), 
        C_AST ENTRY(POINTER);    /* CTRL/C AST routine */      (5)
 
    DECLARE 
        CNTRL_C_INTER STATIC BIT(1) ALIGNED GLOBALDEF,         (6)
        IO_SUCCESS BIT(1) ALIGNED BASED(ADDR(IOSB.VALUE));     (7)
 
    DECLARE VALUE BUILTIN; 
 
    /* 
     * Call Assign I/O channel to get a terminal channel and then 
     * call Queue I/O Request to enable the terminal for CTRL/C. 
     */ 
    STS$VALUE = SYS$ASSIGN ('TT',TTCHAN,,);                     (8)
    IF ^STS$SUCCESS 
    THEN RETURN(STS$VALUE); 
 
    STS$VALUE = SYS$QIO (1,TTCHAN, 
                IO$_SETMODE|IO$M_CTRLCAST, /* function */  (9)
                IOSB,                   /* I/O status block */ 
                ,,                 /* omit QIO AST argument */ 
                VALUE(C_AST),  /* AST routine for IO$_CTRLCAST */ 
                ,,,,);           /* unspecified p2 through p6 */ 
    IF ^STS$SUCCESS 
    THEN RETURN(STS$VALUE); 
 
    STS$VALUE = SYS$WAITFR(1); 
    IF ^IO_SUCCESS 
    THEN RETURN(IOSB.VALUE);                                     (10)
 
    CNTRL_C_INTER = '0'B; 
    RETURN(1); 
    END SET_CTRLC; 

11.8.4.2 Ctrl/c Routine

The following description refers to Example 11-8.

Once a Ctrl/c handler has executed, it cannot be executed again unless the I/O request that establishes a handler is reexecuted. To keep a Ctrl/c handler active, it is common practice to reenable the Ctrl/c routine within the AST routine itself. The C_AST interrupt routine sets the CNTRL_C_INTER bit. When control is returned to the main routine, a Ctrl/c interrupt message is printed out. In addition, the Ctrl/c handler is reenabled by calling SET_CTRLC.

Example 11-8 Ctrl/c Handler

C_AST: PROCEDURE; 
    DECLARE CNTRL_C_INTER STATIC BIT(1) ALIGNED GLOBALREF; 
    CNTRL_C_INTER = '1'B; 
    END C_AST; 

11.8.4.3 Testing the Ctrl/c Routine

The procedure TESTC, in Example 11-9, tests the SET_CTRLC and C_AST routines. The techniques used here can be applied to any procedure in which you want to detect and respond to an external interrupt via Ctrl/c.

Example 11-9 Testing the Ctrl/c Routine

TESTC: PROCEDURE OPTIONS(MAIN) RETURNS(FIXED BIN(31)); 
 
    /* 
     * Field declarations for Return Status Values. 
     */ 
    %INCLUDE $STSDEF; 
 
    DECLARE SET_CTRLC ENTRY RETURNS(FIXED BIN(31));           (1)
    DECLARE CNTRL_C_INTER BIT(1) GLOBALREF; 
    %REPLACE TRUE BY '1'B; 
 
    SIGNAL_INTER: PROCEDURE;                                  (2)
        PUT SKIP LIST('Control/C interrupt'); 
        STS$VALUE = SET_CTRLC();   /* reenable CTRL/C handler */ 
        END; 
    STS$VALUE = SET_CTRLC_();                                 (3)
    IF ^STS$SUCCESS 
    THEN 
        RETURN(STS$VALUE); 
    DO WHILE (TRUE);                                          (4)
        IF CNTRL_C_INTER 
        THEN 
            CALL SIGNAL_INTER; 
        END; 
 
    END TESTC; 

The following notes are keyed to Example 11-9:

  1. The procedure declares the external routine SET_CTRLC and the CNTRL_C_INTER variable.
  2. If SIGNAL_INTER is called, a message is printed and SET_CTRLC is called to reenable the Ctrl/c handler.
    A Ctrl/c handler can be much more elaborate: you may want to use it to close files, to advance processing to a labeled statement or block, and so on.
  3. The procedure calls SET_CTRLC to establish the Ctrl/c handler.
  4. The procedure places itself in an infinite loop. Each time Ctrl/c is entered, the procedure displays its message for the Ctrl/c interrupt and continues.

Note that when this program is run, it can be interrupted at the terminal and stopped only by the Ctrl/y function.

11.8.5 Obtaining Job/Process Information

The Get Job/Process Information (SYS$GETJPI) system service returns information about a specific aspect or attribute of a job that is currently being executed. A call to this service requires that you set up two buffers:

The procedure TIME, shown in Example 11-10, uses SYS$GETJPI to acquire performance statistics about the execution of a program. It has two entry points:

The statistics that are displayed represent the differences between the values acquired at the entry TIMRE and those acquired at the entry TIMRB.

The following notes are keyed to Example 11-10 and Example 11-11:

  1. The module $JPIDEF contains the definitions of the constant identifiers whose names and values correspond to the item codes required for SYS$GETJPI.
  2. The structure JPI_LIST contains a minor structure for each item requested. Each minor structure has the same required format; it contains the following information:
    The list is terminated by a longword containing zero.
  3. The item codes for SYS$GETJPI are specified using the constant identifiers in the INCLUDE file for $JPIDEF. Each constant identifier specifies a unique numeric code that SYS$GETJPI uses to determine the information to be returned.
  4. Variables are declared for the return information for TIMRB and TIMRE. The FORTRAN procedure FOR$SECNDS is in the system run-time procedure library. It returns the current system time in seconds.
  5. At TIMRE and TIMRB, the item list for each item is initialized with a pointer to the appropriate return field.
  6. SYS$GETJPI is invoked as a function whose value is compared to the status code SS$_NORMAL. If they do not match, the procedure exits with a message.
  7. When entered at TIMRE, the procedure obtains the current information, calculates the differences in the statistics required by TIMRB and those obtained in the most recent call to SYS$GETJPI, and displays the results on the terminal. Then, it falls through to TIMRB to ensure that the fields are reinitialized.
  8. When entered at TIMRB, the start values for the statistics are initialized with the current values obtained from SYS$GETJPI, and the procedure exits.

Example 11-10 Calling SYS$GETJPI (VAX)

TIME: PROCEDURE; 
 
    %INCLUDE SYS$GETJPI; 
 
    /* 
     * INCLUDE definitions required by SYS$GETJPI 
     */ 
    %INCLUDE $JPIDEF;     /* item codes */                    (1)
    %INCLUDE $SSDEF;   /* System (SS$_*) status values */ 
    %INCLUDE $STSDEF;   /* status value variable */ 
 
    DECLARE 
        1 JPI_LIST STATIC EXTERNAL,                           (1)
          2 JPI_BUFIO,        /* Buffered I/O count */ 
            3 LENGTH FIXED BIN(15) INITIAL(4), 
            3 ITMCOD FIXED BIN(15) INITIAL(JPI$_BUFIO),       (2)
            3 BUFADR POINTER INITIAL(NULL()), 
            3 RETLEN POINTER INITIAL(NULL()), 
          2 JPI_CPUTIM,          /* CPU time */ 
            3 LENGTH FIXED BIN(15) INITIAL(4), 
            3 ITMCOD FIXED BIN(15) INITIAL(JPI$_CPUTIM),      (3)
            3 BUFADR POINTER INITIAL(NULL()), 
            3 RETLEN POINTER INITIAL(NULL()), 
          2 JPI_DIRIO          /* Direct I/O count */ 
            3 LENGTH FIXED BIN(15) INITIAL(4), 
            3 ITMCOD FIXED BIN(15) INITIAL(JPI$_DIRIO),       (3)
            3 BUFADR POINTER INITIAL(NULL()), 
            3 RETLEN POINTER INITIAL(NULL()), 
          2 JPI_PAGEFLTS          /* Page faults */ 
            3 LENGTH FIXED BIN(15) INITIAL(4), 
            3 ITMCOD FIXED BIN(15) INITIAL(JPI$_PAGEFLTS),    (3)
            3 BUFADR POINTER INITIAL(NULL()), 
            3 RETLEN POINTER INITIAL(NULL()), 
          2 ENDLIST FIXED BIN(31) INITIAL(0); 
 
    DECLARE 
        (TO,CLOCK_TIME) FLOAT BIN(24) STATIC EXTERNAL,        (4)
        (BUFIO,END_BUFIO,CPUTIM,END_CPUTIM,DIRIO, 
            END_DIRIO,PAGEFLTS,END_PAGEFLTS) 
                    FIXED BIN(31) STATIC EXTERNAL, 
        CPUSECONDS FLOAT BIN(24); 
 
DECLARE FOR$SECNDS ENTRY (FLOAT BIN(24)) RETURNS(FLOAT BIN(24)); 
 
TIMRE:        ENTRY; 
    JPI_BUFIO.BUFADR = ADDR(END_BUFIO);                       (5)
    JPI_CPUTIM.BUFADR = ADDR(END_CPUTIM); 
    JPI_DIRIO.BUFADR = ADDR(END_DIRIO); 
    JPI_PAGEFLTS.BUFADR = ADDR(END_PAGEFLTS); 
 
    IF SYS$GETJPIW(,,,JPI_LIST,,,)^=SS$_NORMAL                (6)
    THEN 
        PUT SKIP LIST ('Error from SYS$GETJPI'); 
 
    CLOCK_TIME = FOR$SECNDS(TO); 
    CPUSECONDS = (END_CPUTIM-CPUTIM)/100E0; 
    BUFIO = END_BUFIO-BUFIO;                                  (7)
    DIRIO = END_DIRIO-DIRIO; 
    PAGEFLTS = END_PAGEFLTS-PAGEFLTS; 
    PUT SKIP EDIT ('Times in seconds','Page','Direct','Buffered') 
                (A(20),A(10),A(10),A(10)); 
    PUT SKIP EDIT ('CPU','Elapsed','Faults','I/O','I/O') 
                (A(10),A(10),A(10),A(10),A(10)); 
    PUT SKIP EDIT (CPUSECONDS,CLOCK_TIME,PAGEFLTS,DIRIO,BUFIO) 
                (F(7,1),COLUMN(11),F(9,1),COLUMN(21),F(7,0),COLUMN(31), 
                F(7,0),COLUMN(41),F(7,0)); 
 
    /* 
     * After calling TIMRE, fall through here to reinitialize. 
     */ 
TIMRB:        ENTRY;                                          (8)
    TO = FOR$SECNDS(0E0); 
    JPI_BUFIO.BUFADR = ADDR(BUFIO);                           (5)
    JPI_CPUTIM.BUFADR = ADDR(CPUTIM); 
    JPI_DIRIO.BUFADR = ADDR(DIRIO); 
    JPI_PAGEFLTS.BUFADR = ADDR(PAGEFLTS); 
 
    IF SYS$GETJPIW(,,,JPI_LIST,,,)^=SS$_NORMAL 
    THEN 
        PUT SKIP LIST ('Error from SYS$GETJPI'); 
 
    RETURN; 
 
    END TIME; 


Previous Next Contents Index