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: Assigns a read-only channel
to the mailbox. Waits for another program
to assign a writable channel to the mailbox. Reads, using the IO$M_WRITERCHECK
function modifier, what has been written to the mailbox. Each record
is echoed to SYS$OUTPUT. 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: Assigns a write-only channel
to the mailbox. Waits for a reader. 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);
}
|
|
|