MAINSAIL Language Manual, Chapter 20

previous   next   top   complete contents   complete index   framed top   this page unframed


20. Exceptions

An exception is an unusual or erroneous condition that occurs during a program's execution. When an exception occurs, it causes the execution of a statement called an exception handler (see Section 20.1). A handler may, for some exceptions, repair the error and resume execution at the place where the exception occurred, or it may recover from the error by aborting the execution of one or more handled statements (including PROCEDURE invocations), or it may propagate the exception to another handler. If there is no handler for an exception, the MAINSAIL runtime system reports an error by calling the system PROCEDURE errMsg.

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.

20.2. Handling Exceptions

There are two ways a handler can terminate the handling of an 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.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;

20.6. Aborting PROCEDUREs

Immediately before MAINSAIL aborts the execution of one or more PROCEDUREs, it raises the predefined exception denoted by the identifier $abortProcedureExcpt. This exception differs from other exceptions in the following ways:

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(textFileinputFile;

inputFile := NULLPOINTER;
$HANDLE
    
WHILE open(inputFile,"compile: ",input!prompt!errorOK)
    
DOB compileModule(inputFile); close(inputFileEND
$WITHB
    
IF $exceptionName = $abortProcedureExcpt THEN
        
IF inputFile THEN close(inputFile);
    
$raise END END;

20.7. Exception Naming Conventions

In systems composed of separately developed MODULEs, two distinct exceptions may mistakenly be denoted by the same STRING. To help avoid such conflicts, MAINSAIL supports a convention whereby each STRING denoting an exception is assumed to consist of one or more phrases separated by colons. The following is the general form of such a STRING, in which each si is a phrase:

"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 compilerAbort 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:

MAINSAILAbort program
MAINSAIL compilerAbort 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:

These bugs may be fixed in a future relase of MAINSAIL.

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.

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–2. Stack Diagrams in the Absence of Exceptions and $HANDLE Statements
<diagram>

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–3. Depiction of a Handle Frame
<diagram>

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–4. An Exception Is Caught by the First Ready Handler down the Stack
<diagram>

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
<diagram>

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–6. Frames Invoked from a Handler
<diagram>

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–7. Exception Raised in a Handler
<diagram>

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:

$overheadTooHighExcpt
$almostOutOfMemoryExcpt
$descendantKilledExcpt

Figure 20–8. $abortProcedureExcpt is Raised in All Handle Frames in PROCEDUREs That Are Being Aborted
<diagram>


previous   next   top   complete contents   complete index   framed top   this page unframed

MAINSAIL Language Manual, Chapter 20