Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Guardian Programming - Interprocess communication.

586 views
Skip to first unread message

Shiva

unread,
May 27, 2015, 2:22:52 PM5/27/15
to
I was reading through the Guardian Programmer's Guide and I came up with the below for one way communication between processes.

$REQ

NAME ':=' "$SER1";
LEN := 5;
ERROR := FILE_OPEN_ (NAME:LEN,FNUM, , , , 1);
BUFFER ':=' "MESSAGE ...";
CALL WRITEX (FNUM, BUFFER, WCOUNT);

$SER1

NAME ':=' "$RECEIVE";
LEN := 8;
ERROR := FILE_OPEN_ (NAME:LEN,FNUM, , , , 1);
CALL READX (FNUM, BUFFER, WCOUNT);

It is textbook simple example but a note which says 'Reply to requestor is made when the READX finishes, allowing the WRITEX in requestor to finish'. Why does that WRITEX wait for READX to complete? How is it accomplished? Is this related to WAITED programming? And if so, why does it relate to a WRITEX alone? Does it not apply to all other system procedures, for example a READX or a READUPDATEX? I understand that it gives the server time to read the message - but how is this accomplished? The receive^depth being set to 1? That's for waited programming I believe? And in that case can it be negated by using NOWAITED approach? I should admit that I have not completed reading through the GPG which might answer my question, but this line amidst the general concept of file I/O before introducing the related system concept has stirred a lot of questions.

As always, thanks for your patient answers in advance. :)

Randall

unread,
May 27, 2015, 2:34:05 PM5/27/15
to
NOWAIT depends on the flags supplied to FILE_OPEN_ in the requester. The WRITEX will completed when the READX completes. You could also use READUPDATEX and complete the operation with a REPLYX in the server. If you use NOWAIT, you will need to call AWAITIOX to complete the I/O in the requester. There are a range of ways of doing NOWAIT, including multi-threading. If you are doing to need a lot of outstanding nowait operations, consider being in C instead of TAL and using pthreads (PUT) to make the code easier to maintain for the long-term - and there are more examples of pthread code in the world than multi-threaded NonStop code. More manuals to read through ;).

Shiva

unread,
May 27, 2015, 2:55:45 PM5/27/15
to
Ha ha, of course. Do you have examples of NOWAITED programming implemented in C? It would be handy. Also about the 'one and a half way communication between processes' mentioned in the GPG - it says to create a requester which will send a WRITEX call to the server without a return data expectation, and wait till server reads the message using READUPDATEX call and still wait until the server uses REPLYX to respond.

And even then the requester cannot read the data but returns file system error, why would such an example be required in real time? And basically even for a WAITED programming the WRITEX from requester would end after a READUPDATEX call ends, why should it wait for REPLYX to complete? Why would the requester even believe that a REPLY would arise as it is just a WRITEX and not a WRITEREADX?

Robert Hutchings

unread,
May 27, 2015, 3:19:31 PM5/27/15
to
On 5/27/2015 1:55 PM, Shiva wrote:
> Ha ha, of course. Do you have examples of NOWAITED programming implemented in C? It would be handy. Also about the 'one and a half way communication between processes' mentioned in the GPG - it says to create a requester which will send a WRITEX call to the server without a return data expectation, and wait till server reads the message using READUPDATEX call and still wait until the server uses REPLYX to respond.
>
> And even then the requester cannot read the data but returns file system error, why would such an example be required in real time? And basically even for a WAITED programming the WRITEX from requester would end after a READUPDATEX call ends, why should it wait for REPLYX to complete? Why would the requester even believe that a REPLY would arise as it is just a WRITEX and not a WRITEREADX?
>
Google "kari tandem" and you should find a website by a Tandem employee
named Kari K. .... it has a full GUARDIAN web server in C code plus some
other utilities and links...

Randall

unread,
May 27, 2015, 3:19:34 PM5/27/15
to
On Wednesday, May 27, 2015 at 2:55:45 PM UTC-4, Shiva wrote:
> Ha ha, of course. Do you have examples of NOWAITED programming implemented in C? It would be handy. Also about the 'one and a half way communication between processes' mentioned in the GPG - it says to create a requester which will send a WRITEX call to the server without a return data expectation, and wait till server reads the message using READUPDATEX call and still wait until the server uses REPLYX to respond.
>
> And even then the requester cannot read the data but returns file system error, why would such an example be required in real time? And basically even for a WAITED programming the WRITEX from requester would end after a READUPDATEX call ends, why should it wait for REPLYX to complete? Why would the requester even believe that a REPLY would arise as it is just a WRITEX and not a WRITEREADX?

The C samples are easily created from TAL. Just slight syntax changes. The calls are the same (except commas instead of colons between some fields). The OSS Programmer's Guide also has some pretty good examples of clients and servers in C and includes pthread samples - note that pthread samples only work in OSS because PUT and SPT only support OSS. The non-threaded samples should work in both OSS and Guardian - there is no difference in either function or performance when using $RECEIVE.

Shiva

unread,
May 27, 2015, 3:23:17 PM5/27/15
to
Thanks, Robert! I've seen that website before. Forgot about them for a moment there! I'll dig around in there! Thanks again!

Randall, I did not know that the OSS programmer's guide had C examples. I'll check them out. Thanks! Thanks for the tip about pThreads there. Afraid of multi threading, just like Tandem. Multiprocessing should do fine, but I will look up PUT! Thanks! :)

Keith Dick

unread,
May 27, 2015, 3:55:38 PM5/27/15
to
Some points the others seem not to have made:

The WRITEX cannot complete before the READX completes because there is the possibility to send an error code back from the server (though the server cannot do that unless it does a READUPDATE instead of a READ).

Don't get this confused with nowait I/O. A nowait I/O operation does not complete until the AWAITIO says it has finished.

Most real servers do use READUPDATE/REPLY so they can return filesystem error codes when that is appropriate.

When the protocol between the requester and the server includes a reply message from the server, the requester must use WRITEREAD, not WRITE.

Perhaps all of those points will be covered later in the Guardian Programmer's Guide. I don't remember how good a job it does of explaining requesters and servers.

Multithreading is not such a good technique to use on the NonStop system as it is on some other systems. It usually is better to use multiple processes. When using multithreading, it is too easy to fall into the trap of communicating between the threads via global memory, and that can get in the way of shifting to multiple processes, such as Pathway serverclasses, in order to improve performance. There are some cases for which multithreading is okay, but the simple rule of thumb would be to avoid it.

Randall

unread,
May 27, 2015, 4:05:10 PM5/27/15
to
On Wednesday, May 27, 2015 at 3:23:17 PM UTC-4, Shiva wrote:
> Thanks, Robert! I've seen that website before. Forgot about them for a moment there! I'll dig around in there! Thanks again!
>
> Randall, I did not know that the OSS programmer's guide had C examples. I'll check them out. Thanks! Thanks for the tip about pThreads there. Afraid of multi threading, just like Tandem. Multiprocessing should do fine, but I will look up PUT! Thanks! :)

I hear your fear - it's actually worse to write multithreading yourself these days. The advantage of using PUT for multithreaded servers is that the thread loop is very simple. Basically you create a bunch of pthreads that have the following structure:

loop forever: {
PUT_RECEIVEREADX
do something simple in a thread
PUT_REPLYX
}

The PUT runtime worries about context switching and dispatching work to available threads. I'm a fan, having had some very high-performing successes with this. Usually you don't have to maintain your own thread context data, just thread-local data.

wbreidbach

unread,
May 28, 2015, 1:36:44 AM5/28/15
to
The first example is not Requester (Client) and Server but sender and receiver.
Client/Server usually means that the client gets an answer from the server besides the errorcode.

The stuff is very simple:
Server (like Randall wrote):
Loop until stop-condition occurs
Readupdatex on $receive
do something
Replyx

Client
just use writereadx to server

Of course all I/O can be done nowaited. In addition you can use writex + readx instead of writereadx but there are only very few programs where this makes any sense.

Randall

unread,
May 28, 2015, 8:41:59 AM5/28/15
to
Just making a PUT outline comment. It's different because you shouldn't call READUPDATEX. Structure for a PUT server is a bit different these days. Adapt or get left behind :) ... code is more maintainable for people not familiar with old-school multithread simulation we used to do in TAL.

Shiva

unread,
Jun 8, 2015, 2:54:50 PM6/8/15
to
Pardon my delayed reply, but I was trying to understand each and every comment made here. And Keith cleared my doubt with this -

The WRITEX cannot complete before the READX completes because there is the possibility to send an error code back from the server (though the server cannot do that unless it does a READUPDATE instead of a READ).

Don't get this confused with nowait I/O. A nowait I/O operation does not complete until the AWAITIO says it has finished.

--

Thanks Randall, Wolfgang, Keith and Robert for the suggestions. I did get the examples from Kari's website and some from OSS programming guide. They do a great job of explaining things.

And the Guardian Programming guide coupled with Guardian Procedure calls reference manual does a good job of explaining the details. Thanks all. :)

Shiva

unread,
Jun 8, 2015, 3:15:48 PM6/8/15
to
I've got a doubt relating to the previous question. So a WRITEX waits for a READX or a READUPDATEX followed by a REPLYX. And this is not NOWAIT programming, this is just pure procedure call sequence. How many such virtual 'waits' are in place for different procedure calls? how about the usual famous procedure calls? What about READX? Does it wait for anything? Or is it only the WRITEs that wait without NOWAIT programming?

Keith Dick

unread,
Jun 8, 2015, 5:03:48 PM6/8/15
to
Shiva wrote:
> I've got a doubt relating to the previous question. So a WRITEX waits for a READX or a READUPDATEX followed by a REPLYX. And this is not NOWAIT programming, this is just pure procedure call sequence. How many such virtual 'waits' are in place for different procedure calls? how about the usual famous procedure calls? What about READX? Does it wait for anything? Or is it only the WRITEs that wait without NOWAIT programming?

Oh, there are lots of them. Any time a procedure does something that requires information from another process, the call could wait for an indefinite amount of time. Remember: Nearly all the interactions between processes are done by messages. You send a message asking for some information or asking for something to be done, and when the other process gets around to it, it sends a reply back.

Your question about whether a READ waits should be obvious. The READ is asking for some data from a device process or another process not connected to a device, so it inherently has a wait in it for that other process to respond.

Many times, the waits are not very long. For instance, asking for the status of a process, if I remember correctly, requires at least a message to the monitor process in the CPU in which the process is running. If you are asking for status by the process' name (not by its phandle or cpu,pin), then the status request first has to find out where the process by that name is located, so the PPD has to be consulted. I know there is a copy of the PPD in each CPU, but I believe it is not directly addressed by the process making the status request. I believe the process making the request has to send a message to the system process that manages the PPD in this CPU to find the location of the process by that name. So, unless there is some optimization in some cases, that simple request for status of a process, given the name of the process, requires two messages/responses. Even if I'm wrong on some of those details, the general point is correct -- many system procedure send mes
sages and wait for the responses before they return.

For an inquiry about a process, those messages and responses generally take very little time, and so are hardly noticed. However, when the system is a little sick, some messages that normally are very quick can become much slower. And if you happen to be making an inquiry about a process on another NonStop system, you have the extra messages involved in sending a request over an Expand link.

See -- there are messages involved with just about everything.

A few things do not require messages. Generally, anything that involves only interacting with the process' own PFS is done directly. You want to know what the current position is in a file you have open? That information is in the access control block that is in your process' PFS, so the filesystem does not have to send a message anywhere to get it. The filesystem code can look at the PFS directly and fetch that value for you. Same with setting a new position in a file.

I imagine there is a list somewhere of which procedures never wait, which ones sometimes wait, and which ones always wait, but if there is, I don't know where it is.

Nowait I/O is kind of a special case, where you get to say, in effect, "Hey, I know this might take a while, so just send the message, but don't wait for the response -- I'll call AWAITIO when I want to find out about the response." In principle, any system procedure that sends messages as part of its work could be implemented in a way similar to what nowait I/O does, but it gets messy very quickly when one call involves several messages. In fact, if I remember correctly, even nowait I/O has some cases in which some preliminary steps before sending the message to the I/O process involve messages for which it waits for a response, so even nowait I/O does not do everything in the nowait manner.

wbreidbach

unread,
Jun 9, 2015, 3:34:44 AM6/9/15
to
In fact everything depends on the FILE_OPEN_. If you do it with nowait_depth set to at least 1, every operation on that file requires AWAITIOX. As far as I remember the only exception is SETMODE.
In general there are 2 arguments to use nowait operations:
1. You do not want to wait for the I/O to complete "forever".
2. You are doing nowait I/O on several files, have one central AWAITIOX and decide about the next action depending on the completed I/O. This is usually called multithreaded programming and in fact this is kind of a state machine.

So as long as you are doing conventional sequential processing there is usually no need to use nowaited I/O. In fact I know of very few application programs using nowaited I/O. Nowaited I/O is very often used in communication processing like TCP/IP or X.25.

Shiva

unread,
Jun 9, 2015, 3:34:06 PM6/9/15
to
Thanks a lot for that Keith. Very informative as always.

And Wolfgang, you're right as always. :)

There's something I had in mind, two terminals sending messages to each other through two different processes which run on each of these terminals. Not sure if I can define this as a Server / Requester concept as I want both of them to send messages the same way to each other. Like a chat system. An OCS inside a tandem session. I was able to design it using a simple WRITEREADX combined with READUPDATEX / REPLYX on the other side... But it was eternally waiting for one message from each other. Only if there's a response to your previous message, you can type your next message. Same rule for both the terminals.

I would have to change the logic to WRITEXs and READXs after opening both these processes(which run on these terminals) as NOWAIT and complete each of these calls using AWAITIOX. That could solve the problem of waiting for a message from the other side to type message on your own, but that could cause a few inadequecies in the solution itself, like: NOWAIT_DEPTH value would determine the number of messages that can be sent through. Say it is set to 20, there won't be more messages expected from the user. But then you'd have to call AWAITIOX 20 times? But you won't know how many messages were sent through? I think there's an option to return the AWAITIOX without canceling if the I/O is not complete yet - but not sure what will happen if there's no IO operation before the AWAITIOX call or everything were completed by previous AWAITIOX call and this was an extra call? And what if we don't close a NOWAIT I/O call with AWAITIOX? Is it always necessary to do so? The reason I ask is because I can't find a way to make this idea to work. If AWAITIOX won't return any error if there are no IO operations on this file num, I hope the below logic will work okie - apart from the basic errors this 'scribbled out logic right out of hand as I think now' might have?

SETMODE (TERM^NUM1, 72, 1); !Use PFS
SETMODE (TERM^NUM2, 72, 1);
SETMODE (RECEIVE^Z, 72, 1);

SETMODE (TERM^NUM1, 30, 1); !Choose any order of reply.
SETMODE (TERM^NUM2, 30, 1);
SETMODE (RECEIVE^Z, 30, 1);

S[0] := "?";
R[0] := "!";
CALL AWAITIOX (TERM^NUM1);
CALL READX (TERM^NUM1, S[1:72], 72,,TAG1);
CALL READX (RECEIVE^Z, R[1:72], 72,,TAG2);

DO
DO
LIMIT := 0D;
CALL AWAITIOX (TERM^NUM1, , , TAG1, LIMIT);
IF NOT <> THEN
CALL WRITEX (TERM^NUM2, S[1:72], 72,,TAG3);
IF NOT <> THEN
CALL AWAITIOX (TERM^NUM2, , , TAG3, LIMIT);
IF <> THEN ERR1 := 1;
CALL AWAITIOX (RECEIVE^Z, , , TAG2, LIMIT);
IF NOT <> THEN
CALL WRITEX (TERM^NUM1, R, 73, TAG4);
IF NOT <> THEN
CALL AWAITIOX (TERM^NUM1, , , TAG4, LIMIT);
IF <> THEN ERR2 := 1;
IF ERR1 AND ERR2 THEN Z := 1 ELSE Z:= 0;
UNTIL Z <> 0;
IF NOT (ERR1 AND ERR2) THEN
BEGIN
CALL READX (TERM^NUM1, S[1:72], 72,,TAG1)
CALL READX (RECEIVE^Z, R[1:72], 72,,TAG2)
END
ELSE
BEGIN
IF NOT ERR1 THEN CALL READX (TERM^NUM1, S[1:72], 72,,TAG1)
ELSE CALL READX (RECEIVE^Z, R[1:72], 72,,TAG2);
END
UNTIL S OR R '=' "EXIT";

This might not work for various reasons as I have not tested it, but have just come up with the logic as I write this post out - so feel free to point out any and all errors. :)

As always thanks for your guidance. Deeply appreciated. :)




Shiva

unread,
Jun 9, 2015, 4:02:32 PM6/9/15
to
Okie, the Guardian Procedure calls manual suggest that I might get an error 26 for each AWAITIOX that does not have a corresponding NOWAIT IO operation. Interesting, might be worth to amend a loop to ignore the error 26 and keep doing AWAITIOXs in case IO operations are initiated or completed, to wait for the next IO operation. Won't you suggest the same? The above code should not find difficulty with error 26 as the AWAITIOXs are placed rightly after CC checks, no?

What about NOWAIT IO calls that are not completed through AWAITIOXs? They just go to a limbo state with no reply to reach back to? Or do I get an error during runtime for that?

wbreidbach

unread,
Jun 10, 2015, 3:18:27 AM6/10/15
to
Am Dienstag, 9. Juni 2015 22:02:32 UTC+2 schrieb Shiva:
> Okie, the Guardian Procedure calls manual suggest that I might get an error 26 for each AWAITIOX that does not have a corresponding NOWAIT IO operation. Interesting, might be worth to amend a loop to ignore the error 26 and keep doing AWAITIOXs in case IO operations are initiated or completed, to wait for the next IO operation. Won't you suggest the same? The above code should not find difficulty with error 26 as the AWAITIOXs are placed rightly after CC checks, no?
>
> What about NOWAIT IO calls that are not completed through AWAITIOXs? They just go to a limbo state with no reply to reach back to? Or do I get an error during runtime for that?

I use to do that in a different way:

file_num := -1;
call awaitiox (file_num,....);
if file_num = term_numx1
and so on

or
case file_num of begin
term_numx1 ->.....
...
otherwise ->;
end;


In additin you could use tags within the writex or readx, awaitiox will give you the tag of the operation that has completed.

Please notice that I am using _ instead of ^. You can use both, but term^num and term_num would be 2 different variables!

Randall

unread,
Jun 10, 2015, 2:49:19 PM6/10/15
to
On Wednesday, May 27, 2015 at 2:22:52 PM UTC-4, Shiva wrote:
Be aware that I/O to some Guardian sub-device types is will cause blocking regardless of FILE_OPEN_ modes. For example, if a READX is posted on the device, and the read gets to the terminal process, a WRITE(READ)X will not complete on that device - from any process - until the user interacts with the device to all the I/O to complete. This is potentially non-intuitive for developers from other platforms. A subtype 30 terminal process does not have to behave that way AFAIK.

JShepherd

unread,
Jun 10, 2015, 6:28:11 PM6/10/15
to
In article <ab9015e3-5366-4077...@googlegroups.com>,
rsbe...@nexbridge.com says...
>
>On Wednesday, May 27, 2015 at 2:22:52 PM UTC-4, Shiva wrote:
>> I was reading through the Guardian Programmer's Guide and I came up with =
>the below for one way communication between processes.
>>=20
>> $REQ
>>=20
>> NAME ':=3D' "$SER1";
>> LEN :=3D 5;
>> ERROR :=3D FILE_OPEN_ (NAME:LEN,FNUM, , , , 1);
>> BUFFER ':=3D' "MESSAGE ...";
>> CALL WRITEX (FNUM, BUFFER, WCOUNT);
>>=20
>> $SER1
>>=20
>> NAME ':=3D' "$RECEIVE";
>> LEN :=3D 8;
>> ERROR :=3D FILE_OPEN_ (NAME:LEN,FNUM, , , , 1);
>> CALL READX (FNUM, BUFFER, WCOUNT);
>>=20
>> It is textbook simple example but a note which says 'Reply to requestor i=
>s made when the READX finishes, allowing the WRITEX in requestor to finish'=
>. Why does that WRITEX wait for READX to complete? How is it accomplished? =
>Is this related to WAITED programming? And if so, why does it relate to a W=
>RITEX alone? Does it not apply to all other system procedures, for example =
>a READX or a READUPDATEX? I understand that it gives the server time to rea=
>d the message - but how is this accomplished? The receive^depth being set t=
>o 1? That's for waited programming I believe? And in that case can it be ne=
>gated by using NOWAITED approach? I should admit that I have not completed =
>reading through the GPG which might answer my question, but this line amids=
>t the general concept of file I/O before introducing the related system con=
>cept has stirred a lot of questions.=20
>>=20
>> As always, thanks for your patient answers in advance. :)
>
>Be aware that I/O to some Guardian sub-device types is will cause blocking =
>regardless of FILE_OPEN_ modes. For example, if a READX is posted on the de=
>vice, and the read gets to the terminal process, a WRITE(READ)X will not co=
>mplete on that device - from any process - until the user interacts with th=
>e device to all the I/O to complete. This is potentially non-intuitive for =
>developers from other platforms. A subtype 30 terminal process does not hav=
>e to behave that way AFAIK.


Additionally,
For disk files a call to SETMODENOWAIT() is a waited operation and
performs in the same way as a call to SETMODE().




wbreidbach

unread,
Jun 11, 2015, 8:54:40 AM6/11/15
to
If you have more than 1 I/O outstanding on a file (neither possible for disk files nor for $RECEIVE) these I/Os will usually be completed in the same order they were created unless you use SETMODE 30, which allows the requests to complete in any order.
Just to get things complete: SETMODENOWAIT on any file opened for nowaited I/O is a nowaited operation and has to be completed using AWAITIO(X) according to the manual. In case the file is not opened for nowait I/O there is no difference between SETMODE and SETMODENOWAIT.

Keith Dick

unread,
Jun 11, 2015, 11:03:01 AM6/11/15
to
On the SETMODE/SETMODENOWAIT situation: There was no SETMODENOWAIT originally. SETMODE is always waited, even if the file was opened for no-wait I/O, despite the fact that some operations require sending a message to the I/O process. I'm not sure why it was done that way, but I imagine SETMODE originally was thought to be for things that just changed the ACB and did not require messages, but then it very quickly also got used for things that did require sending a message to the I/O process. At first, the lack of no-wait operation for SETMODE probably wasn't very much of a problem, since SETMODE wasn't used very often, and when it was, it usually was done at the beginning of processing, so a little bit of waited I/O during initialization wouldn't be much of a problem. Eventually, I imagine after having more experience doing multithreaded programs, it became clear that being able to do SETMODE in a no-wait fashion would be very helpful, and so they added SETMODENOWAIT. T
hey could not just change the way SETMODE worked to required it to be completed with an AWAITIO, since that would break any existing code that used SETMODE on a file opened for no-wait I/O.

Keith Dick

unread,
Jun 11, 2015, 11:31:31 AM6/11/15
to
How sure are you of this? It seems wrong to me.

It certainly is correct that, for example, if there is a READ or WRITEREAD outstanding on a terminal and another program tries to do a WRITE, READ, WRITEREAD, or probably any other operation on that terminal, the second program's operation will not be done until the first READ or WRITEREAD completes. However, if that second program's operation was done on a file number that was from a no-wait open of the terminal, that program's READ, WRITE, or WRITEREAD call will return, as normal. It is just that AWAITIO won't see a completion of that I/O until the earlier READ or WRITEREAD completes. But if the second program is written to do other things it might be able to do while it is waiting to see the completion of the operation on the terminal, the program will be able to do those other things. It won't be completely blocked in the filesystem.

That assumes that second program already has the terminal open before the earlier READ or WRITEREAD occurs. If the second program tries to open the terminal after the earlier READ or WRITEREAD occurred, I don't know whether the I/O process will complete the open request until the earlier READ or WRITEREAD completes. I think it depends on how the I/O process is coded. I am pretty sure it is possible to code an I/O process so that it can service open requests despite having a read pending, but maybe not all of them are coded that way. If the I/O process does not allow opens to complete when another operation is pending, then the second program's open will not complete (whether the open was done waited or no-waited), and the second program won't even get to the point of trying to do its READ, WRITE, or WRITEREAD. But if the second program already had the terminal open for no-wait I/O, the second program won't get blocked in the filesystem.

wbreidbach

unread,
Jun 11, 2015, 2:52:18 PM6/11/15
to
I think there is a misunderstanding. I was not talking about a second program but about one program doing more thatn one I/O against one OCB. Just for example:
The program establishes a TCP/IP session, does a SETMODE 30 and posts a "READ", afterwards the program does "WRITES" within the same session. Because of the SETMODE 30 the write operations may complete before the program gets any answer.
I do not think that this works with a terminal, afaik SETMODE 30 is not really supported for terminals. It would have been useful within my BROWSER tool but there is no SETMODE 30 so I am pretty sure I have tried it long time ago and it did not work.

Randall

unread,
Jun 11, 2015, 4:20:37 PM6/11/15
to
Just to be clear, I was talking about sub-type 30 process, not SETMODE 30. DEVICEINFO2 calls can happen asynchronously if desired.

Keith Dick

unread,
Jun 11, 2015, 11:18:53 PM6/11/15
to
Shiva wrote:
> Okie, the Guardian Procedure calls manual suggest that I might get an error 26 for each AWAITIOX that does not have a corresponding NOWAIT IO operation. Interesting, might be worth to amend a loop to ignore the error 26 and keep doing AWAITIOXs in case IO operations are initiated or completed, to wait for the next IO operation. Won't you suggest the same? The above code should not find difficulty with error 26 as the AWAITIOXs are placed rightly after CC checks, no?
>
> What about NOWAIT IO calls that are not completed through AWAITIOXs? They just go to a limbo state with no reply to reach back to? Or do I get an error during runtime for that?

I don't have much free time at the moment, so I cannot take enough time to study what you have written to get a complete understanding of what you want to do and why you are having trouble doing it. But I am getting an impression that you are overlooking something about AWAITIO and how that something can make organizing some programs pretty easy. I might be wrong and this is not applicable to your program, but let me say a little about it, then you can decide.

The thing you might be overlooking is that you can tell AWAITIO to wait for an outstanding no-wait operation on any file to complete by passing -1 as the file number to wait for. And when you give a time limit to AWAITIO when telling it to wait for a completion on any file, if the time limit is reached, AWAITIO does not cancel any of the outstanding operations.

You can use this feature of AWAITIO to turn very complex interactions into something almost as simple as an ordinary server program. You have just one AWAITIO call in the program that serves to get the next thing the program must do (respond to whatever I/O operation just completed). You use the tag argument on the no-wait operations to keep track of where the processing is for each I/O. Probably the best way to do this is to define a structure in which you keep all the information associated with a given file and pass the address of that structure as the tag argument when you start a no-wait I/O operation on the file. If a file might be doing a number of different things at different times, you have to decide whether it is better to have a different structure for each of the different things you might do, or have different sections of a single structure.

When AWAITIO reports a completion, the tag value it returns will let you go directly to the structure for the file involved to figure out what to do next -- usually you would have a field in the structure that is a code number that says what state the processing is at (think of a state machine, if you know what that is). That state, possibly together with results of the most recently-completed operation, and maybe other data in the structure, would determine what to do next. Think a big CASE statement using that state code number to get to a section of code that performs the next step (or that calls a procedure to do the next step). Often, the next step of the processing will include starting another no-wait operation, or occasionally starting no-wait operations on a number of files. Sometimes, the recently-completed operation finishes a task, and no further I/O operation needs to be started. In any case, after completing whatever the next processing step is, the progra
m loops back to the one AWAITIO call to wait for the next operation to complete, giving it the next thing to do.

So to apply this notion to your problem, I think you would have your program open its terminal, start a no-wait WRITEREAD on the terminal, then go to the AWAITIO for file -1. One of the things that the program might receive from the terminal is a command that identifies another terminal and text to be sent to it. Your code probably would look to see whether it has that terminal open yet. If not, it would save the text in your structure, do a no-wait open of the other terminal, record the file number in your structure, then loop back to the AWAITIO call. When you get the completion of the no-wait open of the other terminal, or if the other terminal was already open when the request to send the message was received, start a no-wait WRITE of the text you saved, and loop back to the AWAITIO call. You might want to dynamically allocate space to save the text to be sent while waiting for the no-wait open to finish, in case a second request to send a message arrives before the
no-wait open completes. If you want to allow sending messages to multiple terminals, you might dynamically allocate storage for each new other terminal a message is sent to, so you don't have to put an arbitrary limit on the number of other terminals you can communicate with. The address of that dynamically allocated storage might be the tag argument you use in the I/O calls to open and write to its terminal.

Or you might make the program open the terminal, send the message, then close the terminal for each message, rather than keeping the other terminal open forever after you send the first message to it.

If you get a second message for a terminal before you are completely done sending the previous one, you'll have to decide whether to somehow link the second message to the first, so you can send the second one when the first one completes, or write an error to your own terminal that says the previous message is still in progress. If you open the terminal, send the message, then close the terminal for each message, I think you would not have to check that a previous message was completely done, but just do another no-wait OPEN for each message.

You might want to identify the other terminal not by its name, but by the name of the user sitting at it. You probably could make that work by having your program take a command from its terminal (or as its command line argument) that gives the name of the person using it. When your program gets that command, it would write a record to a key-sequenced file whose record contains the user name and the terminal name, with the user name being the key. All instances of your program would use the same file. When your program gets a request to send a message to a user, it would have to allocate some space to save the text of the message, do a FILE_SETKEY_ using the user name as the key value, do a no-wait READ to see whether that user name is in the file, then start a no-wait WRITEREAD on your terminal and loop back to the AWAITIO. When your program gets the completion of that READ from the users file, it will know whether the user exists, and if so, what terminal he or she is
using. If the user exists, and you don't already have the terminal open, your program would start a no-wait open of that terminal, then loop back to the AWAITIO. When the open of the terminal completes, or if it already had the terminal open, your program would start a no-wait WRITE of the saved text to the other terminal, then loop back to the AWAITIO. When your program quits, it would have to remove the record for its user from the file. If either the no-wait READ of the users file, the no-wait open of the other terminal, or the no-wait WRITE to the other terminal gets an error, you probably would want to report that to your terminal. To do that, you could use CANCEL to cancel the WRITEREAD to your terminal, start a no-wait WRITE to your terminal, then loop back to the AWAITIO call. When the WRITE of the error message completes, start a WRITEREAD to your terminal and loop back to the AWAITIO call.

As when identifying the destination by the terminal name, you might want to dynamically allocate space for each destination user so you are not limited to a preset number of them you can communicate with, or do the whole lookup, open, send, and close for each message, rather than keeping the terminals open.

If the program dies without deleting its user's record, that would be a little bit of a problem. A way to avoid that problem would be to record your process' phandle in the users file instead of the terminal name, and send the messages to the other process rather than the other terminal. This would give you an error if the other process had died without removing its record from the users file. of course, that means your program would have to open its $RECEIVE, start a no-wait READUPDATE on $RECEIVE, and when it gets a message on its $RECEIVE, it would have to CANCEL the WRITEREAD to its terminal, start a no-wait WRITE to its terminal to write the text of the message, REPLY to the $RECEIVE message, start a no-wait READUPDATE from $RECEIVE, then loop back to the AWAITIO. When the WRITE to its terminal completes, it would start a no-wait WRITEREAD to its terminal, then loop back to the AWAITIO.

CANCELing the WRITEREAD to your terminal when you receive a message to be written to the terminal could be annoying if the user was in the middle of typing something. You could instead save the message to be written and write it to the terminal after the next input from your user. That would keep your user from seeing incoming messages if he or she were not sending messages at the time. You could use SIGNALTIMEOUT when you receive and save an incoming message to set a time limit on how long it will wait, and only CANCEL the WRITEREAD to the terminal if the SIGNALTIMEOUT expires (that will be reported by a message on $RECEIVE). If the user completes the WRITEREAD by entering something before the SIGNALTIMEOUT expires, you could use CANCELTIMEOUT to cancel the signal.

A way to avoid the problem of possibly canceling when the user is typing might be this: Don't always have a WRITEREAD outstanding on your terminal, but have your program take BREAK ownership of the terminal (SETMODE 11 and 12), watch for a BREAK message to arrive on $RECEIVE, do the no-wait WRITEREAD on your terminal after getting the BREAK message, and don't post another WRITEREAD when that one completes -- wait for another BREAK message. With this approach, the user would have to push the BREAK key to signal his or her intention to enter a command. When the user sees your program's prompt, he or she could enter a command and push Enter. He or she would have to press the BREAK key to enter another command. If the user pushes the BREAK key, but does not push Enter after that, that would block messages sent from other terminals from appearing. You could defend against that by starting a SIGNALTIMEOUT timer, or just let the user block incoming messages that way.

As long as there is at least one outstanding no-wait operation, the AWAITIO call will not get an error 26. If the logic of the program is such that it might happen that sometimes there would not be any outstanding I/O, that probably means the program is logically completely done and should stop. But nearly always, when you use this approach, it is because there always will be something the program is waiting to do.

As I said at the start, I am not sure about what you are trying to do, and I am not sure you are overlooking this.

Flong

unread,
Jun 12, 2015, 9:13:12 AM6/12/15
to
A super description of using AWAITIO -1 from Keith.

I have just one additional note to add.
AWAITIO has been around forever. More modern versions of Guardian (oops sorry, the NonStop OS) support a set of procedures that are a safer (and more capable) alternative. These are the FILE_COMPLETExxx procedures (see the Guardian Procedure Calls Reference Manual for details). The big difference is that the file complete procs can be used to define a set of files that you want to wait on.

AWAITIO -1 waits on any file. This is important if you use any sytem library facilities that can open files nowait. You don't want to "steal" the completion of a file being used by the system "under the covers". The most obvious case of this in a process the uses SQL (either MX or MP). For example, suppose you open a cursor and do a fetch. If you call AWAITIO -1 before c;losing the cursor, its possible to "steal" a file completion that belongs to SQL.

So my suggestion is that for new code it is best practice to use the FILE_COMPLETEXXX procedures instead of AWAITIO.

Just my twopence worth.

J.

Keith Dick

unread,
Jun 12, 2015, 2:07:26 PM6/12/15
to
That's a good point to bring up. Thanks for mentioning it. The potential for code using AWAITIO -1 to interfere with library code you call that also uses no-wait I/O has always been there when you use the approach utilizing AWAITIO -1, and you have had to be wary of what libraries you want to use do in that regard. I should have included a warning about that.

I have never used the FILE_COMPLETExxx procedures, but they do seem to address that danger of using AWAITIO -1. You just have to be careful that whenever you open or close a file, you adjust the set of files that FILE_COMPLETExxxx monitors, but that seems not to be a very big burden.
0 new messages