10.11 FINALLY: Termination, Detection and Cleanup

In section 10.8.2 it was pointed out that immediate normal program termination could be triggered manually through a RETURN from the body of the program module. The transfer of control was described as being to a point just after the last statement in the block body of the program module, rather than simply "to the END of the module." In fact, there can be additional code between the two in what is called a FINALLY clause, and this code can be used for the purpose of cleanup after the program, (such as the closing of files). Here is a sketch:

MODULE Program;
(* imports *)
(* declarations *)
BEGIN  (* the body *)
  (* statement sequence *)
FINALLY
  (* statement sequence *)
END Program.

NOTES: 1. The statement sequence in the FINALLY clause always executes when a program is terminated, regardless of the reason for this termination.

2. Every module may have a FINALLY clause.

If both the program module and one or more library modules that it imports have FINALLY clauses, then when the program terminates, the FINALLY clauses are executed in the reverse order that the module bodies were run (initialized) in the first place. This order will of course depend on the order of the imports in the program module and in the modules they in turn import and can only be known to the programmer of all the modules in the suite.

The orderly execution of the finalization clauses of the various modules constituting a program is termed program finalization.

NOTE: If a program terminates because of a HALT or some error condition encountered by a modules, only the modules that have at least begun to run their bodies will be finalized. The FINALLY clause of any module whose (initialization) body has not yet been commenced will not be invoked.

Whatever cleanup has to be done by a module should therefore be related only to what that module itself has done. If a program module, for instance, opens files, that module ought at some point close those files. In some cases, this might best be done in a FINALLY clause, depending on the logic of the rest of the code. Here is a simple example, based on the chapter eight program ReadNAdd:

MODULE FinallyDemo;

(* Written by R.J. Sutcliffe *)
(* to illustrate the use of Finalization *)
(* using ISO standard Modula-2 *)
(* last revision 1994 04 22 *)

(* This module, like ReadNAdd,  reads a series of Integers from the disk file called "numbers". It sums them and prints out the sum. *)

FROM StreamFile IMPORT
  ChanId, Open, read, Close, OpenResults;
FROM TextIO IMPORT
  WriteLn, WriteString, SkipLine, ReadChar;
FROM IOResult IMPORT
  ReadResult, ReadResults;
FROM WholeIO IMPORT 
  ReadInt, WriteInt, WriteCard;
FROM StdChans IMPORT
  StdInChan, StdOutChan;

VAR
  infile, stdOut, stdIn : ChanId;
  number, sum : INTEGER;
  res : OpenResults;
  ch : CHAR;

BEGIN
  stdOut := StdOutChan(); (* to force screen output *)
  stdIn := StdInChan(); (* to force keyboard input *)
  
  Open (infile, "numbers", read, res);
  IF res = opened
    THEN
      sum := 0; (* initialize sum *)
      REPEAT  (* Collect the numbers from the file *)
        ReadInt (infile, number); 
        IF ReadResult (infile) = allRight
          THEN
            SkipLine (infile);
            INC (sum, number);    (* ok, so add to sum *)
          END;  (* if *)
      UNTIL ReadResult (infile) # allRight;
 
     WriteString (stdOut, "The sum of the numbers is ");
     WriteInt (stdOut, sum, 6);
     WriteLn (stdOut);
    ELSE
      RETURN
    END; (* if *)
    WriteString (stdOut,"Processing concluded normally");
    WriteLn (stdOut);
   
FINALLY
  IF res # opened
    THEN
      WriteString (stdOut,"Sorry, couldn't open the file");
      WriteLn (stdOut);
      WriteString (stdOut, " because of error number ");
      WriteCard (stdOut, ORD (res), 1);
      WriteLn (stdOut);
    ELSE
      Close (infile);
    END;
  WriteString (stdOut, "type a character to continue==> ");
  ReadChar (stdIn, ch);

END FinallyDemo.

HALT interacts with finalization in the following ways:

1. HALT is a termination event, and like a simple RETURN in a program module will initiate the finalization of the modules whose initialization has begun.
2. If HALT is called from a dynamic module (i.e. a module inside a procedure) any FINALLY clause in that dynamic module is not executed.
3. If HALT is called in a FINALLY clause, that particular clause is immediately concluded (transfer to a point just beyond its last statement) and the finalization of the program suite carries on with the next scheduled FINALLY clause in its proper order.

Because HALT and RETURN are not the same kind of termination event, it may be necessary to distinguish between them in code included in a FINALLY clause. This is done as in the following sketch outline:

MODULE DistinguishHalt;
(* By R. Sutcliffe
    To demonstrate HALT/FINALLY interaction
    revised 1994 04 22 *)

FROM TERMINATION IMPORT
  HasHalted;
IMPORT STextIO;

VAR
  ch : CHAR;
BEGIN
   STextIO.WriteString ("Halt? Y/N => ");
   STextIO.ReadChar (ch);
   STextIO.SkipLine;
   IF CAP (ch) = "Y"
     THEN
       HALT
     END;
     
FINALLY
  IF NOT HasHalted () 
    THEN
      STextIO.WriteString ("Not ");
    END;
    STextIO.WriteString ("Halted");
    STextIO.WriteLn;
    STextIO.WriteString ("Press a return to continue");
    STextIO.SkipLine;

END DistinguishHalt.

The module TERMINATION is a required system module in the ISO libraries and provides services to distinguish termination events.


Contents