RSD1901@TAMSIGMA.BITNET (Shane Davis) (01/12/88)
Hello again, In lieu of a VMS Internals book (the one I had been using was borrowed and the one from whom I borrowed it has taken a position elsewhere), I must ask this question of the Info-VAX list. Can a process in Local Event Flag Wait (LEF) state be interrupted by AST's generated by $QIO requests? I do not want to complicate my program by nesting hibernations and am not entirely sure if $WAKEing one hibernation will leave the other pending hibernate intact or if it will wake the process altogether. What I need to do is issue a $QIO to a mailbox and have its AST routine (as given by the astadr parameter to the $QIO) interrupt the process for a short time to let the sender of the mailbox message know that a resource is unavailable while waiting for another QIO read request to a network (EXOS TCP/IP is the software) to complete, signalling that a remote server has been restarted. After the notification of the server restart has been received, the program will then go back into HIB and wait for subsequent requests since the resource is again available. The routine in which all this is happening will also be entered from an AST processing routine that was started while the process was under one $HIBER. Thus, when the server is again reachable, the process should reenter HIB. If I do use $HIBER to wait for the network QIO to complete, will a $WAKE also elimintae the original $HIBER or only the one entered to wait for the network? The approach I'm using now is to use $SYNCH to wait for the network QIO to complete, but writes to the forementioned mailbox must be processed even while waiting for the network resource. So, after all this confusing explanation, I'll restate my main question: can a process waiting for a single event flag be interrupted by AST's? If not, will nested hibernations and wake-ups work? After all this, I will probably just put a flag logical name in LNM$SYSTEM and have it indicate to the mailbox message senders whether the resource is or is not available. Thanks in advance, and I hope you can think straight after my attempt at an explanation of my dilemma. Please send responses to XPMAINT@TAMVENUS.BITNET. --Shane Davis
LEICHTER@VENUS.YCC.YALE.EDU ("Jerry Leichter ", LEICHTER-JERRY@CS.YALE.EDU) (01/13/88)
The author starts off with a simple question: Can a process in Local Event Flag Wait (LEF) state be interrupted by AST's generated by $QIO requests? The answer to this question, as written, is YES, but... I do not want to complicate my program by nesting hibernations and am not entirely sure if $WAKEing one hibernation will leave the other pending hibernate intact or if it will wake the process altogether. Still a simple question with a simple answer (see below), but the waters getting murky... What I need to do is issue a $QIO to a mailbox and have its AST routine (as given by the astadr parameter to the $QIO) interrupt the process for a short time to let the sender of the mailbox message know that a resource is unavailable while waiting for another QIO read request to a network (EXOS TCP/IP is the software) to complete, signalling that a remote server has been restarted. After the notification of the server restart has been received, the program will then go back into HIB and wait for subsequent requests since the resource is again available. Looks good, absolutely standard use of QIO's and AST's. The routine in which all this is happening will also be entered from an AST processing routine that was started while the process was under one $HIBER. Now things have gotten completely murky, as it is no longer clear where and when the proposed wait for an event flag is to take place. Thus, when the server is again reachable, the process should reenter HIB. If I do use $HIBER to wait for the network QIO to complete, will a $WAKE also eliminate the original $HIBER or only the one entered to wait for the network? OK, we need to step back and understand how AST, event flags, and such work, as once you start digging, there isn't enough information here to really answer the question. First off, a process has an "AST active" bit that can be either on or off. When it is on, the process is running "in AST state"; it can receive no further AST's (at least at the same our an outer access mode. That needn't concern us for normal user-mode progamming, though it explains why DCL can receive a supervisor-mode CTRL/Y AST even while a program is executing in user-mode AST state.) When a process is NOT in AST state, it can usually be interrupted by AST's. There two exceptions: SUSP(ended) processes, and those that have disabled AST delivery with the SYS$SETAST() call. Both hibernation and LEF state can be interrupted by an AST. So, the first answer to the first question is yes - EXCEPT that saying that a process is in LEF state doesn't give the whole picture. It may have entered LEF state while in AST state (by doing a QIOW or a SYNC or an explict EF wait). In that case, the "AST active" bit is still on, and no further AST's can be delivered. (The same goes, of course, if the event flag wait was started after using SYS$SETAST to disable AST delivery; or if the process was suspended while waiting.) Now for hibernation. Hibernation is a very simple process state - a process is either hibernating or not. A hibernating process will come out of hiber- nation when it is woken, or when an AST is delivered to it. In the latter case, the previous hibernation state is remembered, and the process will resume hibernation after the AST routine exits. (However, a WAKE will clear the remembered hibernation state even while an AST is in progress, so another process can always issue an effective WAKE - and a process can issue a WAKE for itself while at AST level and thus "kick" its non-AST-level code.) If a process begins hibernating while in AST state, or with AST delivery disabled, no AST can be delivered to it to break it out of its hibernation. In that case, only a WAKE from another process will get it moving again. Again, there are complications if you consider inner access modes. Hiber- nation is process-wide; it is not tied to access mode. A process that issues a $HIBER call in kernel mode will wake up if another process issues a user- level $WAKE call for it. On the other hand, while a process is in an inner mode, it cannot receive any AST's for outer modes, so this particular avenue of awakening from the hibernation may be blocked. The fact that there's only one level of hibernation has an important consequence, due to the fact that DCL SPAWN and ATTACH rely on hibernation - processes may receive "unexpected" wakeups. Hence, correct code must NEVER assume that the simple fact that it woke from hibernation means that some event it was waiting for has taken place; it must check. That is, the ONLY correct way to use hibernation for synchronization is: sleep: while (event-hasn't-happened) $HIBER() wake: mark-event-as-happened; $WAKE(...) So, hibernations don't nest, and hibernating while in AST state is almost certainly not what you want to do. Finally, how do hibernation and LEF state mix? Well, since a process must take a deliberate action to enter either state - i.e., it can only enter either state from the CURR state - the answer, in a sense is, they don't. However, there is one situation in which both can (sort of) apply: Process hibernates, receives an AST (temporarily breaking it out of hibernation), then waits for an event flag. In that case, the process is in LEF state (with the AST active bit set); it's got a nested hibernation request waiting for it once it exits from its AST routine, which it will presumably do after its event flag wait completes. Since the AST active bit is set, no further AST's can be delivered to the process. BTW, if I remember right, the mechanism for all this is very simple: While a process is hibernating, its stored PC points to the call to SYS$HIBER. AST delivery simply makes the process computable; when the AST routine completes, the process returns to its stored PC, which calls SYS$HIBER again. A WAKE works because when applied to a process that is not currently hibernating - as a process executing an AST is not - it sets a "wake pending" bit in the process header. When this bit is set, the next call to SYS$HIBER returns immediately, without sleeping. -- Jerry
jeh@crash.cts.com (Jamie Hanrahan) (01/13/88)
In article <8801121623.AA23046@ucbvax.Berkeley.EDU> RSD1901@TAMSIGMA.BITNET (Shane Davis) writes: > [questions re. AST deliverability in LEF state, nested HIBERs, etc.] I'm a little brain-dead at the moment, and I didn't completely follow your description of your application design. I'm particularly puzzled about what you meant by "nested $HIBERs". (More on this later.) However, are the rules concerning AST delivery, HIBER/WAKE, etc. An AST queued to a process will be delivered to that process (i.e. the AST procedure will be called) unless one or more of the following conditions are true: 1. The process already has an AST active at the same access mode (user, supervisor, executive, kernel), or at a more privileged access mode, than the access mode of the AST. 2. The process is running or waiting at a more privileged access mode than that of the AST. For instance, if you're looping in kernel mode, or even if you're in HIB state as a result of a $HIBER call that you did while in kernel mode, no user-, supervisor-, or executive-mode ASTs can be delivered. For processes that stay in user mode except for brief periods when they call system services or RMS -- which includes most processes we deal with -- the above rules boil down to: The process must not already be executing (or waiting) at AST level. If it is, the next AST is queued, as are all subsequent ASTs. Each gets delivered when the previous one RETs. (More on the "or waiting" part in a few moments.) 3. The process is in a wait state that precludes AST delivery. Of the wait states which programmers voluntarily put processes into, only SUSPend precludes AST delivery; ASTs are deliverable in LEF, CEF, and HIB states. They are not deliverable in most (if not all; I don't have my source fiche at hand) of the wait states into which the system "automatically" places processes, such as page fault, the infamous MWAIT state, etc., as in most such states the process is sitting at IPL 2, which inhibits the AST delivery mechanism. (The rules for the SUSPend state change in V5. You'll be able to say "suspend, but leave kernel-mode ASTs deliverable"; this allows a process suspended in this way to be deleted while blocking user-mode ASTs (presumably the application's ASTs). For most user-generated code this will have little significance, except that they'll have a way to kill off processes that get stuck in suspend.) 4. The process has explicitly blocked AST delivery at the access mode of the AST. (It can do this by calling the $SETAST service from the mode to be controlled. Of course, it can later enable ASTs with the same service, at which time any ASTs that got queued up in the interim will be delivered, one at a time. This is a good way to hold off AST delivery while you process data gathered by the AST, or to avoid problems with using non-AST-reentrant code when an AST might be delivered, etc.) 5. The process is executing at IPL 2 or above. (Many system services, $QIO for example, raise IPL to 2 temporarily to block AST delivery, even special kernel ASTs. They only do so for very short times. As with $SETAST, as soon as IPL is lowered, any ASTs that were queued while IPL was raised will be delivered, unless something else inhibits them.) So, assuming that you are talking about all user-mode code, and you're not playing with $SETAST, yes, ASTs are deliverable in LEF state, and also in HIB... with one important exception. It's not really an exception, but it's not obvious: IF YOU ENTER THE WAIT STATE (LEF, CEF, HIB) FROM CODE INVOKED VIA AST -- that is, if an AST was active when you entered the wait state; doesn't matter whether the AST procedure itself, or an inner procedure which the AST procedure called, calls the `wait' system service -- ASTs CANNOT BE DELIVERED WHILE YOU ARE IN THAT WAIT STATE. So, for instance, if you: (in non-AST code:) do something that will eventually cause an AST to be queued $HIBER (whatever it is that you did causes the AST to be queued) (the AST is delivered) (in your AST procedure:) do something that will eventually cause an AST to be queued $HIBER RETURN You're stuck. You called $HIBER at AST level; therefore ASTs can't make you run. Some other process will have to $WAKE you. It doesn't matter if you use two $HIBERs as shown here, or a $HIBER in the non-AST and a $WAITFR in the AST... what matters is that you entered a wait state, ANY wait state, from AST level. This blocks ASTs at your current access mode. There might be some applications where you want this behavior. As a general rule, don't call "wait" services from AST level. -------------------- Now, about nested HIBERs and all that... I think a brief explanation of how wait states interact with ASTs is in order. When you use $SUSPND, $HIBER, or $WAITFR (or any of its variants, including $SYNCH), there are three things to keep in mind. First, there's a bit (or bits) somewhere which, if set, will allow you to run. For $WAITFR and etc. the bits are event flags; for $SUSPND and $HIBER they are the "resume pending" and "wake pending" bits, set by the $RESUME and $WAKE services, respectively. Second, when you call any of these services, they *first* check the appropriate bit(s) to see if you really should go into the wait state. For instance, if someone tries to $WAKE you and you aren't hibernating, your wake pending bit is set. If you try to hibernate after that, $HIBER checks (and clears) the wake pending bit, and you keep right on going. A subsequent $HIBER will succeed (unless another $WAKE occurred after the first $HIBER). Note that no count of outstanding wake requests is kept-- only the fact that there's at least one. $SUSPND works the same way, only it uses a different bit. $WAITFR, et al, don't clear the event flags after checking them, but otherwise are the same. Third, and here's the fun part: When you call any of these services, if you do in fact go into the desired wait state, THE STORED PROGRAM COUNTER (PC) FOR YOUR PROCESS POINTS (effectively) TO THE CALL TO THE SYSTEM SERVICE, NOT THE NEXT THING AFTER IT. What does this mean? It means that whenever your process comes out of a wait state, you call the system service that put you in the wait state AGAIN. The reason for the wait is re-evaluated, if you will. You might go right back into the wait state, depending on what else has happened. Let's see how this all works to make ASTs and wait services act the way they do: (non-AST code) start an I/O, specifying an AST procedure $HIBER (AST procedure) have we done enough IOs? if yes $WAKE (path a) else start the next one (path b) endif RETURN We've started the first I/O (from the non-AST code) and entered $HIBER. (We'll assume that the "wake pending" bit was originally clear.) The IO completes. The delivery of the AST makes your process computable; that is, it takes it out of HIB state. Completely. The only record that we were ever in HIB state is that stored PC that's pointing to the call to the $HIBER service. So the process is made current. Whups! There's an AST to be delivered, so the saved PC is ignored for now; instead the system climbs down to user mode and CALLs your AST procedure. The AST runs. The first time through it takes path b; it starts another IO, then RETs to its caller (the AST delivery mechanism). There are no more ASTs queued, so the saved PC gets used to restart execution of your non-AST code... and the $HIBER service is called, and the "wake pending" bit is found to be clear, so back to hibernate state you go. Repeat until the AST procedure decides "Hold, enough!" It calls $WAKE and RETurns. Now when you re-execute $HIBER it finds the "wake pending" bit set (and clears it), so it doesn't go into $HIBER state. It just returns to its caller and you keep executing. Same thing if you'd used $WAITFR in the main program and $SETEF in the AST. Now, a related question to see if you understand the material... :-) Suppose you: (non-AST code) $QIO, specifying an event flag and an AST $WAITFR the event flag (in the AST procedure) clear the event flag RETURN What happens? Well, when the I/O completes, the event flag gets set (and your process is made computable as a result, but we're not done yet), and the IOSB is filled in, and the AST gets delivered. The AST procedure, nasty thing that it is, clears the event flag, then returns to the system. The process is now free to execute non-AST code. Since the stored PC points to the $WAITFR call, and since the AST cleared the event flag, back into the LEF (or CEF) state you go. I hope this helps. Sorry for the length, but better to rattle something off that's too long than decide that I don't have time to do it up right. Perhaps I'll clean this up in the next few weeks and submit it to the Pageswapper (newsletter of the DECUS VAX SIG); any comments or suggestions for improvements will be welcomed. Oh... there *is* an optimization in the system for $WAKE out of the $HIBER state. The $WAKE service checks your process and, if it's in the $HIBER state, takes it out thereof, leaves wake pending clear, and prevents you from re-executing the $HIBER call, in order to save a little bit of time (hiber/wake is supposed to be the fastest inter- process synchronization technique, although in my tests waitfr/setef was not noticeably slower. Note that this optimization doesn't work when $WAKE is called from within an AST executing in the context of the process being awakened, as the process being "awakened" is current, not hibernating, at the time; $WAKE has no way to determine that you "used to be" hibernating.