VSDRV for RSX-11M V3.2 _Introduction_ RSX-11M V3.2 provides several means for communication between tasks. Among these are global event flags, common memory, and the send/receive data directives. The latter provide for data transfer from one task to another, but restrict the amount of data to 13 words per transfer. RSX-11M-Plus allows variable-length data blocks of up to 256 words in length; this feature is not provided with RSX-11M, because it requires support for secondary executive pool, which RSX-11M does not have. Because a "variable length send/receive" function can be useful in a number of applications, a simple driver has been written to provide some of the same functionality. The RSX-11M device name for this driver is VS:, and its features are as follows: The driver is actually a queue manager, which has its own private pool of dynamic memory. (This pool is assembled to a minimum size, and may be expanded when the driver is loaded, using LOA VS:/SIZE=x for a total driver size of x words.) Functions provided include: create a queue, put a message in a queue, get a message from a queue, and delete a queue. Each queue has a six-character name. When a task accesses a queue, it specifies the queue name. By default, each task can access a queue whose name is the same as the task's name. If selected when the driver is built, a protection feature prevents non-privileged tasks from creating, deleting, or getting messages from any queue other than the default queue. Queues are created explicitly through driver QIO calls, and are always accessed in FIFO (first in-first out) order. A task can cause its "get message" request to wait until a message has been put into the queue. _Functions, Parameters, Status Codes_ Here is a list of the QIO function codes recognized by the VS: driver. Function codes are in octal. IO.KIL (0012) Cancel all waiting read requests for this task. IO.WLB (0400) Put a message into any existing queue. IO.RLB (1000) Get a message from a queue. IO.RLB!SF.WAI (1200) Wait for a message to be put into a queue, and get it. IO.ATT (1400) Device attach -- ignored. IO.DET (2000) Device detach -- ignored. IO.CRQ (2400) Create a message queue. IO.DLQ (3000) Delete a message queue and all queued messages. IO.DMP (3400) For debugging, dump a snapshot of the driver's pool. The six-word QIO parameter list is used by VS: as follows: Word 1 -- Buffer address (with IO.WLB, IO.RLB, IO.DMP) Word 2 -- Buffer length in bytes (with IO.WLB, IO.RLB, IO.DMP) Word 3 -- First word of RAD50 queue name (with IO.WLB; for privileged tasks, also with IO.RLB, IO.CRQ, IO.DLQ) Word 4 -- Second word of RAD50 queue name (with same functions as word 3) Word 5 -- Unused Word 6 -- Unused Notes: * Buffers used with VS: must be aligned on word boundaries. * The "read" function returns the name of the sending task (in RAD50 representation) in the first two words of the buffer. Thus, if you are expecting messages of up to N bytes in length, you must provide at least N+4 bytes of buffer space for a "read" operation. Status codes that can be returned by VS: in the first word of the I/O status block include: IS.SUC (+1.) Successful completion; second word of IOSB is a byte count. IE.ABO (-15.) Operation aborted due to IO.KIL or I/O rundown at task exit. IE.PRI (-16.) Privilege violation--unprivileged task attempted to create, delete, or read a queue other than its default, or attempted to do an IO.DMP function (only if protection enabled). IE.QNF (-101.) Queue not found. IE.QEX (-102.) Queue already exists (on attempt to create a queue). IE.NMS (-103.) No more space in VS: private pool. IE.RAW (-104.) Read already waiting on queue (for IO.RLB!SF.WAI). IE.NOM (-105.) No message available in queue (for IO.RLB without SF.WAI). IE.UBS (-106.) User buffer is too small to hold entire message (or to hold entire driver pool, for IO.DMP). _Disadvantages and advantages of VS:, compared with variable send/receive_ Disadvantages of VS: 1. One or more LUN's must be set aside for use with VS: (one for each simultaneous outstanding wait on a queue). Variable send/receive requires no LUN's at all. 2. VS: driver and pool occupy additional memory space, which cannot be used for other purposes when VS: is not in use. Variable send/receive code is part of the exec, and pool space is part of the exec's secondary pool, usable by other components. 3. VS: requires that queues be created and deleted explicitly. Variable send/receive has a queue automatically created for each task. 4. If a task does a read-and-wait function, it cannot be checkpointed. A task that is waiting on a receive-data AST can be checkpointed. Advantages of VS: 1. VS: can be used under RSX-11M, which does not support variable send/receive or secondary exec pool; the only cost in terms of exec resources is the I/O packet required for each request. 2. VS: queues exist independent of tasks in the system. When a task exits, no messages are deleted (not even from the task's "default" queue); queues can still be "flushed" by deleting and immediately re-creating them. 3. VS: allows a task to manipulate several queues of messages, while variable send/receive provides only one queue per task. With VS:, tasks can "share" queues (under some protocol established among them) if the application requires. Features that might be nice, but ... 1. VS: does not allow messages to be retrieved in random order, nor does it allow a message to be examined and put back into the queue. 2. VS: does not provide any way for automatically flushing a queue when a task exits (see advantage 2, however); this might be a useful feature in some cases. 3. VS: does not restrict queue or message sizes beyond physical limitations due to pool size. It might be useful to have restrictions on the number of messages in a queue, or the size of an individual message. Currently, any task, either maliciously or due to a bug, can fill up VS: pool with messages put into a queue that is not being read, thus causing legitimate messages to be rejected due to lack of space. Some kind of "active/dormant" flag within each queue could be used to determine whether messages can be queued, without disturbing those messages that are already in the queue. Other possible solutions include: some form of timeout count on each message; restricted access to queues (by UIC, list of task names, ???); all of these have additional disadvantages as well. 4. (Add your own to this list, and send them to me, John Osudar!) _Topics for advanced users_ These topics are not covered here, but you may want to think about them (if you know what they are!): 1. How to use the QIO AST mechanism to emulate a "receive data AST" 2. How to design protocols for communication among tasks using VS: _Use of VS: from FORTRAN_ Here are some notes on using VS: from a FORTRAN program. This information can also be found in the RSX-11M/M-PLUS Executive Reference Manual. (1) Setting up the QIO parameter list The QIO parameter list is an array of six integers. The VS: driver uses these six words as described previously. To create the parameter list, use a statement like this: INTEGER IPARM(6) To put a buffer address into the first word, use the library subroutine GETADR, like this: CALL GETADR(IPARM(1),IBUFER) (Here, IBUFER is the name of the buffer array that is used to transfer messages between the driver and the task.) To put a buffer size into the second word, use a simple assignment statement: IPARM(2)=124 (This tells the driver that 124. bytes of storage are available in the buffer on a read request, or that the message being sent is 124. bytes long, on a write request. This would require that the buffer array be declared to contain at least 124/2 = 62 words, e.g. INTEGER IBUFER(62) or the equivalent.) To put a RAD50 queue name into the third and fourth words, you must create a two-word integer array containing the queue name, and set the name into the array with a DATA statement, like this: INTEGER QNAME(2) DATA QNAME/3RMYQ,3RUE5/ (The constant form 3Rxxx is like 3Hxxx or 'xxx' but creates a RAD50 representation of the character string. In this case, QNAME(1) will contain the string 'MYQ', and QNAME(2) will contain 'UE5', which together form the name of the queue 'MYQUE5'.) Then, you put the queue name into the parameter list with two simple assignments: IPARM(3)=QNAME(1) IPARM(4)=QNAME(2) (There are other ways to do this, especially for cases where the queue name is not constant, but may be determined when the program is run. Consult the FORTRAN User's Guide for subroutines that convert ASCII character strings to RAD50, if you need them.) Note that you do not need to set up the first two words of the parameter list unless you are doing a "read" or "write" function, and that the third and fourth words can be set to zeroes if you want to use your task's default queue name (same as the task name). (2) Assigning a unit to the VS: driver In order to access the VS: driver, you must have a logical unit assigned to it. This can be done when you build your task, with an option like this: ASG=VS:3 (which assigns LUN 3 to the VS: driver) Also, you can do this with a subroutine call within your program: CALL ASNLUN(3,'VS',0) Here, you specify the LUN as the first parameter; the other parameters must be 'VS' and 0 as shown. You can assign as many LUN's to the VS: driver as you need. In particular, if your program will be waiting for messages from more than one queue at a time (with the IO.RLB!SF.WAI function), then you must have as many LUN's assigned as there will be simultaneous waits. (3) Doing the actual operation To do the operation, you call one of the library subroutines QIO or WTQIO. In both cases, the calls are identical: CALL WTQIO(fnc,lun,[efn],,[isb],[prl][,ids]) The parameters enclosed in [...] are optional, but commas must be included as shown, even if some parameters are left out. The parameters are: fnc One of the octal function codes, specified in FORTRAN with a leading " to signify "octal"; e.g. "1200 lun The logical unit number on which to perform this operation. The LUN must be assigned to VS: already. efn An event flag number. Event flags are single-bit flags that can be used to signal the occurrence of certain events, in this case the completion of the requested operation. You should probably use "local" event flags here (event flags used only by your task); these flags are numbered from 1 to 32. It is through the event flag that you determine when the operation has been completed. isb The name of a two-word array, the I/O status block, which receives a status code in the low byte of the first word, and a byte count in the second word. The status code should be +1 for successful operations. The byte count tells you how long the message is when you do a "read", and should equal your message length when you do a "write". prl The name of the six-word parameter list array. ids An integer variable into which the system places a "directive status"; if you have made any mistakes, or if some condition has occurred that has prevented your request from being passed to the VS: driver, this word will contain an error code. Normal completion should produce a +1 status. The difference between using the QIO and WTQIO calls is this: WTQIO will not return control to your program until the operation has been completed, while QIO will return as soon as the operation has been set up and sent to the VS: driver. Look at the Executive Reference Manual for subroutines that test event flag status, or stop/wait for event flags to be set, if you are planning on using the QIO call instead of the WTQIO call.