Island: Finalizer does not get called

I have the following program written using Island / Oxygene:

uses rtl;

type
    TTest = private class
        constructor;
        begin
            writeLn ('constructor TTest');
        end;

        method DoIt;
        begin
            writeLn ('TTest.DoIt');
        end;

        finalizer;
        begin
            writeLn ('finalizer TTest');
        end;
    end;

    Program = class
      private
        class method CallTest;
        begin
            var Test := new TTest;
            Test.DoIt;
        end;

      public
        class method Main(args: array of String): Int32;
        begin
            CallTest;

//            gc.GC_gcollect;

        end;
    end;

end.

The “finalizer” only gets called when I implicitly call “gc.GC_gcollect” before the program terminates. Otherwise, the program just terminates without calling the finalizer. As the finalizer’s main purpose is to clean up when the class gets destroyed I find this behaviour extremely irritating. Furthermore, when I execute the same code in .NET the finalizer gets automatically called before the program terminates.

I am using the Island compiler with Elements v9.3.103.2211.

Regards, Olaf

Neither in .NET nor in Island is the finalizer guaranteed to be called at all. Only when memory is low, or the GC happens to run will it call it. If you need deterministic cleanup you should be using “using”/IDisposable.

Is this then different from the .net Finalize() ?

According to MSDN Finalize the GC will call Finalize on any objects requiring it at shutdown (but not if the process is ungracefully terminated). What cannot be said with any certainty is whether or when the Finalizer might be called before shutdown (or some other conditions under which finalisers can be blocked or otherwise not perform as might be expected).

But based on the .net docs I would still expect a finalizer to be called if a process terminates normally and without any of the complications called out in those docs.

https://msdn.microsoft.com/en-us/library/system.object.finalize(v=vs.110).aspx

Yeah I went by this: https://stackoverflow.com/questions/3458177/are-net-finalizers-always-executed

@ck @mh

In the following link it says: “Finalizers are also called when the program exits”

In Dispose pattern, there should be public Dispose, and a protected virtual Dispose, for the “disposing” to work, the Finalizer MUST to kick in at the program exit…

Can we have Finalizer to run at Process exit, on Island? This is critical for my TensorFlow lib to work!

 // Protected implementation of Dispose pattern.
   protected override void Dispose(bool disposing)
   {
      if (disposed)
         return; 
      
      if (disposing) {  // This part of code requires Finalizer to
                         // do the final safe net.
         // Free any other managed objects here.
         //
      }
      
      // Free any unmanaged objects here.
      //
      disposed = true;
      
      // Call the base class implementation.
      base.Dispose(disposing);
   }

I’ll see what I can do re: Boehm cleanup; but do remember that there are no guarantees with a gc. If you DO want to be 100% sure, you can call:

class method ExternalCalls.atexit(func: atexitfunc);

Which is always ran (either on DLL unload or app finish).

@ck Yes understand.

Finalizer is a “safety net” to resort, not to abuse.
But for Dispose pattern, the call to Finalizer is a necessary safety net.

It needs to be called AUTOMATICALLY on Process Exit when destroying the object, if it has NOT been called by GC. The GC itself may or may not call Finalizer, and if it calls, the call will be in-deterministic.

The principle should apply to COM-based memory manager too, that is, Finalizer should be called upon object destroy.

Will “SupressFinalizer” also be supported to suppress Finalizer?

So COM is different; it uses reference counting and will trigger an IDispose on reaching 0.
I have an issue logged to trigger GC shutdown on process exit, but boehm makes no gaurantees. So atexit is a much better candidate for your usecase.

Supressfinalize is in now (IslandRTL)

@ck
Thank you.

You mean Bohem makes no garanttee to run Finalizer at Procees Exit?

You mean Finalizer will be called when IDispose refCnt reach 0? Or Dispose() will be automatically called if the Runtime detects a IDispose interface?

How should I use atexit? A dummy’s sample?

Indeed it does not.

The com infrastructure triggers IDispose when release reaches 0 yes.

class method somefunc;
begin
// Cleanup all global state here.
end;

Easy enough:
atexit(@somefunc);

I am not sure how this can clean up instance-based unmanaged resource (in my case, all native TensorFlow C pointers), none global (unless I manually manage a list of those resources which is clumsy. )

Looks like COM is a better option for my situation?

can you tell me what the exact problem is you are solving? Remember that the GC does work. At which point do you need deterministic cleanup for this? (If so, might the IDisposable pattern be a better option?)

Thank you @ck

I did implement IDisposable pattern. But I still want Finalizer to get called at Object destroy, as long as Finalizer is defined.

The reason is to have a similar .NET behavior on Island platform, so in case my user FORGET to call Dispose(), there is still an ultimate safety net to prevent resource leaking.

Any advice?

yes that’s what the GC is for. However the only time it doesn’t trigger, is when you shut down the app right away. It is guaranteed to run ‘eventually’ normally. (Note that .NET is the same. It usually does but makes no promises)

@ck
But the OP’s code as in below, doesn’t have Finalizer get called?

You mentioned that you logged an issue to run Finalizer at Program Exit, will that garanttee Finalizer will be called at Program Exit? I am confused

uses rtl;

type
TTest = private class
constructor;
begin
writeLn (‘constructor TTest’);
end;

    method DoIt;
    begin
        writeLn ('TTest.DoIt');
    end;

    finalizer;
    begin
        writeLn ('finalizer TTest');
    end;
end;

Program = class
  private
    class method CallTest;
    begin
        var Test := new TTest;
        Test.DoIt;
    end;

  public
    class method Main(args: array of String): Int32;
    begin
        CallTest;

// gc.GC_gcollect;

    end;
end;

end.

What I mean is, the GC will call it “eventually”, unless your application shuts down before it gets to it. I’m forcing a GC now before shutdown which should help in your case. But the best solution is to do implement IDisposable (and let users use that); there’s a supressfinalize now you can use to ensure it doesn’t re-trigger finalize.

1 Like

Yes. That is exactly I am asking for. Will we have that ready soon?
:star_struck:

If you pull IslandRTL now you have it now. Else tomorrow’s build will have it too.

1 Like

@mh @ck
I downloaded the latest 2473 build, but I don’t see SupressFinalize is there?
Or what is the correct way to call it? GC.SupressFinalize?

No idea, this will need to have to wait for Monday, sorry.