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.