February 27, 2014 Re: GC for noobs | ||||
---|---|---|---|---|
| ||||
Posted in reply to Szymon Gatner | On Thursday, 27 February 2014 at 13:00:27 UTC, Szymon Gatner wrote:
> On Thursday, 27 February 2014 at 12:32:36 UTC, Namespace wrote:
>> On Thursday, 27 February 2014 at 12:25:49 UTC, Szymon Gatner wrote:
>>> On Thursday, 27 February 2014 at 11:13:17 UTC, bearophile wrote:
>>>> Szymon Gatner:
>>>>
>>>>>I just want them to do their cleanup first as they should.
>>>>
>>>> Why? Perhaps if you explain what's behind your needs better, people can help better.
>>>>
>>>> Bye,
>>>> bearophile
>>>
>>> In my specific example I am creating OpenGL Renderer. Renderer class instance holds GLContext instance. After context creation GL objects are created like textures and vertex buffers. Those are Texture and VertexBuffer class instances too. It is critical that those child resources are freed before GL context is destroyed. Thing is, even tho Renderer keeps list of Textures , VertexBuffers and Context object, order of their destruction is completely undefined making Context d-tor called first and then child resources d-tors which ends in catastrophe.
>>>
>>> In C++ (which has a LOT of problems) this is a no-brainer. Order of d-tors is fully defined. Sub-objects are destroyed before parent d-tor is called so only thing to worry about is the order of definition of members.
>>>
>>> This is just an example but I would think that it is something rather important to have... What about child objects un-registering themselves in d-tors from a list that parent object holds and parent is destroyed first? What about asynchronous completion handler object that should notify some other object that it finished/not finished a job but a notifee is already destroyed because application is being shut-down? I honestly can't imagine how to reason about object graph lifetimes in GC world and I am sure I am missing something very basic. Probably a very different mindset.
>>
>> I had the same problem in Dgame. Therefore I use shared pointers (https://github.com/Dgame/Dgame/blob/master/Graphics/Surface.d#L90) or if I have to use classes, I use this:
>>
>> https://github.com/Dgame/Dgame/blob/master/Window/Window.d#L44
>> https://github.com/Dgame/Dgame/blob/master/Window/Window.d#L177
>>
>> I put the instances in a module global array and the module dtor finalize the data.
>
> Module d-tor() looks like a pretty good solution, thanks. shared_ptr tho... I was really hoping that I am moving away from it to a better place...
>
> Still, this feels like working around a language issue, if c-tor order is defined why d-tor isn't? I am ok with non-deterministic time of execution of d-tors/finalizers but not-having parent-child d-tor order defined? That is weird.
I use this solution especially because I have to finalize the data before I call SDL_Quit. And therefore I cannot trust the non-deterministic execution of the Dtor.
|
February 27, 2014 Re: GC for noobs | ||||
---|---|---|---|---|
| ||||
Posted in reply to Szymon Gatner | On 2/27/2014 9:25 PM, Szymon Gatner wrote:
>
> This is just an example but I would think that it is something rather
> important to have... What about child objects un-registering themselves
> in d-tors from a list that parent object holds and parent is destroyed
> first? What about asynchronous completion handler object that should
> notify some other object that it finished/not finished a job but a
> notifee is already destroyed because application is being shut-down? I
> honestly can't imagine how to reason about object graph lifetimes in GC
> world and I am sure I am missing something very basic. Probably a very
> different mindset.
>
Two simple rules I follow.
#1 Never rely on class destructors for determinstic clean up
#2 Never rely on class destructors for releasing system resources
I tend to add two methods to classes that need to do any sort of set up and clean up: initialize and terminate. Then, at app exit, I make sure that the terminate methods are called either with scope(exit) or, if I want to log any exceptions that percolate up to main, try-catch-finally. Something like this:
class TextureManager {
private TexContainer _loadedTextures;
public void initialize() {...}
public void terminate() {
foreach( tex; _loadedTextures[])
tex.terminate();
}
}
struct Game {
@disable this();
@disable this( this );
private static TextureManager _texMan;
public static void initialize() {
_texMan = new TextureManager();
_texMan.initialize();
}
public static void terminate() {
_texMan.terminate();
}
}
void main() {
scope( exit ) Game.terminate();
Game.initialize();
}
In some parts of a system, for objects that tend to be short lived, I'll use structs with destructors for RAII. A good example of this is for vertex and fragment shaders. The shader programs are long-lived, so those are classes, but I always destroy the shader objects as soon as any programs that need them are built. So those are perfect candidates for the struct-based RAII approach.
RAII is a convenient tool, but it's just that, a tool. One of the tradeoffs you have to make with D's GC is that you can't use RAII the same way you do in C++. You *can* use it with structs, but for classes you're going to have to put C++ out of your head.
|
February 27, 2014 Re: GC for noobs | ||||
---|---|---|---|---|
| ||||
Posted in reply to Namespace | On Thursday, 27 February 2014 at 13:04:17 UTC, Namespace wrote:
>
> I use this solution especially because I have to finalize the data before I call SDL_Quit. And therefore I cannot trust the non-deterministic execution of the Dtor.
I fully understand that ;)
I will take a look at DGame too for sure. Atm now I am still learning D and its abilities to directly use C code.
|
February 27, 2014 Re: GC for noobs | ||||
---|---|---|---|---|
| ||||
Posted in reply to Szymon Gatner | On 2/27/2014 10:00 PM, Szymon Gatner wrote:
>
> Still, this feels like working around a language issue, if c-tor order
> is defined why d-tor isn't? I am ok with non-deterministic time of
> execution of d-tors/finalizers but not-having parent-child d-tor order
> defined? That is weird.
>
I've never seen a garbage collected language where the GC guarantees order of destruction. There's probably a very good reason for it.
|
February 27, 2014 Re: GC for noobs | ||||
---|---|---|---|---|
| ||||
Posted in reply to Mike Parker | On Thursday, 27 February 2014 at 13:07:37 UTC, Mike Parker wrote:
> On 2/27/2014 9:25 PM, Szymon Gatner wrote:
>
>>
>> This is just an example but I would think that it is something rather
>> important to have... What about child objects un-registering themselves
>> in d-tors from a list that parent object holds and parent is destroyed
>> first? What about asynchronous completion handler object that should
>> notify some other object that it finished/not finished a job but a
>> notifee is already destroyed because application is being shut-down? I
>> honestly can't imagine how to reason about object graph lifetimes in GC
>> world and I am sure I am missing something very basic. Probably a very
>> different mindset.
>>
>
> Two simple rules I follow.
>
> #1 Never rely on class destructors for determinstic clean up
> #2 Never rely on class destructors for releasing system resources
>
> I tend to add two methods to classes that need to do any sort of set up and clean up: initialize and terminate. Then, at app exit, I make sure that the terminate methods are called either with scope(exit) or, if I want to log any exceptions that percolate up to main, try-catch-finally. Something like this:
>
> class TextureManager {
> private TexContainer _loadedTextures;
> public void initialize() {...}
> public void terminate() {
> foreach( tex; _loadedTextures[])
> tex.terminate();
> }
> }
>
> struct Game {
> @disable this();
> @disable this( this );
>
> private static TextureManager _texMan;
>
> public static void initialize() {
> _texMan = new TextureManager();
> _texMan.initialize();
> }
> public static void terminate() {
> _texMan.terminate();
> }
> }
>
> void main() {
> scope( exit ) Game.terminate();
> Game.initialize();
> }
>
> In some parts of a system, for objects that tend to be short lived, I'll use structs with destructors for RAII. A good example of this is for vertex and fragment shaders. The shader programs are long-lived, so those are classes, but I always destroy the shader objects as soon as any programs that need them are built. So those are perfect candidates for the struct-based RAII approach.
>
> RAII is a convenient tool, but it's just that, a tool. One of the tradeoffs you have to make with D's GC is that you can't use RAII the same way you do in C++. You *can* use it with structs, but for classes you're going to have to put C++ out of your head.
Then the question is why not use structs all the time?
After using D for a bit more as a week I come to the solution that looks like this.
strict WasAClassInCpp
{
@disable this(); //sadly we can not use ctor in D like in C++ :(
@disable this(this); //this is not a copy constructor, this is something called after all the data in the struct was copied...
~this(){ ... } //dtor, just like in C++
}
So all my classes in C++ are ported to D structs.
I was using class only to port abstract class from C++.
Replacing it by inerface.
|
February 27, 2014 Re: GC for noobs | ||||
---|---|---|---|---|
| ||||
Posted in reply to Remo | On Thursday, 27 February 2014 at 13:18:51 UTC, Remo wrote:
> Then the question is why not use structs all the time?
Key class feature is run-time polymorphism (via interfaces/inheritance). I tend to use structs for everything else in my personal code.
|
February 27, 2014 Re: GC for noobs | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dicebot | On Thursday, 27 February 2014 at 13:31:45 UTC, Dicebot wrote:
> On Thursday, 27 February 2014 at 13:18:51 UTC, Remo wrote:
>> Then the question is why not use structs all the time?
>
> Key class feature is run-time polymorphism (via interfaces/inheritance). I tend to use structs for everything else in my personal code.
Maybe we really should implement ARC classes... :)
|
February 27, 2014 Re: GC for noobs | ||||
---|---|---|---|---|
| ||||
Posted in reply to Namespace | On Thursday, 27 February 2014 at 13:44:11 UTC, Namespace wrote:
> On Thursday, 27 February 2014 at 13:31:45 UTC, Dicebot wrote:
>> On Thursday, 27 February 2014 at 13:18:51 UTC, Remo wrote:
>>> Then the question is why not use structs all the time?
>>
>> Key class feature is run-time polymorphism (via interfaces/inheritance). I tend to use structs for everything else in my personal code.
>
> Maybe we really should implement ARC classes... :)
I don't see how it is relevant. I would not have used ARC classes either.
|
February 27, 2014 Re: GC for noobs | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dicebot | On Thursday, 27 February 2014 at 14:12:58 UTC, Dicebot wrote:
> On Thursday, 27 February 2014 at 13:44:11 UTC, Namespace wrote:
>> On Thursday, 27 February 2014 at 13:31:45 UTC, Dicebot wrote:
>>> On Thursday, 27 February 2014 at 13:18:51 UTC, Remo wrote:
>>>> Then the question is why not use structs all the time?
>>>
>>> Key class feature is run-time polymorphism (via interfaces/inheritance). I tend to use structs for everything else in my personal code.
>>
>> Maybe we really should implement ARC classes... :)
>
> I don't see how it is relevant. I would not have used ARC classes either.
Why not? Overhead? No RAII support?
|
February 27, 2014 Re: GC for noobs | ||||
---|---|---|---|---|
| ||||
Posted in reply to Namespace | On Thursday, 27 February 2014 at 14:14:43 UTC, Namespace wrote:
> Why not? Overhead? No RAII support?
Simply no reason to use classes, structs have all features I need for cases when polymorphism is not necessary (95%+). Being value type is also convenient as it leaves more control to the programmer and RAII is nice cherry on top.
|
Copyright © 1999-2021 by the D Language Foundation