[comp.lang.modula2] Termination code

Pat.Terry@p101.f19.n490.z2.fidonet.org (Pat Terry) (03/26/89)

Jelske Kloppenberg has asked about module termination.  The forthcoming
draft ISO standard is likely to require a procedure somewhere (in a
required library module) to handle this problem.  The syntax will be
something like

   IMPLEMENTATION MODULE Library;
     FROM Finish IMPORT SetTerminate;

     PROCEDURE TermProc;
       BEGIN
         (* code required for termination when this Library is used *)
       END TermProc;

     BEGIN
       (*Initialisation code*);
       SetTerminate(TermProc)
     END Library.

That is, Finish.SetTerminate(T : PROC) will install T in a stack of
procedures that are called automatically, at least when the program ends
normally or executes HALT.  It is still to be decided what will happen in
other situations (eg when exceptions are raised, coroutines "stop" and so
on).

Incidentally, this feature is already present in many MS-DOS implementations.
JPI, StonyBrook, FST, FTL and Logitech all have it (with slight variations
on how it's actually done).  I suspect it's in many other implementations
too, as it's such an obvious thing to want to do!


--  
uucp: ..!{mcvax!uunet,tektronix,sun!nosun}!oresoft!dawggon!2!490!19.101!Pat.Terry
Internet: Pat.Terry@p101.f19.n490.z2.fidonet.org

rjh@cs.purdue.EDU (Bob Hathaway) (03/31/89)

In article <846.242C4BE0@dawggon.fidonet.org> Pat.Terry@p101.f19.n490.z2.fidonet.org (Pat Terry) writes:
>That is, Finish.SetTerminate(T : PROC) will install T in a stack of
>procedures that are called automatically, at least when the program ends
>normally or executes HALT.  It is still to be decided what will happen in
>other situations (eg when exceptions are raised, coroutines "stop" and so
>on).

Why should procedures be called at program termination?  When the program
ends all space and resources should be freed anyway.  I can see invoking
termination blocks at module scope exit for abstract state machines or 
initialization code for runtime elaboration of Adt (opaque variable)
declarations together with termination code for Adt scope exits but I don't
see any need for termination code at program exit.

Anyway since I'm typing this in I'll bring up an issue which has been in the 
back of my mind for a long time.  Modula restricts imports to a hierarchy,
i.e. a tree.  To my knowledge, this precludes recursively defined abstract
data types.  One solution is to nest all abstract data types within a module
and export them from a single interface (definition module) but the 
hierarchical restriction seems unnecessary.  For instance, the following
recursive structure was desired in a recently posted article:

1.   >-------> table:  Create Initialize Lookup Insert Print
     |          /|\    GetDefaultValue
     |         / | \
     |        /  |  \
     |       /   |   \
2.   |    list  tree  hash table ...:  Initialize Insert Lookup Print
     |     /     |     \
3.   ^__ ...    ...    ... :  objects

Tables were to be implemented as abstract data types as were lists, trees,
and hash tables.  Objects were abstract data types also and explicitly 
referenced tables, resulting in a recursive definition.  This requires a 
general graph and cannot be implemented hierarchically.  So for discussion,
is there a better solution than nesting modules of all self referencing Adts?
Are there any reasons for restricting compilation dependency *trees* to 
hierarchies?  I've forgotten to look at the Modula-3 report for Import 
dependencies, but I hope the hierarchical restriction has been revoked.

Bob Hathaway
rjh@purdue.edu

aubrey@val.UUCP (Aubrey McIntosh) (04/01/89)

In article <6354@medusa.cs.purdue.edu> rjh@cs.purdue.edu (Bob Hathaway) writes:

[edited heavily throughout.  Hope I preserved the correct flavor.]

>Anyway since I'm typing this in I'll bring up an issue which has been in the 
>back of my mind for a long time.  Modula restricts imports to a hierarchy,
>i.e. a tree.  To my knowledge, this precludes recursively defined abstract
>data types.  
>
>1.   >-------> table:  Create Initialize Lookup Insert Print
>     |          /|\    GetDefaultValue
>     |         / | \
>     |        /  |  \
>     |       /   |   \
>2.   |    list  tree  hash table ...:  Initialize Insert Lookup Print
>     |     /     |     \
>3.   ^__ ...    ...    ... :  objects
>
>Tables were to be implemented as abstract data types as were lists, trees,
>and hash tables.  Objects were abstract data types also and explicitly 
>referenced tables, resulting in a recursive definition.   
>
>Bob Hathaway
>rjh@purdue.edu

*Although* I have always made my DEF modules hierarchical, I view this
as a style.  In fact, as a benchmark of style, the first Modula-2 code
I wrote was very fuzzy about the distinction between DEF and MOD files,
and I went through considerable agony before it dawned on me that I
could import fairly freely into MOD files.  Please note that I don't
recommend doing that, because of the expense of changing the DEFs,
but it did come in handy on that first project.

Bob's article made me curious enough about this that I picked up a
copy of PIM2, 3rd corr., and broused a bit.  In the Report part, 
"chapter" 14, p169 (Compilation units), I find:

   (discussion about definition modules and implementation modules)
`  ... Opaque export is restricted to pointers ... '

`  As in local modules, the body of an implementation module acts
   as an initialization facility for its local objects.  Before its  
   execution, the imported modules are initialized in the order
   in which they are listed.  If circular references occur among
   modules, their order of initialization is not defined.
'

Additionally, in 4, p 146 (Declarations and Scope rules) I read
rules 1..3 as applicable...
    1.  If an identifier ... is used in another declaration ...
        then D1 must textually preceed D2.
    2.  A type T1 can be used in a declaration of a pointer type T
        which textually precedes the declaration of T1 if both are
        declared in the same block.  This is a relaxation of rule 1.
    3.  If an identifier ... is exported from compilation unit M1 ...
        the scope extends to all those units which import M1.



I admit that I haven't read PIM2 in its entirety since the first
edition, but I didn't find a prohibition in those places I would
first look.

As I type this, I believe that a simple circular reference among
DEF modules, where opaque types are used in subsequent definitions,
doesn't violate anything.  What's also going through my mind, is
that I also expect any implementation which compiles SYM files to
have an implementation restriction on this. :-(  Perhaps I will
go and try some contrived cases.   If I do put much more time into
this, I will use either the Logitech or the C18 compiler to check
things out, and one of the circular imports will have two versions
of the DEF file.

 
-- 
Whatcha' call a boomerang that doesn't come back?      --Lost--
--------------------------------------------------------------
1-(512)-346-5781 (v)                                Using Modula-2.     
Austin, TX 78759   ...!cs.utexas.edu![dell|oakhill|kvue]!val!aubrey

Pat.Terry@p101.f19.n490.z2.fidonet.org (Pat Terry) (04/03/89)

Bob Hathaway has queried the need for providing "termination" code.  He   
writes:

>Why should procedures be called at program termination?  When the program
>ends all space and resources should be freed anyway.

The sort of situation I have in mind is where a library module opens files, 
takes over interrupt vectors and so on.  If indeed the implementation is 
such that the O/S environment is restored to *exactly* what it was when the
program was set running, then there may be no need to provide this facility.
Given that many implementations don't seem to do this (especially those on 
microcomputers, where taking over an interrupt vector and not restoring it
leads to chaos), some way of doing so seems a good idea.  Since detecting   
the sort of activity just mentioned (or implementing it) may be done   
deviously using SYSTEM functions, the need for a programmer to have an easy   
hook into the termination chain is increased.

>I can see invoking termination blocks at module scope exit for abstract 
>state machines or initialization code for runtime elaboration of Adt 
>(opaque variable) declarations together with termination code for Adt 
>scope exits but I don't see any need for termination code at program exit.

I take this to mean you would also like to be able to write, for example

      PROCEDURE x;
        (*inner*) MODULE y;
         . . .
        BEGIN
          Initialise;
          SetTerminate(Cleanup)
        END y;
      
        ...
      END x;

so that whenever x was called "Initialise" was invoked, and whenever x 
returned "CleanUp" was invoked.  I don't think the WG13 effort has 
considered this, and perhaps, for completeness, it should.  On the other  
hand, the problem is solved by placing a call to CleanUp just before the  
end of x.


         


--  
uucp: {mcvax!uunet,tektronix,sun!nosun}!oresoft!dawggon!2!490!19.101!Pat.Terry
Internet: Pat.Terry@p101.f19.n490.z2.fidonet.org

rjh@cs.purdue.EDU (Bob Hathaway) (04/04/89)

In article <904.24372D43@dawggon.fidonet.org> Pat.Terry@p101.f19.n490.z2.fidonet.org (Pat Terry) writes:
>Bob Hathaway has queried the need for providing "termination" code.  He   
>writes:
>>Why should procedures be called at program termination?  When the program
>>ends all space and resources should be freed anyway.
>
>The sort of situation I have in mind is where a library module opens files, 
>takes over interrupt vectors and so on.  If indeed the implementation is 
>such that the O/S environment is restored to *exactly* what it was when the
>program was set running, then there may be no need to provide this facility.
>Given that many implementations don't seem to do this (especially those on 
>microcomputers, where taking over an interrupt vector and not restoring it
>leads to chaos), some way of doing so seems a good idea.  Since detecting   
>the sort of activity just mentioned (or implementing it) may be done   
>deviously using SYSTEM functions, the need for a programmer to have an easy   
>hook into the termination chain is increased.
>

Ok, I saw termination code at module scope exit as a generalization of this
idea, with program termination code being specified as termination code for
the main program module.


>>I can see invoking termination blocks at module scope exit for abstract 
>>state machines or initialization code for runtime elaboration of Adt 
>>(opaque variable) declarations together with termination code for Adt 
>>scope exits but I don't see any need for termination code at program exit.
>
>I take this to mean you would also like to be able to write, for example
>
>      PROCEDURE x;
>        (*inner*) MODULE y;
>         . . .
>        BEGIN
>          Initialise;
>          SetTerminate(Cleanup)
>        END y;
>      
>        ...
>      END x;
>
>so that whenever x was called "Initialise" was invoked, and whenever x 
>returned "CleanUp" was invoked.  I don't think the WG13 effort has 
>considered this, and perhaps, for completeness, it should.  On the other  
>hand, the problem is solved by placing a call to CleanUp just before the  
>end of x.


Yes, this is what I meant.  However, for top-level modules serving as
abstract state machines there is no means for calling CleanUp just before
the end of x, since no x exists, and an explicit termination code section
is therefore required.  As an alternative, the main program termination code
could call a CleanUp procedure for each top-level module but this would have
an analogous drawback to calling top-level module initialization routines
from the main program instead of from each module initialization block, that
of tightly coupling modules.  Also, leaving initialization and termination
sequences to module clients introduces potential insecurities by not 
guaranteeing these sequences will be performed correctly, so module
initialization and termination blocks provide a simple and safe solution.  

As I remember, Pascal-Plus has something like the following syntax:


      MODULE x;
        MODULE y;
         . . .
        BEGIN (* y *)
          (* y. *)Initialise;
	  ***	      (* A termination block begins after *** *)
          (* y. *)Terminate;
        END y;
	. . .
	BEGIN (* x *)
        (* x. *)Initialise;
	***
        (* x. *)Terminate;
      END x;


Bob Hathaway
rjh@purdue.edu