HP OpenVMS I/O User’s Reference Manual: OpenVMS Version 8.4 > Chapter 4 Mailbox Driver

4.5 Mailbox Driver Programming Examples

This section contains the following programming examples:

  • Example 4-1 shows a MACRO32 program that creates a mailbox and puts mail into it.

  • Example 4-2 assigns a read-only channel to the mailbox.

  • Example 4-3 assigns a write-only channel to the mailbox.

Example 4-1 creates a mailbox and puts mail into it; no matching read is pending on the mailbox. First, the program shows that if the function modifier IO$M_NOW is not used when mail is deposited, the write function waits until a read operation is performed. In this case, IO$M_NOW is specified and the program continues after the mail is left in the mailbox.

Next, the mailbox is read. If there is no mail in the mailbox, the program waits because IO$M_NOW is not specified. IO$M_NOW should be specified if there is any doubt about the availability of data in the mailbox, and it is important for the program not to wait.

It is up to the user to coordinate the data that goes into and out of mailboxes. In this example, the process reads its own message. Normally, two mailboxes are used for interprocess communication: one for sending data from process A to process B, and one for sending data from process B to process A. If a program is arranged in this manner, there is no possibility of a process reading its own message.

NOTE: The table for temporary mailbox names can be redefined to be a group table. This allows the processes in other jobs with same group number to use the same logical name to access the mailbox. For example, LNM$TEMPORARY_MAILBOX can be redefined to any shareable table that the process has write access to. In this case, it could be redefined to LNM$GROUP if the process has GRPNAM privlege or if the group table allows the process to write to it. See the description of the $CREMBX service in the System Services Reference Manual for more information.

Example 4-2 (Example 4-2) and Example 4-3 (Example 4-3) work together from two separate processes and show the unidirectional mailbox synchronization features. With the default definition of LNM$TEMPORARY_MAILBOX, the logical name for the mailbox is created in the job logical name table. The processes running both example programs should be in the same job.

Example 4-2 performs the following functions:

  1. Assigns a read-only channel to the mailbox.

  2. Waits for another program to assign a writable channel to the mailbox.

  3. Reads, using the IO$M_WRITERCHECK function modifier, what has been written to the mailbox. Each record is echoed to SYS$OUTPUT.

  4. When SS$_NOWRITER is returned from the read operation, goes back to Step 2 and waits for another writer.

Example 4-3 is a writer to the mailbox. It performs the following functions:

  1. Assigns a write-only channel to the mailbox.

  2. Waits for a reader.

  3. Gathers user input until the user enters Ctrl/Z, then writes that input to the mailbox.

Example 4-1 Mailbox Driver Program Example 1

; *********************************************************************
;

        .TITLE  MAILBOX DRIVER PROGRAM EXAMPLE
        .IDENT  /01/

;
; Define necessary symbols.
;

        $IODEF                          ;Define I/O function codes

;
; Allocate storage for necessary data structures.
;

;
; Allocate output device name string and descriptor.
;

DEVICE_DESCR:                           ;
        .LONG   20$-10$                 ;Length of name string
        .LONG   10$                     ;Address of name string
10$:    .ASCII  /SYS$OUTPUT/            ;Name string of output device
20$:                                    ;Reference label

;
; Allocate space to store assigned channel number.
;

DEVICE_CHANNEL:                         ;
        .BLKW   1                       ;Channel number

;
; Allocate mailbox name string and descriptor.
;

MAILBOX_NAME:                           ;
        .LONG   ENDBOX-NAMEBOX          ;Length of name string
        .LONG   NAMEBOX                 ;Address of name string
NAMEBOX: .ASCII /146_MAIN_ST/           ;Name string
ENDBOX:                                 ;Reference label

;
; Allocate space to store assigned channel number.
;

MAILBOX_CHANNEL:                        ;
        .BLKW   1                       ;Channel number

;
; Allocate space to store the outgoing and incoming messages.
;

IN_BOX_BUFFER:                          ;
        .BLKB   40                      ;Allocate 40 bytes for
                                        ;received message
        IN_LENGTH=.-IN_BOX_BUFFER       ;Define input buffer length

OUT_BOX_BUFFER:                         ;
        .ASCII  /SHEEP ARE VERY DIM/    ;Message to send
        OUT_LENGTH=.-OUT_BOX_BUFFER     ;Define length of message to
                                        ;send

;
; Finally, allocate space for the I/O status quadword.
;

STATUS:                                 ;
        .QUAD   1                       ;I/O status quadword

;
; *********************************************************************
;
;                      Start Program
;
; *********************************************************************
;

;
; The program first creates a mailbox and assigns a channel to the
; process output device.  Then a message is placed in the mailbox and
; a message is received from the mailbox (the same message).  Finally,
; the program prints the contents of the mailbox on the process output
; device.
;

START:  .WORD   0                       ;Entry mask
        $CREMBX_S CHAN=MAILBOX_CHANNEL,- ;Channel is the mailbox
                PROMSK=#^X0000,-        ;No protection
                BUFQUO=#^X0060,-        ;Buffer quota is hex 60
                LOGNAM=MAILBOX_NAME,-   ;Logical name descriptor
                MAXMSG=#^X0060          ;Maximum message is hex 60
        CMPW    #SS$_NORMAL,R0          ;Successful mailbox creation?
        BSBW    ERROR_CHECK             ;Find out
        $ASSIGN_S -                     ;Assign channel
                DEVNAM=DEVICE_DESCR,-   ;Device descriptor
                CHAN=DEVICE_CHANNEL     ;Channel
        CMPW    #SS$_NORMAL,R0          ;Successful channel assign?
        BSBW    ERROR_CHECK             ;Find out

;
; The program now writes to the mailbox using a write request that
; includes the function modifier IO$M_NOW so that it need not wait for
; a read request to the mailbox before continuing to the next step in
; the program.
;

        $QIOW_S FUNC=#IO$_WRITEVBLK!IO$M_NOW,- ;Write message NOW
                CHAN=MAILBOX_CHANNEL,-  		       ;to the mailbox channel
                P1=OUT_BOX_BUFFER,-            ;Write buffer
                P2=#OUT_LENGTH                 ;Buffer length
        CMPW    #SS$_NORMAL,R0                 ;Successful write request?
        BSBW    ERROR_CHECK                    ;Find out

;
; Read the mailbox.
;

        $QIOW_S FUNC=#IO$_READVBLK,-    ;Read the message
                CHAN=MAILBOX_CHANNEL,-  ;in the mailbox channel
                IOSB=STATUS,-           ;Define status block to
                -                       ;receive message length
                P1=IN_BOX_BUFFER,-      ;Read buffer
                P2=#IN_LENGTH           ;Buffer length
        CMPW    #SS$_NORMAL,R0          ;Successful read request?
        BSBW    ERROR_CHECK             ;Find out

;
; The program now determines how much mail is in the mailbox (this
; information is in STATUS+2) and then prints the mailbox message on
; the process output device.
;

        MOVZWL  STATUS+2,R2             ;Byte count into R2
        $QIOW_S FUNC=#IO$_WRITEVBLK,-   ;Write function to the
                CHAN=DEVICE_CHANNEL,-   ;output device channel
                P1=IN_BOX_BUFFER,-      ;Address of buffer to write
                P2=R2,-                 ;How much to write
                P4=#32                  ;Carriage control

;
; Finally, deassign the channel and exit.
;

EXIT:   $DASSGN_S CHAN=DEVICE_CHANNEL   ;Deassign channel
        RET                             ;Return

;
; This is the error-checking part of the program.  Normally, some kind
; of error recovery would be attempted at this point if an error was
; detected.  However, this example program simply exits.
;

ERROR_CHECK:                            ;
        BNEQ    EXIT                    ;System service failure, exit
        RSB                             ;Otherwise, return

        .END    START

Example 4-2 assigns a read-only channel to the mailbox.

Example 4-2 Mailbox Driver Program Example 2

/*
 * MAILBOX_READER.C
 * C program to demonstrate features of the Mailbox driver.
 * This program is a Mailbox READER. It assigns a READ_ONLY channel to the
 * mailbox. Its partner program is a Mailbox WRITER.
 * Compile with Compaq C on VAX or Alpha systems: 
 * $ CC MAILBOX_READER 
 * $ LINK MAILBOX_READER
 * /
#include <stdio.h>             /* Standard C I/O */
#include <descrip.h>           /* Descriptor structure definitions */
#include <lib$routines.h>      /* LIB$ RTL function definitions */
#include <starlet.h>           /* System service definitions */
#include <ssdef.h>             /* System Service status code definitions */
#include <cmbdef.h>            /* CREMBX definitions */
#include <efndef.h>            /* Event Flag definitions */
#include <iodef.h>             /* I/O definitions */
 
#define $ARRAY_DESCRIPTOR(name,size,array_name) \
    static char array_name[ size ]; \
    struct dsc$descriptor_s name = \
       { size, DSC$K_DTYPE_T, DSC$K_CLASS_S, array_name }
int main(void)
{
/*
 * Message limits are intentionally small to facilitate demonstration of
 * error conditions.
 */
#define max_msg_len 64            /* Maximum output string size     */
#define mailbox_maxmsg 64         /* Maximum mailbox message size  */
#define mailbox_bufquo 128        /* Total buffer space in mailbox */
$DESCRIPTOR(mailbox_name_desc,"MAILBOX_EXAMPLE");
$DESCRIPTOR(EOF_string_desc,
    "End of file read ... waiting for another WRITER");
$ARRAY_DESCRIPTOR(read_buffer_desc,max_msg_len,read_buffer);

#pragma member_alignment save
#pragma nomember_alignment LONGWORD
    struct io_status_block { /* I/O status block */
    unsigned short int condition;
    unsigned short int count;
    unsigned int dev;
    } iosb;
#pragma member_alignment restore

int status, mailbox_channel;
 
/*
 * Create a temporary mailbox with a READONLY channel. Its logical name
 * will be entered into the LNM$TEMPORARY_MAILBOX logical name table.
 */

    status = sys$crembx(0,&mailbox_channel,mailbox_maxmsg,mailbox_bufquo,
        0,0,&mailbox_name_desc,CMB$M_READONLY);
    if (status != SS$_NORMAL)
       (void) lib$signal(status);
 
/*
 * Mark the mailbox for deletion.  This step is not necessary for a temporary
 * mailbox, but is included as an illustration.
 */
    (void) sys$delmbx(mailbox_channel);
/*
 * Loop forever, first waiting until a WRITE channel is assigned to the mailbox
 * and then reading data from it until the WRITER deassigns.
 */
    while (TRUE)
    {
       /* First, check to see if there is a WRITER assigned to the mailbox */
        status = sys$qiow(
            EFN$C_ENF,
            mailbox_channel,
            IO$_SENSEMODE|IO$M_WRITERCHECK,            &iosb,
            0,0,
            0,0,0,0,0,0);

        /* If there was no WRITER, then wait for one.*/
        if ((unsigned int) iosb.condition == SS$_NOWRITER)
        status = sys$qiow(
            EFN$C_ENF,
            mailbox_channel,
            IO$_SETMODE|IO$M_WRITERWAIT,
            &iosb,
            0,0,
            0,0,0,0,0,0);

        /*
         * While the status is good, READ from the mailbox, and echo the
         * data to SYS$OUTPUT.
         */
        while (status == SS$_NORMAL)
        {
            status = sys$qiow(
                EFN$C_ENF,
                mailbox_channel,
                IO$_READVBLK|IO$M_WRITERCHECK,
                &iosb,
                0,0,
                read_buffer_desc.dsc$a_pointer,max_msg_len,
                0,0,0,0);
            if (status != SS$_NORMAL)
               (void) lib$signal(status);
            status = iosb.condition;
 
            if (status == SS$_NORMAL)
            {
                read_buffer_desc.dsc$w_length = iosb.count;
                (void) lib$put_output(&read_buffer_desc);
            }
            else if (status == SS$_ENDOFFILE)
            {
                (void) lib$put_output(&EOF_string_desc);
            }
        }
    }
}

Example 4-3 assigns a write-only channel to the mailbox.

Example 4-3 Mailbox Driver Program Example 3

/*
 * MAILBOX_WRITER.C
 * C program to demonstrate features of the Mailbox driver.
 * This program is a Mailbox WRITER. It assigns a WRITE_ONLY channel to the
 * mailbox. It's partner program is a Mailbox READER.
 * Compile with Compaq C on VAX or Alpha systems:
 * $ CC MAILBOX_WRITER
 * $ LINK MAILBOX_WRITER
 */

#include <stdio.h>             /* Standard C I/O */
#include <descrip.h>           /* Descriptor structure definitions */
#include <lib$routines.h>      /* LIB$ RTL function definitions */
#include <rmsdef.h>            /* RMS status code definitions */
#include <starlet.h>           /* System service definitions */
#include <ssdef.h>             /* System Service status code definitions */
#include <cmbdef.h>            /* CREMBX definitions */
#include <efndef.h>            /* Event Flag definitions */
#include <iodef.h>             /* I/O definitions */

#define $ARRAY_DESCRIPTOR(name,size,array_name) \
    static char array_name[ size ]; \
    struct dsc$descriptor_s name = \
       { size, DSC$K_DTYPE_T, DSC$K_CLASS_S, array_name }

void enable_room_ast(int mailbox_channel, int efn);
void more_room_ast(int efn);
 
volatile int ast_enabled = FALSE;
int main(void)
{
/*
 * Message limits are intentionally small to facilitate demonstration of
 * error conditions.
 */
#define max_msg_len 128           /* Maximum input string size     */
#define mailbox_maxmsg 64         /* Maximum mailbox message size  */
#define mailbox_bufquo 128        /* Total buffer space in mailbox */
$DESCRIPTOR(mailbox_name_desc,"MAILBOX_EXAMPLE");
$DESCRIPTOR(prompt_string_desc,
    "DATA TO SEND TO MAILBOX (<CTRL Z> to terminate) >>>");
$ARRAY_DESCRIPTOR(write_buffer_desc,max_msg_len,write_buffer);
 
#pragma member_alignment save
#pragma nomember_alignment LONGWORD
struct io_status_block {                /* I/O status block */
    unsigned short int condition;
    unsigned short int count;
    unsigned int dev;
    } iosb;
#pragma member_alignment restore
 
int status, mailbox_channel, wait_efn;
 
/*
 * Create a temporary mailbox with a WRITEONLY channel. Its logical name
 * will be entered into the LNM$TEMPORARY_MAILBOX logical name table.
 */
 
   status = sys$crembx(0,&mailbox_channel,mailbox_maxmsg,mailbox_bufquo,
       0,0,&mailbox_name_desc,CMB$M_WRITEONLY);    
if (status != SS$_NORMAL)       (void) lib$signal(status);
 
/*
 * Mark the mailbox for deletion.  This step is not necessary for a temporary
 * mailbox, but is included as an illustration.
 */
    (void) sys$delmbx(mailbox_channel);
 
/*
 * Reserve an event flag to use with "room in mailbox" AST notifications.
 */
    status = lib$get_ef(&wait_efn);
    if (status != SS$_NORMAL)
        (void) lib$signal(status);
 
/*
 * Loop forever, first waiting until a READ channel is assigned to the mailbox
 * and then write data until there is no more data to write.
 */
    while (TRUE)
    {
        /*
         * Wait for a READER to assign a channel. If a READER is already
         * assigned, this will return immediately.
         */
        status = sys$qiow(
            EFN$C_ENF,
            mailbox_channel,
            IO$_SETMODE|IO$M_READERWAIT,
            &iosb,
            0,0,
            0,0,0,0,0,0);
        while (status)
        {
            write_buffer_desc.dsc$w_length = max_msg_len;
            status = lib$get_input(
                &write_buffer_desc,
                &prompt_string_desc,
                &write_buffer_desc.dsc$w_length);

            /* If at end of file (user typed <CTRL Z>) then write EOF to
             * the mailbox, deassign the channel, and exit.
             * The writer should not deassign the channel while the write EOF
             * operation is pending, since the write would be cancelled and
             * the reader would never receive the EOF.  Omitting IO$M_NOW in
             * this QIOW insures that it will not complete until the reader
             * has actually read the EOF from the mailbox.
             */
            if (status == RMS$_EOF)
            {                (void) sys$qiow(
                    EFN$C_ENF,
                    mailbox_channel,
                    IO$_WRITEOF|IO$M_READERCHECK,
                    &iosb,
                    0,0,0,0,
                    0,0,0,0);
                (void) sys$dassgn(mailbox_channel);
                (void) sys$exit(SS$_NORMAL);
            }
 
            /* Write the message into the mailbox.  If there isn't enough
             * room, try again until it fits.
             * Note that if the NORSWAIT function modifier had been eliminated
             * below, then the ROOM_NOTIFY and the retry loop could have been
             * removed. ROOM_NOTIFY was used in this example simply to show
             * its use. It would be more appropriately used when the program
             * has other things it can be working on, as opposed to the
             * example below in which the program is not doing anything except
             * WAITING for room in the mailbox.
             */
            do
            {
                status = sys$qiow(
                    EFN$C_ENF,
                    mailbox_channel,
                    IO$_WRITEVBLK|IO$M_READERCHECK|IO$M_NOW|IO$M_NORSWAIT,
                    &iosb,
                    0,0,
                    write_buffer_desc.dsc$a_pointer,
                    write_buffer_desc.dsc$w_length,
                    0,0,0,0);
                if (status == SS$_NORMAL)
                {
                    /* If there is no longer a reader, just exit. */
                    if ((unsigned int) iosb.condition == SS$_NOREADER)
                    {
                        (void) sys$dassgn(mailbox_channel);
                        (void) sys$exit(iosb.condition);
                    }
                }
                else if (status == SS$_MBFULL)
                {
                    if (ast_enabled)
                        /*
                         * Wait here until the AST routine sets the event
                         * flag.  A read might have already occurred, in which
                         * case the wait will return immediately.
                         */
                        (void) sys$waitfr(wait_efn);
                    else
                        /*
                         * The mailbox was full a moment ago at the time of
                         * write, but a read might have already occurred and
                         * the mailbox might be empty now.  It is possible
                         * that no more reads will complete (and deliver
                         * the AST) before the next write.  So enable the AST
                         * and try the write one more time before waiting for
                         * the event flag.
                         */
                        enable_room_ast(mailbox_channel, wait_efn);
                }                else  /* An unexpected error condition */
                    (void) lib$signal(status);
            }
            while (status != SS$_NORMAL);
        }
    }
}
void enable_room_ast(int mailbox_channel, int efn)
/*
 * This routine requests AST delivery when there is room in the mailbox.
 * AST delivery may be triggered by a read or a cancelled I/O.
 */
{
    int status;
 
    ast_enabled = TRUE;
    status = sys$clref(efn);

    /*
     * This QIOW returns immediately, whether there is room in the mailbox
     * or not.  Even if there is room in the mailbox now, the AST is
     * NOT delivered immediately, but only later when a read or cancel
     * I/O occurs on the mailbox.
     */
    status = sys$qiow(
        EFN$C_ENF,
        mailbox_channel,
        IO$_SETMODE|IO$M_MB_ROOM_NOTIFY,
        0,0,0,
        more_room_ast,efn,0,0,0,0);
}
void more_room_ast(int efn)
/*
 * This AST routine is called when there is room to write more data into
 * the mailbox.
 */
{
    ast_enabled = FALSE;
    (void) sys$setef(efn);
}