www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Odd behavior found in GC when terminating application

reply solidstate1991 <laszloszeremi outlook.com> writes:
I usually write my applications' main logic into one or more 
classes unless it can be avoided, and thus I often use ctors and 
dtors for initializing and cleaning up things. One of them (my 
engine) uses dtors for cleaning up things from a non-GC external 
library (SDL2, using the Derelict loader).

If I close one of the apps I've written with them, it generates 
an access violation error within the SDL2 library. This doesn't 
happen with others, probably depending on what kind of resources 
I use. However, I just found a fix to the issue: if I manually 
call the dtor of the main logic after it exited from the main 
loop and remove the call for SDL2_QUIT(), there's no issues with 
SDL2, even the time it takes to quit it back to normal instead of 
the almost instant crash upon trying to close the application.
Jan 05 2018
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Friday, 5 January 2018 at 14:23:30 UTC, solidstate1991 wrote:
 I usually write my applications' main logic into one or more 
 classes unless it can be avoided, and thus I often use ctors 
 and dtors for initializing and cleaning up things.
The problem with this is when the GC runs, it considers everything dead to have died at the same time - parent and child objects alike - and will run the destructors in whatever order is most convenient for the GC implementation. That means the SDL quit function may be called BEFORE other cleanup, leading to your access violation. Indeed, what you want to do is explicitly call things that need to be done in order... in order. Can't rely on the GC to call functions in any particular way.
Jan 05 2018
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Friday, January 05, 2018 14:29:41 Adam D. Ruppe via Digitalmars-d wrote:
 On Friday, 5 January 2018 at 14:23:30 UTC, solidstate1991 wrote:
 I usually write my applications' main logic into one or more
 classes unless it can be avoided, and thus I often use ctors
 and dtors for initializing and cleaning up things.
The problem with this is when the GC runs, it considers everything dead to have died at the same time - parent and child objects alike - and will run the destructors in whatever order is most convenient for the GC implementation. That means the SDL quit function may be called BEFORE other cleanup, leading to your access violation. Indeed, what you want to do is explicitly call things that need to be done in order... in order. Can't rely on the GC to call functions in any particular way.
Either that or use structs on the stack instead of classes on the heap so that you're actually using destructors instead of finalizers. - Jonathan M Davis
Jan 05 2018
parent reply 12345swordy <alexanderheistermann gmail.com> writes:
On Friday, 5 January 2018 at 14:35:44 UTC, Jonathan M Davis wrote:
 On Friday, January 05, 2018 14:29:41 Adam D. Ruppe via 
 Digitalmars-d wrote:
 [...]
Either that or use structs on the stack instead of classes on the heap so that you're actually using destructors instead of finalizers. - Jonathan M Davis
There been discussions regarding spiting up the destruction and finalizers.
Jan 05 2018
next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Jan 05, 2018 at 03:26:03PM +0000, 12345swordy via Digitalmars-d wrote:
 On Friday, 5 January 2018 at 14:35:44 UTC, Jonathan M Davis wrote:
 On Friday, January 05, 2018 14:29:41 Adam D. Ruppe via Digitalmars-d
 wrote:
 [...]
Either that or use structs on the stack instead of classes on the heap so that you're actually using destructors instead of finalizers.
[...]
 There been discussions regarding spiting up the destruction and finalizers.
Yeah, the GC does not guarantee any particular order that finalizers will be called. If you need to ensure a particular order, you'll need to either do it yourself, or use a refcounting scheme like RefCounted and set it up so that the last references go away in the right order. Lately, I've been moving towards using RefCounted for things that need deterministic destruction or things that need to be finalized in some specific order. The GC is more for objects that don't need any special action when they're collected. T -- Knowledge is that area of ignorance that we arrange and classify. -- Ambrose Bierce
Jan 05 2018
prev sibling next sibling parent reply Meta <jared771 gmail.com> writes:
On Friday, 5 January 2018 at 15:26:03 UTC, 12345swordy wrote:
 On Friday, 5 January 2018 at 14:35:44 UTC, Jonathan M Davis 
 wrote:
 On Friday, January 05, 2018 14:29:41 Adam D. Ruppe via 
 Digitalmars-d wrote:
 [...]
Either that or use structs on the stack instead of classes on the heap so that you're actually using destructors instead of finalizers. - Jonathan M Davis
There been discussions regarding spiting up the destruction and finalizers.
I can't find the thread (it's a couple years old at this point, I think), but Andrei once proposed removing class destructors and met very heavy resistance so he dropped it.
Jan 05 2018
parent reply 12345swordy <alexanderheistermann gmail.com> writes:
On Friday, 5 January 2018 at 17:14:58 UTC, Meta wrote:
 On Friday, 5 January 2018 at 15:26:03 UTC, 12345swordy wrote:
 On Friday, 5 January 2018 at 14:35:44 UTC, Jonathan M Davis 
 wrote:
 On Friday, January 05, 2018 14:29:41 Adam D. Ruppe via 
 Digitalmars-d wrote:
 [...]
Either that or use structs on the stack instead of classes on the heap so that you're actually using destructors instead of finalizers. - Jonathan M Davis
There been discussions regarding spiting up the destruction and finalizers.
I can't find the thread (it's a couple years old at this point, I think), but Andrei once proposed removing class destructors and met very heavy resistance so he dropped it.
Removing it is not the same thing as spiting them up.
Jan 05 2018
parent Meta <jared771 gmail.com> writes:
On Friday, 5 January 2018 at 19:18:59 UTC, 12345swordy wrote:
 On Friday, 5 January 2018 at 17:14:58 UTC, Meta wrote:
 On Friday, 5 January 2018 at 15:26:03 UTC, 12345swordy wrote:
 On Friday, 5 January 2018 at 14:35:44 UTC, Jonathan M Davis 
 wrote:
 On Friday, January 05, 2018 14:29:41 Adam D. Ruppe via 
 Digitalmars-d wrote:
 [...]
Either that or use structs on the stack instead of classes on the heap so that you're actually using destructors instead of finalizers. - Jonathan M Davis
There been discussions regarding spiting up the destruction and finalizers.
I can't find the thread (it's a couple years old at this point, I think), but Andrei once proposed removing class destructors and met very heavy resistance so he dropped it.
Removing it is not the same thing as spiting them up.
I meant it as an addendum
Jan 05 2018
prev sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Friday, January 05, 2018 15:26:03 12345swordy via Digitalmars-d wrote:
 On Friday, 5 January 2018 at 14:35:44 UTC, Jonathan M Davis wrote:
 On Friday, January 05, 2018 14:29:41 Adam D. Ruppe via

 Digitalmars-d wrote:
 [...]
Either that or use structs on the stack instead of classes on the heap so that you're actually using destructors instead of finalizers. - Jonathan M Davis
There been discussions regarding spiting up the destruction and finalizers.
Part of the problem with that is that if you stick a struct on the heap, its destructor becomes a finalizer. And for that matter, a class' finalizer becomes a destructor if you put in on the stack with scope or std.typecons.scoped. So, while you generally code as if structs have destructors and classes have finalizers, that distinction isn't entirely true. And if destructors and finalizers are different, then suddenly you have to implement two different functions to do more or less the same thing. That wouldn't be all bad, because on some level, it would force folks to consider the consequences of putting stuff on the heap vs the stack with regards to destruction, but in practice, it probably would result in folks either just usually implementing one and cause problems because of that or blindly making one call the other or make both call a common function to shared code. It could very well be that changing how destructors and finalizers are declared should be done, but it's not exactly a straightforward issue, and we'd need to come up with something that was clearly better for it to make sense to make a change. - Jonathan M Davis
Jan 05 2018