Error Recovery [ HP Pascal/iX Programmer's Guide ] MPE/iX 5.0 Documentation
HP Pascal/iX Programmer's Guide
Error Recovery
The system programming extensions that support error recovery are the
predefined procedure escape, the predefined function escapecode, and the
TRY-RECOVER construct. They are interdependent. A typical TRY-RECOVER
construct has the form:
TRY
statement;
{statement;}
.
.
.
RECOVER
BEGIN {error-handling code}
temp := escapecode; {save escapecode value, which can change}
CASE temp OF {handle error}
{handle expected values of temp here}
OTHERWISE
escape(temp); {cannot handle this error here;
pass to any enclosing TRY-RECOVER construct}
END; {CASE}
END; {error-handling code}
Escape Procedure
The predefined procedure escape is called by your program, a library
routine, or the operating system when a run-time error occurs. If a
TRY-RECOVER construct is active when the system calls escape, the program
executes the statement associated with the RECOVER part (see "TRY-RECOVER
Construct" ). If no TRY-RECOVER construct is active, the program
aborts. A TRY-RECOVER construct is active if the TRY statement has been
executed, but the RECOVER statement has not.
The procedure escape has one parameter, error_code, which is an integer
expression. Escape sets error_code, whose value you can then access with
the predefined function escapecode.
Example
PROGRAM p;
VAR
x : integer;
ecode : integer;
.
.
PROCEDURE PUTJCW; INTRINSIC;
PROCEDURE proc (n : integer);
BEGIN {proc}
{Test for erroneous parameter}
IF NOT (n IN [0..100]) THEN
escape(-755);
.
.
putjcw(jcwname,jcwvalue,error); {system call}
IF error > 0 THEN
escape(error); {system call failed}
.
.
END; {proc}
BEGIN {main program}
TRY
proc(x);
RECOVER
ecode := escapecode; {See note in "Escapecode Function"}
IF ecode = -775 THEN
{Report bad value of m}
ELSE IF ecode = -3550 THEN
{Report failure of system call}
ELSE
halt(ecode);
END. {main program}
Escapecode Function
The predefined function escapecode returns the integer value of
error_code, the parameter of the predefined procedure escape (see "Escape
Procedure" ).
The result of escapecode is undefined if escape was never called, and
after exit from the TRY-RECOVER construct by normal, sequential means
(rather than exit by explicit escape, exit, or goto). If you call
escapecode when its result is undefined, the result is indeterminate and
meaningless. Access escapecode only in the RECOVER part of a TRY-RECOVER
construct.
To see the symbolic names for the escape codes that the Pascal
subsystem returns, list the file PASESC.PUB.SYS (on MPE/iX) or
/usr/include/pasesc.ph (on HP-UX).
TRY-RECOVER Construct
The TRY-RECOVER construct defines a group of statements as error recovery
code.
Syntax
TRY statement [; statement]... RECOVER statement
Parameter
statement Labeled or unlabeled statement.
If an error occurs when the program executes a statement (or any routines
called by the statement in the TRY part):
1. The subsystem in which the error occurred (the program, a library,
or the operating system) calls the predefined procedure escape
with error_code as its parameter. The parameter error_code is an
integer expression whose value represents the error.
2. The procedure escape sets error_code and saves it.
3. The program's run-time environment reverts to that of the program
unit (main program, procedure, or function) that contains the
TRY-RECOVER construct.
4. The program executes the statement of the RECOVER part (skipping
any statements between the statement where the error occurred and
the RECOVER's statement).
If no statement causes an error, the program skips the RECOVER's
statement and executes the statement that follows the TRY-RECOVER
construct.
Example 1
PROGRAM prog (input,output);
$STANDARD_LEVEL 'HP_MODCAL'$
VAR
i,j,k,l : integer;
PROCEDURE proc;
BEGIN
i := 0;
j := 0;
k := 0;
END;
BEGIN
TRY
read(i); {Error here transfers control to proc.}
read(j); {Executed only if no error occurs for read(i).
Error here transfers control to proc.}
read(k); {Executed only if no error occurs for read(i) or read(j).
Error here transfers control to proc.}
RECOVER
proc; {Executed only if an error occurs
for read(i), read(j), or read(k).}
l := i+j+k; {Always executed.}
END.
If the RECOVER's statement is empty, the person who is running the
program will not know when the TRY-RECOVER construct has handled an
error.
If an error occurs when the program executes the RECOVER's statement, the
program aborts--unless the TRY-RECOVER construct is within another
TRY-RECOVER construct. In that case, the program executes the RECOVER
statement of the outer TRY-RECOVER construct.
Example 2
PROGRAM prog (input,output);
$STANDARD_LEVEL 'HP_MODCAL'$
VAR
i,j : integer;
iok : Boolean;
PROCEDURE newj;
BEGIN
writeln('That value is illegal.');
prompt('Please enter an integer for j:');
read(j);
END;
PROCEDURE newij;
BEGIN
IF NOT iok THEN i := 0 ELSE newj;
END;
BEGIN {prog}
iok := FALSE;
TRY
prompt('Enter an integer for i:');
read(i); {An error here transfers control to newij}
iok := TRUE; {Not executed if read(i) causes an error}
TRY
read(j); {An error here transfers control to newj}
RECOVER
newj; {An error here transfers control to newij}
RECOVER
newij; {An error here aborts the program}
END. {prog}
The following example illustrates how nested TRY-RECOVER statements
divide the responsibility of error recovery.
Example 3
The diagram below shows when, in time, the TRY-RECOVER statements labeled
A, B1, B2, and C in the preceding program are active. When more than one
TRY-RECOVER statement is active, the innermost one takes precedence.
The RECOVER's statement can use the predefined function escapecode to
determine the error that occurred and act accordingly.
Example 4
PROGRAM system;
IMPORT
system_escapecodes; {see note following example}
PROCEDURE support;
BEGIN
IF error THEN escape(88);
END;
PROCEDURE userprogram;
BEGIN
support;
END;
BEGIN {system}
TRY userprogram
RECOVER
CASE escapecode OF
minuser..maxuser : writeln('Software detected errors');
range : writeln('Value range error');
stackoverflow : writeln('Stack overflow');
ioverflow : writeln('Integer overflow');
idivbyzero : writeln('Integer divide by zero');
roverflow : writeln('Real overflow');
runderflow : writeln('Real underflow');
rdivbyzero : writeln('Real divide by zero');
nilpointer : writeln('Nil pointer reference');
casebounds : writeln('Case expression bounds error');
stroverflow : writeln('String overflow');
filerror : writeln('File I/O error');
OTHERWISE
writeln('Unrecognized error');
END; {CASE}
END. {system}
NOTE This is only an example. The operating system on which HP Pascal
runs does not use the constants that represent error codes in the
example above (ioverflow, roverflow, and so on).
A program can access error_code only by calling the predefined function
escapecode.
TRY-RECOVER and Optimization
If the OPTIMIZE compiler option is used with the TRY-RECOVER construct,
the following information explains what will or will not work at
different levels.
* If an ESCAPE is done in the TRY block, or in any procedure called
from within the TRY block, all values on the left side of an
assignment statement, appearing before an ESCAPE or a procedure
call, are stored.
* If a trap occurs instead of an ESCAPE, the above statement is not
true.
Example
The following example uses the local variable flag to indicate how far
the program gets before an error. It is used to undo or unlock a
resource.
$standard_level 'ext_modcal'$
$ovflcheck off$
program dick;
type iptr=^integer;
procedure lock; external;
procedure plock $alias 'lock'$; begin end;
procedure proc(j:integer;p:iptr);
var flag: {$VOLATILE$} boolean;
i:integer;
begin
flag:=false;
try
lock;
flag:=true;
i:=maxint;
i:=i + j + p^;
if j < 0 then escape(i);
recover
begin
if not flag then halt(1); { should not halt }
end;
end;
begin
proc(1,nil);
end.
This program does not work correctly with optimization because the store
to the variable flag is done after the trap. To run the program
correctly, use $VOLATILE$ so that flag is stored before the trap occurs.
See Chapter 12 for more information on the optimizer.
Assert Procedure
The predefined procedure assert allows your program to test assumptions,
specify invariant conditions, and check data structure integrity.
Syntax
assert (b, i [, p])
Parameters
b A Boolean expression that assert evaluates. If its value is
true, the program executes the statement following the call to
assert. If its value is false, the program's action depends upon
whether p is specified and whether the ASSERT_HALT compiler
option is OFF or ON (see Figure 11-1 ).
If the compiler can determine that b is a constant expression
whose value is true, then it does not generate code for the call
to assert.
i An integer expression. If the value of b is false and p is
specified, procedure p is called with i as the actual value
parameter. If b is false and p is not specified, the system
issues a run-time error message that includes the value of i.
A call to the predefined function statement_number is a useful
integer expression for i. It returns the statement number (as
shown on the compiler listing) for the statement from which it is
called (in this case, the call to assert).
p The name of a procedure whose heading has the syntax
PROCEDURE p (parameter_name : integer);
If the value of b is false and p is specified, the system
executes the call p(i).
Figure 11-1 illustrates how the predefined procedure assert works.
Figure 11-1. How the Predefined Procedure Assert Works
The default for the ASSERT_HALT compiler option is OFF (see the HP
Pascal/iX Reference Manual or HP Pascal/HP-UX Reference Manual for more
information).
Example
PROCEDURE my_assert (value : integer);
BEGIN
writeln('my_assert #', value);
END;
PROCEDURE x (p : ptrtype; n : integer);
BEGIN
assert(p <> nil, 80101, my_assert);
assert(n >= 0, 80102);
END;
MPE/iX 5.0 Documentation