TCastleView.Stop called from FINALIZATION section

I had a really weird SIGSEGV in my project and I wasn’t able to understand why as I were cleaning and nulling everything correctly. In a long GDB session I’ve found that view’s Stop method is called from a finalization section! That conflicts my cleaning because I’m using a global object I create in initalization and free in finalization that is initialized in the view’s Start method and finalized in the view’s Stop method.

I’m aware my design isn’t the best (it’s an ugly hack just for testing) but anyway I think that view’s Stop method shouldn’t be called after program termination. Why isn’t it called from TApplication.Terminate? Why is it delayed to the finalization section?

This is like TForm’s event OnDestroy: It is guaranteed that it’s called before the program finished.

On the contrary, you can test. Running the attached example shows that after the main begin .. end. ends, the form gets OnHide and OnDestroy events.

terminate_finalization.zip (140.7 KB)

What happens and why:

Application.Terminate merely sets a flag to terminate as soon as possible. This is true both for Application singleton used by LCL (from LCL Forms unit) and for Application singleton from CastleWindow unit.

When using CastleWindow: the finalization section of CastleWindow actually stops any view (old name: “UI state”) we were in, then frees window and application.

When using LCL it is similar, the finalization section of Forms does FreeThenNil(Application).

The above behavior is standard. LCL applications (unrelated to CGE) also behave lke this.

Why?

  • We cannot stop and free things right when Application.Terminate is called, as it’s often called from callbacks/methods of a form/view. Freeing everything from Application.Terminate would require extra-careful usage of Application.Terminate.

  • The main program, of both LCL application and CGE application, doesn’t have any other explicit call to “free everything now”. LCL depends on finalization of Forms. CGE depends on finalization of CastleWindow.

Solutions in your case:

  1. You can make your Stop method “resilient” to the fact that it’s called from finalization. If it accesses something else, that may be already freed at this point, then check is it nil before.

  2. Alternatively: you can get the behavior you want by modifying the auto-generated CGE program file, xxx_standalone.dpr. By default it’s really trivial:

    begin
      Application.MainWindow.OpenAndRun;
    end.
    

    It is OK to edit it, if you know what you’re doing, you can edit it. (By “know what you’re doing” I mean: remember that running "“Code → Regenerate Program” from CGE will overwrite it, so you will have to reapply your change; but we don’t do this regeneration automatically ever, now).

    Just set the current view to nil , to make sure it is stopped at this point:

    begin
      Application.MainWindow.OpenAndRun;
      Application.MainWindow.Container.View := nil;
    end.
    
  3. Alternatively: you can add the Application.MainWindow.Container.View := nil; call before you free your global object. I understand you have a line like this right now:

    finalization
      FreeAndNil(SomeGlobalObjectUsedByViewsStop);
    end;
    

    So just change it to this:

    finalization
       // make sure all views are stopped now, 
      // so that we don't call any TXxxView.Stop after SomeGlobalObjectUsedByViewsStop is freed
      Application.MainWindow.Container.View := nil;
      FreeAndNil(SomeGlobalObjectUsedByViewsStop);
    end;
    
1 Like

Then I misunderstood it.

I take note of the ways to fix it. I think 3. will be my favourite.

1 Like