During normal operation any request from the user for space with MZWORK, MZLIFT et al. is satisfied, after garbage collection if necessary and possible. If however the request cannot be satisfied, the normal course of the program must be broken. To deliver the user from the burden of checking for success after each space request, the garbage collector sends control to the user at the entry-point QNEXT (via ZTELL and the KERNLIB routine QNEXTE), where he can program the recovery of the problem. Normally this will be to skip the current event and to continue with processing the next one.
Other Zebra packages, apart from MZ, and maybe the user himself, have similar problems. Therefore a general trouble control routine ZTELL has been included into Zebra. This is a switching routine with several modes of continuation, one of which is to send control to QNEXT. ZTELL can also be called by the user program, thus:
ID is an integer between 101 and 999, ID's below 100 are reserved for system usage, ID=99 for 'no memory left' from MZGARB. IFLAG is a flag indicating whether the calling code can accept a RETURN from ZTELL: = 0 ZTELL may return; = 1 the calling code is not capable to accept a RETURN; = 2 fatal error, the run must stop.
ZTELL prints a message, sets up a reasonable exit mode as a function of ID and IFLAG into the little labelled common /ZTELLC/ and calls ZTELUS to give the user a chance to modify this mode. On the obligatory return from ZTELUS it takes the selected exit as follows:
COMMON /ZTELLC/ ID, MODE ID is a copy of the first parameter to ZTELL; MODE is the selected exit mode: = 0 RETURN to let the calling routine continue; = 1 CALL QNEXTE to enter QNEXT = 2 CALL ZFATAL = 3 CALL ZEND
The exit mode to QNEXT is enabled only if NQPHAS
in /ZSTATE/
is larger than zero, indicating that the program is
in the normal operation phase.
During the initialisation or the termination phase of the program
transfer is to ZFATAL instead,
to avoid a program crash to be 'recovered' into normal operation.
The pre-loading of MODE
is MODE=IFLAG
for user calls
(ID>100)
;
and for system calls (ID<100)
it is as shown
in the diagnostics chapter for ZTELL.
Here is an example of a ZTELUS
which is happy with the default modes,
except that it wants to go to ZEND for ID=8
:
SUBROUTINE ZTELUS COMMON /ZTELLC/ ID,MODE IF (ID.EQ.8) MODE=3 RETURN ENDThe default subroutine ZTELUS on the library is a do-nothing dummy. The default subroutine QNEXT goes straight to ZFATAL.
On most machines repeated recovery directly to QNEXT causes trouble with the Fortran trace-back and the subroutine stack. For this reason ZEBRA relies on the KERNLIB routine QNEXTE, which implies an organization for event processing as follows:
_________________________ | | Program flow with QNEXT recovery | MAIN program | | | | CALL MZEBRA (0) | | CALL MZSTOR (...) | | | | program | _________ | initialization | | | | | | | | CALL ZPHASE (0) | first entry recover | | | CALL QNEXTE | ------ _________ _| | |_________________________| `----> | | | | | routine | | routine | <---------------------------< | QNEXTE | <-- | ZTELL | | |_________| |_ | ________|________________ | | | | | | | subr QNEXT | | | | | | | | 11 CALL MZWIPE (0) | requests | | | read event | ----------------------------> | Zebra | | IF (end) CALL ZEND | | system | | process event | <---------------------------- | | | output event | normal RETURN's | | | GO TO 11 | | | |_________________________| | | | | | ________|________________ __________ | | | | | | | | | subr ZEND | <--- | subr | <--- | | | | | ZABEND | | _| | | CALL ZPHASE (-3) | |__________| | | | | | | | routine | | program | `---< | ZFATAL | | termination | |_ | |_________________________| |_________|The initialisation part prepares the program to be ready for execution and then calls itself QNEXTE for entry to QNEXT to process the 'next event', being the first event in this case. QNEXT loops internally to process all events.
CALL's from the processing program to the Zebra system are normally satisfied, and control comes back to the user with normal RETURN. Abnormal returns are either via ZFATAL to ZEND, or straight to ZEND, or to QNEXT via QNEXTE.
The Fortran version of QNEXTE
is a simple CALL \Rind{QNEXT
}
followed by RETURN.
If necessary on a given computer,
QNEXTE is a machine-language or a C routine to unwind to itself
the Fortran trace-back and the subroutine stack.
So, if the user wishes at some point to abandon himself
the curent event and to go to the next one,
he should CALL \Rind{QNEXTE
} and not QNEXT.
QNEXT is a user routine to the KERNLIB routine QNEXTE and has thus the usual problem of user routines called from a library routine in that it must be loaded explicitely:
either: compile it together with the other material or: if it resides on a user library it must be INCLUDEd explicitly, for example on the VAX with $ LINK ... MYLIB/INC=QNEXT/LIB ...
This flow-diagram is only an example for the most common case
of actual usage of Zebra.
If one's program is not of the event-processing type
one has to look at QNEXTE/QNEXT from a different angle:
program flow from MAIN has to go to QNEXTE to initialize
for re-entry.
Entry and all re-entries are then to QNEXT,
which has to control the further program flow according
to some flags,
conveniently ID
in /ZTELLC/ and NQPHAS
in /ZSTATE/.
ID is not initialized by MZEBRA,
it is only changed by ZTELL which copies its first parameter to ID
.
This number is an integer in the range 1 to 99 for calls
from the Zebra system.
NQPHAS
is initialized to zero by MZEBRA,
it is then changed only by the user (or the default ZABEND)
either directly or with ZPHASE.