[M3devel] idioms for tracking initialization state and raising errors?

Rodney M. Bates rodney_bates at lcwb.coop
Wed Aug 31 19:45:24 CEST 2016

On 07/31/2016 04:07 AM, Jay K wrote:
> Is this a correct idiom?
> If so, I think it is fairly reasonable.

It looks reasonable to me, however ...

> That is:
>   be very willing to "early return"
>   put all cleanup in finally
>   raise errors in finally
> In particular, I don't want to repeat the cleanup.

You might want to reconsider this.  The TRY-FINALLY construct has
some significant behind-the-scenes overhead, and this could be a
somewhat speed-critical operation.  The compiler has to create a
procedure containing the code in the FINALLY block, put calls on
it in all the places where it is statically known it needs to
execute (e.g., RETURNs and falling out of the main TRY block),
and further register it with the runtime system, so, for the
statically unpredictable cases where an exception comes out of
some explicitly coded call in the main block, the RTS can do
a callback on it, before propagating the exception out another

It may be less elegant, but it could be a good bit faster to
just duplicate cleanup code at the 3 relevant places, i.e.,
the two RETURNs and falling out of the main block and eliminate
the TRY-FINALLY overhead.

Moreover, in each one of these places, the cleanup code can be specialized,
eliminating both IF tests and omitting some of the cleanup actions.

For example, at the second RETURN inside the TRY block, it is certain
that lock#NIL, so the first LeaveCriticalSection can be unconditional,
so the 1st cleanup statement can be eliminated altogether, and that
mutex.initialized, so the 3rd cleanup statement can be eliminated

It remains to consider exceptions raised from inside the TRY main block,
and while it looks doubtful to me that these can matter or even happen,
I think more thought is warranted.

Since NewCriticalSection is a Windows call, it probably can't raise a
Modula3 exception.  Or can it?  Could it segfault and have that turn
into a M3 exception?  If that happened, is there any point in any
cleanup, or is it guaranteed to terminate the program?

RegisterFinalCleanup has an empty RAISES set.  But with all this
unsafe code, is that enough to rely on?  Presumably it could segfault
(I see it directly contains some array subscripting), but would
cleanup matter in that case either?

> I don't want to raise before leaving critical sections.
> I don't intend this to raise within a raise, but only
> from the "normal" exit of the finally block.
> I also don't want extra local booleans, i.e. raise: boolean to indicate failure.
> Just the one to indicate success.
> PROCEDURE InitMutex (mutex: Mutex) =
>    VAR
>      lock: PCRITICAL_SECTION := NIL;
>      locked: PCRITICAL_SECTION := NIL;
>    BEGIN
>      IF mutex.initialized THEN RETURN END;
>      TRY
>        lock := NewCriticalSection();
>        IF lock = NIL THEN RETURN; END;
>        EnterCriticalSection(ADR(initLock));
>        locked := ADR(initLock);
>        IF mutex.initialized THEN RETURN END;
>        (* We won the race. *)
>        RTHeapRep.RegisterFinalCleanup (mutex, CleanMutex);
>        mutex.lock := lock;
>        lock := NIL;
>        mutex.initialized := TRUE;
>      FINALLY
>        IF locked # NIL THEN LeaveCriticalSection(locked); END;
>        DelCriticalSection(lock);
>        IF NOT mutex.initialized THEN (* Raise after leaving critical section. *)
>          RuntimeError.Raise (RuntimeError.T.OutOfMemory);
>        END;
>      END;
>    END InitMutex;
> Thank you,
>   - Jay
> _______________________________________________
> M3devel mailing list
> M3devel at elegosoft.com
> https://m3lists.elegosoft.com/mailman/listinfo/m3devel

Rodney Bates
rodney.m.bates at acm.org

More information about the M3devel mailing list