previous next top complete contents complete index framed top this page unframed
Table 20–1 lists system PROCEDUREs, variables, and macros for dealing with exceptions.
Table 20–1. System PROCEDUREs, Variables, and Macros for Exceptions
| Identifier | Function |
|---|---|
| $raise | Raise an exception |
| $raiseReturn | Return from $raise, if possible |
| $exceptionName | Name of current exception |
| $exceptionPointerArg | POINTER argument to $raise for current exception |
| $exceptionStringArg1 | First STRING argument to $raise for current exception |
| $exceptionStringArg2 | Second STRING argument to $raise for current exception |
| $exceptionCoroutine | Raiser coroutine of current exception |
| $exceptionBits | Bits describing current exception |
| $newException | New name for an exception |
| errMsg | Write an error message and/or raise an exception |
| $registerException | Register an exception so that it can be raised from errMsg |
| $deregisterException | Undo $registerException |
In order to be an exception, a condition must have been caused by the operations being performed when the condition occurred. Asynchronous interrupts and events occurring in other tasks or processes are not considered to be exceptions, and are not dealt with directly by the MAINSAIL exception mechanism.
Exceptions are divided into two categories: those predefined by MAINSAIL (see Section 20.8) and those known only to user programs. User exceptions must be explicitly caused by the user program by means of the system PROCEDURE $raise; predefined exceptions are caused by the MAINSAIL runtime system.
Exceptions are denoted by STRINGs. Section 20.7 describes conventions supported by MAINSAIL to help ensure that exception names are unique.
A program can “register” its exceptions with MAINSAIL using the system PROCEDURE $registerException. The system PROCEDURE errMsg allows a user, in response to the Error response: prompt, to obtain a list of the exceptions that have been registered and to raise any registered exception.
By default, the system PROCEDURE errMsg raises a predefined exception before writing its message to logFile. This may cause the execution of a handler that recovers from the exception by aborting the call to errMsg, in which case the message is not written to logFile. If no such handler is found, the message is written as if no exception had been raised.
20.1. $HANDLE Statement
A $HANDLE statement associates
an exception handler with a statement in
the program (called the handled statement) and executes the handled
statement. If an exception occurs during the execution of the handled
statement, that statement's execution is interrupted and the handler is
executed. If no exception occurs, the handler is ignored.
The general form of the $HANDLE statement is:
$HANDLE s1 $WITH s2
where s1 and s2 are statements. The statement s1 is the handled statement and s2 is the handler.
MAINSAIL provides the abbreviations $HANDLEB for $HANDLE BEGIN and $WITHB for $WITH BEGIN.
When a $HANDLE statement is executed, its handled statement s1 is initiated. If an exception occurs during s1's execution (which may involve several levels of PROCEDURE calls), and the exception has not been handled by another $HANDLE statement initiated during s1's execution, then s1's execution is suspended and the exception handler s2 is executed. Otherwise, s2 is ignored.
A handler can either recover from an exception
(unless the exception disallows it)
or it can propagate
the exception to another exception handler. In the first case, the
handler is said to have handled the exception.
When a handler terminates the execution of its
$HANDLE statement, the
handled statement s1, whose execution
was suspended by the occurrence of
the exception,
is aborted, along with all other statements initiated as a
result of s1's
execution (and all PROCEDUREs thus invoked).
When a PROCEDURE is aborted in this manner,
if it contains any active
handled statements, MAINSAIL raises the predefined exception
$abortProcedureExcpt and executes the handlers associated with the
handled statements. This gives each procedure a chance to do any
“cleaning up” that might be necessary before it is aborted; see
Section 20.6.
20.2. Handling Exceptions
There are two ways a handler can
terminate the handling of an exception:
20.3. Propagating Exceptions
If a handler is unable to handle an exception, it can propagate the
exception to the next handler by calling the
system PROCEDURE $raise with no arguments. The next handler
is the handler associated with the most recently executed
$HANDLE statement whose s1 part
began execution before the current $HANDLE statement and which
has not yet completed. If there is no next handler within
the user program, the MAINSAIL runtime system reports an error by
calling the system PROCEDURE errMsg.
20.4. Information about the Current Exception
A handler can obtain the name of the current exception by calling
$exceptionName. The
current exception is the most recently raised exception
for which a handler is still
executing. Other information about the exception (e.g., whether or not
the handler can resume execution at the place where the exception
occurred) can be obtained by calling
$exceptionBits;
see Section 34.8.
When an exception is caused by means of the system
PROCEDURE $raise (see Section 46.1),
more information about the exception can be passed by means of the
$raise parameters exceptionStringArg1,
exceptionStringArg2, and
exceptionPointerArg. A handler can access
the values passed for these parameters as
$exceptionStringArg1,
$exceptionStringArg2, and
$exceptionPointerArg, respectively.
20.5. Nested Exceptions
Exceptions can be nested. If an exception occurs during a handler's
execution, the handler's execution is suspended and a handler for the
new exception is searched for and initiated, as described above. If the
new handler resumes execution at the place where the new exception
occurred, the previous exception is restored to being the current
exception and the execution of its handler continues. If the new
handler aborts the execution of its $HANDLE statement,
the execution of the previous handler's $HANDLE statement is also
aborted, as in:
$HANDLE
$HANDLE $raise("first exception")
$WITH $raise("second exception")
$WITH; # Abort both $HANDLE statements
20.5.1. Exceptions Raised While an Exception with $cannotFallOut
Set Is Being Handled
If a handler that cannot fall out
(because it is handling an exception with the $cannotFallOut bit
set in $exceptionBits)
raises an exception of its own, the $exceptionBits
bit $cannotFallOut is set for the second
exception when invoking any handler that, if it were to fall out, would
abort the first exception.
For example, in the following $HANDLE statement, $cannotFallOut would be set when the second exception's handler is invoked for exception "y", even though the call to $raise that raised that exception does not explicitly set it:
$HANDLE
$HANDLE $raise("x","","",NULLPOINTER,$cannotFallOut)
$WITHB $raise("y"); $raiseReturn END
$WITHB
write(logFile,"$cannotFallOut set: ",
$exceptionBits TST $cannotFallOut,eol); # Writes TRUE
$raiseReturn END;
Note that in the following example, $cannotFallOut would not be set when the handler for exception "b" is raised, since falling out of this handler would not abort the one for exception "a":
$HANDLE $raise("a","","",NULLPOINTER,$cannotFallOut)
$WITHB
$HANDLE $raise("b")
$WITH write(logFile,"$cannotFallOut set: ",
$exceptionBits TST $cannotFallOut,eol); # Writes FALSE
$raiseReturn END;
If a PROCEDURE must perform certain actions to “clean up” after itself before it is aborted, such as closing files, disposing of dynamic objects, or releasing scan bits, it should contain a handler that catches the exception denoted by $abortProcedureExcpt, does the required cleaning up, and propagates the exception to the next PROCEDURE being aborted; for example:
PROCEDURE compiler;
BEGIN
POINTER(textFile) inputFile;
inputFile := NULLPOINTER;
$HANDLE
WHILE open(inputFile,"compile: ",input!prompt!errorOK)
DOB compileModule(inputFile); close(inputFile) END
$WITHB
IF $exceptionName = $abortProcedureExcpt THEN
IF inputFile THEN close(inputFile);
$raise END END;
"s1:s2: ... :sn"
According to this convention, the last phrase describes the actual exception and the first phrases serve to distinguish the exception from other exceptions having the same last phrase. Typically, the first phrases would contain the name of the company owning the rights to the system in which the exception is embedded, or the name of a product of the company, as in:
"MAINSAIL compiler: Abort compilation"
All predefined exceptions begin with the substring "MAINSAIL". Programmers should avoid choosing exception names that begin with that substring.
The system PROCEDURE $newException
(see Section 42.3)
returns a STRING consisting of a
unique decimal integer followed by a colon.
To avoid conflicts with names created from STRINGs returned by
$newException, users should avoid choosing an exception name that
begins
with a decimal integer and a colon unless that prefix was obtained by
calling $newException.
20.8. Predefined Exceptions
Predefined exceptions are exceptions known to MAINSAIL. A
predefined exception may occur as a result of an operation other than a
call to the system PROCEDURE $raise. The
STRINGs denoting predefined exceptions
can be referred to by means of
identifiers defined by MAINSAIL.
The predefined exceptions and the identifiers by which they are known are given in Appendix C. The conditions under which these exceptions can be raised are described at the relevant points in this manual.
XIDAK reserves the right to create new predefined exceptions. MAINSAIL programmers should be aware that system predefined exceptions, or undocumented exceptions used internally by the MAINSAIL runtime system, may be raised by many MAINSAIL constructs. Since exception handlers may not recognize the names of all the exceptions they handle, they must therefore be written to deal with unknown exceptions by examining $exceptionBits and taking appropriate action.
20.8.1. $abortProgramExcpt and $systemExcpt
When the exception denoted by $abortProgramExcpt
is raised and no other
handler handles it, execution of the current program is aborted and
control passes to the MAINSAIL component that initiated it, e.g., the
MAINSAIL executive, MAINEDIT, MAINDEBUG, or MAINPM.
The exception denoted by $systemExcpt includes all errors not covered by the other predefined exceptions, in which case the specific error is described by the STRINGs returned by $exceptionStringArg1 and $exceptionStringArg2. $systemExcpt is usually raised by calling errMsg.
The exceptions denoted by $abortProgramExcpt and $systemExcpt are the only predefined exceptions that may be handled by resuming execution at the place where the exception occurred.
The exception denoted by $abortProgramExcpt is the only predefined exception that is registered.
20.9. Informational Exceptions
Exceptions are usually raised when some condition occurs that
requires action on the part of a PROCEDURE invocation ancestral
to the current one.
It is usually expected that an ancestral PROCEDURE will either
abort the raising PROCEDURE (by falling out of a handler) or
will repair the condition that provoked the exception and return
to the place of the exception with $raiseReturn.
Some exceptions, however, do not necessarily require any action, but merely inform the ancestral PROCEDUREs of something that has occurred. The system exception $descendantKilledExcpt falls into this category. System exceptions that are informational can be distinguished because they have both the $cannotReturn and $cannotFallOut bits set in $exceptionBits (although $abortProcedureExcpt also has both these bits set, $abortProcedureExcpt is not considered an informational exception).
General-purpose error handlers should be written so as to handle
informational exceptions properly, i.e., to call $raise with
no parameters to propagate the exception.
20.10. errMsg Response Abbreviations
It is possible for a user program to define new responses to the
errMsg
Error response: prompt by calling the system PROCEDURE
$registerException.
There are some rules describing the permissible formats of such
responses.
In a response to the Error response: prompt, distinctions between upper- and lowercase letters are ignored when comparing a specified response to one of the expected responses.
Responses may be abbreviated, but an abbreviation must be unambiguous. A response's text is divided into phrases separated by colons. By convention, the last phrase describes the action to be taken as a result of the response. Any preceding phrases serve to distinguish the response from other responses having the same last phrase; for example, the response "MAINSAIL: Abort program" consists of the phrases "MAINSAIL" and "Abort program".
Responses may be abbreviated by omitting leftmost phrases (and the colons that follow them). The phrases present may be abbreviated by omitting trailing words and, within the words present, by omitting trailing characters. A blank or tab between words in a specified response is equivalent to any number of blanks or tabs in an expected response.
If a specified response is an abbreviation for more than one expected response, then:
For the following set of expected responses:
MAINSAIL: Abort program
MAINSAIL compiler: Abort compilation
Abort
the following responses are valid and invalid, as described:
| a p | Denotes MAINSAIL: Abort program. |
| m c: | Denotes MAINSAIL compiler: Abort compilation. |
| m:a | Ambiguous abbreviation. It is an abbreviation for both MAINSAIL: Abort program and MAINSAIL compiler: Abort compilation. |
| abort | Denotes Abort. It is also an abbreviation for each of the other expected responses, but is assumed to denote Abort since abort and Abort match exactly. |
| m | Invalid abbreviation; there is no response whose rightmost phrase begins with m or M. |
20.10.1. Sample Use of Registered Exceptions
Suppose a program is controlling the execution of several
processes by some undisclosed means and wants to allow a user to kill
any of them from errMsg by typing kill p, where
p is the name of the
process, or to kill the current process by typing just
kill. Then it
could register the exception KILL as follows:
$registerException
("KILL","Kill process p",useKeyWord,"p");
$registerException
("KILL","Kill current process",$doNotMatch);
(parameters to $registerException are described in Section 46.10). If errMsg is called and the user types ?, errMsg displays:
KILL p Kill process p
KILL Kill current process
There are two instances of the exception KILL, but the second one (the one registered with $doNotMatch set) is used only by errMsg when it displays the registered exceptions.
If the user types k main<eol>, k matches the exception KILL and main is set aside as the response's argument. errMsg raises the exception KILL and passes main as the $raise argument exceptionStringArg1. The program is expected to have a handler for the exception KILL and gets the name of the process to be killed by calling $exceptionStringArg1. If the user types just k, the empty STRING is passed as the $raise argument exceptionStringArg1.
A call to $registerException might look like:
$registerException("DEBUG","to enter the debugger")
In order for an exception to be raised from errMsg, it must have been registered; exceptions that are not to be raised from errMsg need not be registered.
The system PROCEDURE $deregisterException
(see Section 33.18)
causes an exception to be no
longer registered.
20.11. Known Exception Handling Problems
The exception mechanism contains the following known bugs:
PROCEDURE p;
$HANDLE # P0
$raise("X")
$WITH
$HANDLE # P1
$raise("Y")
$WITH
$raise;
PROCEDURE q;
$HANDLE # Q0
p
$WITH
; # fall out of Y and X
the $HANDLE statement P1 does not see $abortProcedureExcpt when Q0 falls out, although P0 does. Both should see $abortProcedureExcpt because both are aborted by Q0 falling out.
These bugs may be fixed in a future relase of MAINSAIL.
Figure 20–2 shows a picture of the
PROCEDURE stack
in the absence of exceptions and exception handlers.
In this simple case, the only things on the stack are
PROCEDURE frames
(one for each PROCEDURE invocation).
Figure 20–3 shows how a handle frame is depicted.
A handle frame is conceptually pushed onto the stack whenever
a $HANDLE statement is entered.
The handler part of the handle frame is then marked as “ready”
(meaning ready to handle any exception that occurs in the stack
above it).
A handle frame is popped off when one of the following
things happens:
(Handle frames are a conceptual tool only.
In the actual implementation of $HANDLE statements,
nothing is pushed on the stack;
the implementation of $HANDLE statements is more efficient
than the diagrams imply.)
Figure 20–4 shows how an exception is raised
and caught by the first ready exception handler below it on the
stack.
Once a handler is handling an exception, its status changes
from “ready” to “busy”,
meaning that it cannot handle any additional ordinary exceptions.
All the PROCEDURE frames and handle frames above the handler
are grayed out in the diagrams, meaning that the execution of the
corresponding code is suspended.
Figure 20–5 shows how an exception is propagated to
the next ready handler down the stack when $raise is called with
no arguments.
Figure 20–5. An Exception is Propagated by a Call
to $raise with No Arguments
Figure 20–6 shows how PROCEDURE or handle
frames invoked
from an exception handler are depicted:
they are placed on the stack above the frames suspended by exception
handling with a large arrow pointing from the exception handler to
the frame it invoked.
The handler from which the arrow points is the one
to which control will return if the new frames complete execution
normally.
An exception can, of course, occur within the PROCEDURE or handle
frame.
Figure 20–7
shows what happens when an exception
is raised in an exception handler:
the exception is propagated to the next ready handler on the stack below
the handler in which it was raised.
The handler in which the exception was raised is busy, and therefore
cannot handle the exception.
This figure also shows the use of $raiseReturn, which “undoes” an
exception:
it returns control to the place where the exception was raised,
unsuspends any code suspended because of the exception,
and makes ready any handlers that were made busy by the exception.
Figure 20–8 shows how the special exception
$abortProcedureExcpt is
raised in all handlers on the stack, even those that are currently
busy.
Other system exceptions with this characteristic are:
Figure 20–8. $abortProcedureExcpt is Raised
in All Handle Frames in PROCEDUREs That Are Being Aborted
20.12. Visualizing Exceptions
Understanding the mechanics of raising and handling exceptions can
be difficult using just a verbal description.
This section provides some diagrams of the PROCEDURE stack
to help you understand how
exceptions work.

$overheadTooHighExcpt
$almostOutOfMemoryExcpt
$descendantKilledExcpt

MAINSAIL Language Manual, Chapter 20