HP 3000 Manuals

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