www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Questions about new() and auto classes

reply Oliver <Oliver_member pathlink.com> writes:
Hello !

I am new to D and have a bit of experience with C++.
The D manual says the folowing :

Instances of class objects are created with NewExpressions:

A a = new A(3);

Then there is a detailed explanation of what happens when new() is used.
In the same chapter there is an explanation about auto classes :

When an auto class reference goes out of scope, the destructor (if any) for it
is automatically called. This holds true even if the scope was exited via a
thrown exception.

My questions :

1.Does that mean that the destructor is not called for a non-auto class that
goes out of scope and for which no references are left ?

2.Is new() the only way to create new instances of classes ?
(or writing your own new)

3.When an object that has been allocated with new() has only one reference and
this reference goes out of scope, is the object automatically deleted by the GC
?

Help would be appreciated.

Regards, Oliver
May 09 2005
next sibling parent "Andrew Fedoniouk" <news terrainformatica.com> writes:
 When an auto class reference goes out of scope, the destructor (if any) 
 for it
 is automatically called. This holds true even if the scope was exited via 
 a
 thrown exception.

 My questions :

 1.Does that mean that the destructor is not called for a non-auto class 
 that
 goes out of scope and for which no references are left ?

Yes, destructor is not called immediately in this case. GC will call delete of your object later - when it will collect garbage.
 2.Is new() the only way to create new instances of classes ?
 (or writing your own new)

Yes. But remeber that instances of structures behave differently and you can allocate them on stack. In D class and structure are not synonyms. (which is good, IMHO)
 3.When an object that has been allocated with new() has only one reference 
 and
 this reference goes out of scope, is the object automatically deleted by 
 the GC
 ?

Automatic deletion happens during garbage collection which might not occur at all.
 Help would be appreciated.

BTW: There is a digitalmars.d.learn newsgroup. I think that you may find some useful answers there. Regards. Andrew.
May 09 2005
prev sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Oliver" <Oliver_member pathlink.com> wrote in message 
news:d5oaah$189q$1 digitaldaemon.com...
 1.Does that mean that the destructor is not called for a non-auto class 
 that
 goes out of scope and for which no references are left ?
 3.When an object that has been allocated with new() has only one reference 
 and
 this reference goes out of scope, is the object automatically deleted by 
 the GC
 ?

With regards to these two questions: As Andrew noted in his reply, the destructors will be called when the GC is run. Usually. If the GC is run during the program, it will call the destructor on any class instances which need it. However, at the end of the program, all allocated memory is deleted - but no destructors are called. This is, in my opinion, inconsistent and very bad behavior. This model assumes that D programs will only perform vanilla memory allocation, and that they will not create other kinds of resources (COM objects, video card resources, etc.) which are _not_ handled by the program itself and which must be cleaned up manually. I really hope this behavior changes.
May 09 2005
next sibling parent Derek Parnell <derek psych.ward> writes:
On Mon, 9 May 2005 20:25:00 -0400, Jarrett Billingsley wrote:

 "Oliver" <Oliver_member pathlink.com> wrote in message 
 news:d5oaah$189q$1 digitaldaemon.com...
 1.Does that mean that the destructor is not called for a non-auto class 
 that
 goes out of scope and for which no references are left ?
 3.When an object that has been allocated with new() has only one reference 
 and
 this reference goes out of scope, is the object automatically deleted by 
 the GC
 ?

With regards to these two questions: As Andrew noted in his reply, the destructors will be called when the GC is run. Usually. If the GC is run during the program, it will call the destructor on any class instances which need it. However, at the end of the program, all allocated memory is deleted - but no destructors are called.

What?!?! That's crazy! That's like saying that D regards RAM as the only resource that we have to manage.
 This is, in my opinion, inconsistent and very bad behavior.  This model 
 assumes that D programs will only perform vanilla memory allocation, and 
 that they will not create other kinds of resources (COM objects, video card 
 resources, etc.) which are _not_ handled by the program itself and which 
 must be cleaned up manually.
 
 I really hope this behavior changes.

Absolutely. I'd be worried about databases and file locks too. Not all files are locked using the operating system's built-in mechanism. -- Derek Melbourne, Australia 10/05/2005 10:41:23 AM
May 09 2005
prev sibling parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
"Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote in message 
news:d5ouoi$2i8$1 digitaldaemon.com...
 "Oliver" <Oliver_member pathlink.com> wrote in message 
 news:d5oaah$189q$1 digitaldaemon.com...
 1.Does that mean that the destructor is not called for a non-auto class 
 that
 goes out of scope and for which no references are left ?
 3.When an object that has been allocated with new() has only one 
 reference and
 this reference goes out of scope, is the object automatically deleted by 
 the GC
 ?

With regards to these two questions: As Andrew noted in his reply, the destructors will be called when the GC is run. Usually. If the GC is run during the program, it will call the destructor on any class instances which need it. However, at the end of the program, all allocated memory is deleted - but no destructors are called.

Current the GC is run at program exit. Many of us wish it weren't. To me the three main reasons why it shouldn't run is 1) it is run after the module dtors which means your destructors can't rely on anything that depends on the module being "initialized" 2) it sucks up significant time to quit when the OS will reclaim "everything" anyway (people may disagree on what "everything" means) 3) dtors aren't guaranteed to run anyway so in particular people can't count on them to run at program exit.
 This is, in my opinion, inconsistent and very bad behavior.  This model 
 assumes that D programs will only perform vanilla memory allocation, and 
 that they will not create other kinds of resources (COM objects, video 
 card resources, etc.) which are _not_ handled by the program itself and 
 which must be cleaned up manually.

 I really hope this behavior changes.

Note any resource that is only freed by a destructor is a leak independent of the exit behavior since the dtor might not be run ever.
May 09 2005
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Ben Hinkle" <ben.hinkle gmail.com> wrote in message 
news:d5p0gn$3k0$1 digitaldaemon.com...
 Current the GC is run at program exit. Many of us wish it weren't. To me 
 the three main reasons why it shouldn't run is
 1) it is run after the module dtors which means your destructors can't 
 rely on anything that depends on the module being "initialized"
 2) it sucks up significant time to quit when the OS will reclaim 
 "everything" anyway (people may disagree on what "everything" means)
 3) dtors aren't guaranteed to run anyway so in particular people can't 
 count on them to run at program exit.

Well, what I'm suggesting is that the spec *change* so that dtors _are_ guaranteed to be run. I find that to be rather important behavior. Besides - if your program is taking a long time to exit when the GC is run at the end, that just sounds like a case of extremely lax memory management programming. Now yes, the GC is supposed to clean up after us if we make mistakes and to make some things easier - but it's just irresponsible to _never_ delete anything that you're done with!
 Note any resource that is only freed by a destructor is a leak independent 
 of the exit behavior since the dtor might not be run ever.

So.. when else are we supposed to release these other resources? The dtor seems like a rather logical place to do this, especially if these resources are created in the ctor..
May 10 2005
next sibling parent reply Burton Radons <burton-radons smocky.com> writes:
Jarrett Billingsley wrote:

 "Ben Hinkle" <ben.hinkle gmail.com> wrote in message 
 news:d5p0gn$3k0$1 digitaldaemon.com...
 
Current the GC is run at program exit. Many of us wish it weren't. To me 
the three main reasons why it shouldn't run is
1) it is run after the module dtors which means your destructors can't 
rely on anything that depends on the module being "initialized"
2) it sucks up significant time to quit when the OS will reclaim 
"everything" anyway (people may disagree on what "everything" means)
3) dtors aren't guaranteed to run anyway so in particular people can't 
count on them to run at program exit.

Well, what I'm suggesting is that the spec *change* so that dtors _are_ guaranteed to be run. I find that to be rather important behavior. Besides - if your program is taking a long time to exit when the GC is run at the end, that just sounds like a case of extremely lax memory management programming. Now yes, the GC is supposed to clean up after us if we make mistakes and to make some things easier - but it's just irresponsible to _never_ delete anything that you're done with!

Okay, both sides of the debate here are confused as to what is currently going on in the GC. Here's an example to illustrate it: class C { char [] name; this (char [] name) { this.name = name; } ~this () { printf ("DELETED %.*s!\n", name); } } C global; void main () { C stack = new C ("Stack"); new C ("Heap"); global = new C ("Global"); } When run, this prints "DELETED Stack!" and "DELETED Heap!", leaving the global undeleted. That's because the GC DOES try to clean up after itself but calls the wrong function - instead of calling collectNoStack (which still scans static data), it should call a function that does nothing but call destructors in all the allocated objects, which would be a very fast pass. If this were fixed and a replacement for gc.d/gcx.d were sent to Walter, I see no reason why he wouldn't include it in Phobos. There's another deficiency in that signals are not handled, so if you replace the main with this: void main () { C stack = new C ("Stack"); new C ("Heap"); global = new C ("Global"); C null_object; null_object.toString (); // SIGSEGV } Then no destructors are called at all. A signal-handling patch would probably also be accepted into Phobos if it works in both Windows and Linux; be certain that if the GC destruction causes another signal thrown, that it can recover and continue.
May 10 2005
next sibling parent reply Sean Kelly <sean f4.ca> writes:
In article <d5r87a$1tuj$1 digitaldaemon.com>, Burton Radons says...
When run, this prints "DELETED Stack!" and "DELETED Heap!", leaving the 
global undeleted.  That's because the GC DOES try to clean up after 
itself but calls the wrong function - instead of calling collectNoStack 
(which still scans static data), it should call a function that does 
nothing but call destructors in all the allocated objects, which would 
be a very fast pass.

So long as there's a rule that dtors should never expect contained objects to exist, I agree. And given that the point of dtors in a GC language are to release non-memory resources, this does seem reasonable. Sean
May 10 2005
parent reply Burton Radons <burton-radons smocky.com> writes:
Sean Kelly wrote:

 In article <d5r87a$1tuj$1 digitaldaemon.com>, Burton Radons says...
 
When run, this prints "DELETED Stack!" and "DELETED Heap!", leaving the 
global undeleted.  That's because the GC DOES try to clean up after 
itself but calls the wrong function - instead of calling collectNoStack 
(which still scans static data), it should call a function that does 
nothing but call destructors in all the allocated objects, which would 
be a very fast pass.

So long as there's a rule that dtors should never expect contained objects to exist, I agree. And given that the point of dtors in a GC language are to release non-memory resources, this does seem reasonable.

That's no different than the present situation. Does generational collect allow this kind of situation: class A { } class B { A a; } void main () { (new B).a = new A; } Where "A" is deleted in one collection pass but "B" is preserved for a later pass - or will it always collect "B" first or at the same time as "A"? Because if the latter, then the situation can be made more sane by mandating that destructors are called in a pass before the data is freed. That way all pointers remain legal in the destructors. They could even call methods on one another, but no resurrection. I don't know whether Walter would be amenable to putting that kind of requirement in the standard; I recommended it a couple years back but I can't remember what he responded with. I don't really know anything about garbage collectors altogether.
May 10 2005
parent Kevin Bealer <Kevin_member pathlink.com> writes:
In article <d5r9t1$1v30$1 digitaldaemon.com>, Burton Radons says...

That's no different than the present situation.

Does generational collect allow this kind of situation:

    class A { }
    class B { A a; }

    void main ()
    {
        (new B).a = new A;
    }

Where "A" is deleted in one collection pass but "B" is preserved for a 
later pass - or will it always collect "B" first or at the same time as 
"A"?  Because if the latter, then the situation can be made more sane by 
mandating that destructors are called in a pass before the data is 
freed.  That way all pointers remain legal in the destructors.  They 
could even call methods on one another, but no resurrection.

I don't know whether Walter would be amenable to putting that kind of 
requirement in the standard; I recommended it a couple years back but I 
can't remember what he responded with.  I don't really know anything 
about garbage collectors altogether.

I think this hampers performance and has other problems. Finding an ordering for deletion would essentially require a mark and sweep pass on the garbage itself, at the very least. Currently, object in the trash bin can be deleted in any order, but your technique would require the GC to iterate over a linked list for example, and delete the objects in the forward order. But the guarantee is broken if you have a cyclical structure. So: hampers performance, and can't be guaranteed in all cases. You can get a similar effect via the following "weak pointer" technique: : class foo { } : class bar { : this() : { : weak_table[this] = x = new foo; : } : : ~this() : { : // not a delete() call; just remove from table. : weak_table[this] = null; : delete weak_table[this]; : } : : foo x; : } : : foo[bar] weak_table; The weak_table keeps the pointers around until bar is deleted. When that happens the "x" will continue to exist until the next pass, unless a pointer to it exists elsewhere. This lets you keep multiple pointers to the same "foo" from any number of "bar" objects. When the last weak_table entry for foo is removed, foo becomes garbage. Since this happens during a run, foo will not be collected yet. Note that if a "bar.dup" call is made, the second object will not be able to safeguard ordering of deletion. So don't do that. Otherwise I think this is mostly safe. Also, if there are cycles in the data the affected objects are leaked. Kevin
May 11 2005
prev sibling parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
"Burton Radons" <burton-radons smocky.com> wrote in message 
news:d5r87a$1tuj$1 digitaldaemon.com...
 Jarrett Billingsley wrote:

 "Ben Hinkle" <ben.hinkle gmail.com> wrote in message 
 news:d5p0gn$3k0$1 digitaldaemon.com...

Current the GC is run at program exit. Many of us wish it weren't. To me 
the three main reasons why it shouldn't run is
1) it is run after the module dtors which means your destructors can't 
rely on anything that depends on the module being "initialized"
2) it sucks up significant time to quit when the OS will reclaim 
"everything" anyway (people may disagree on what "everything" means)
3) dtors aren't guaranteed to run anyway so in particular people can't 
count on them to run at program exit.

Well, what I'm suggesting is that the spec *change* so that dtors _are_ guaranteed to be run. I find that to be rather important behavior. Besides - if your program is taking a long time to exit when the GC is run at the end, that just sounds like a case of extremely lax memory management programming. Now yes, the GC is supposed to clean up after us if we make mistakes and to make some things easier - but it's just irresponsible to _never_ delete anything that you're done with!

Okay, both sides of the debate here are confused as to what is currently going on in the GC. Here's an example to illustrate it: class C { char [] name; this (char [] name) { this.name = name; } ~this () { printf ("DELETED %.*s!\n", name); } } C global; void main () { C stack = new C ("Stack"); new C ("Heap"); global = new C ("Global"); } When run, this prints "DELETED Stack!" and "DELETED Heap!", leaving the global undeleted. That's because the GC DOES try to clean up after itself but calls the wrong function - instead of calling collectNoStack (which still scans static data), it should call a function that does nothing but call destructors in all the allocated objects, which would be a very fast pass.

I'm aware of fullCollectNoStack - that's what I meant by a GC is run at the end. The topic of gc on exit has come up recently: digitalmars.D/20780, with Walter's statement that he plans on commenting out the fullCollectNoStack call: digitalmars.D/20809. It also came up ages ago but I didn't find a thread with a quick grep of the archives. Note a full pass with no static data roots would have to be careful about Thread objects which can still be alive when the last GC runs (in particular the main thread is still alive). I've also argued (with Mike Parker, I think) that D needs to work out the behavior of threads when the main thread is exiting. Right now child threads can still be alive on exit and it makes for some seg-v's on exit that are a pain.
 If this were fixed and a replacement for gc.d/gcx.d were sent to Walter, I 
 see no reason why he wouldn't include it in Phobos.

 There's another deficiency in that signals are not handled, so if you 
 replace the main with this:

     void main ()
     {
         C stack = new C ("Stack");
         new C ("Heap");
         global = new C ("Global");
         C null_object;
         null_object.toString (); // SIGSEGV
     }

 Then no destructors are called at all.  A signal-handling patch would 
 probably also be accepted into Phobos if it works in both Windows and 
 Linux; be certain that if the GC destruction causes another signal thrown, 
 that it can recover and continue. 

May 10 2005
next sibling parent "Ben Hinkle" <ben.hinkle gmail.com> writes:
 I've also argued (with Mike Parker, I think) that D needs to work out the 
 behavior of threads when the main thread is exiting. Right now child 
 threads can still be alive on exit and it makes for some seg-v's on exit 
 that are a pain.

I meant Mike Swieton. sorry!
May 10 2005
prev sibling parent Sean Kelly <sean f4.ca> writes:
In article <d5reg4$22b2$1 digitaldaemon.com>, Ben Hinkle says...
Note a full pass with no static data roots would have to be careful about 
Thread objects which can still be alive when the last GC runs (in particular 
the main thread is still alive). I've also argued (with Mike Parker, I 
think) that D needs to work out the behavior of threads when the main thread 
is exiting. Right now child threads can still be alive on exit and it makes 
for some seg-v's on exit that are a pain.

I was thinking about this the other day. What I'm thinking of doing for Ares is terminating all spawned threads when the thread module is unloaded. This could leave the app is a nasty state, but it's probably better than the alternatives. The only real risk is of a complicated dtor freezing shutdown because it's attempting to enter a critical section that was previously locked by a terminated thread. Though if you wanted to get fancy you could probably implement the thread code such that synchronization isn't attempted if the thread module isn't loaded. Sean
May 10 2005
prev sibling next sibling parent reply Mike Capp <mike.capp gmail.com> writes:
In article <d5r662$1s9o$1 digitaldaemon.com>, Jarrett Billingsley says...
So.. when else are we supposed to release these other resources?  The dtor 
seems like a rather logical place to do this, especially if these resources 
are created in the ctor.. 

Welcome to GC hell. In many (possibly most) cases you can use an auto class/variable, which guarantees to run the dtor when it goes out of scope. However, this is no help when you've got references with unpredictable lifetimes. As I understand the rules for auto, you can't write a refcounting smart pointer in D. GC has its place and is a great help when you don't care about timing, but it's emphatically NOT a substitute for generalized RAII, and it's usually unsuitable for managing any resources except memory. This is (pretty much the only) thing keeping me from switching from C++ to D. cheers, Mike
May 10 2005
next sibling parent Kevin Bealer <Kevin_member pathlink.com> writes:
In article <d5r8q4$1ugr$1 digitaldaemon.com>, Mike Capp says...

In many (possibly most) cases you can use an auto class/variable, which
guarantees to run the dtor when it goes out of scope. However, this is no help
when you've got references with unpredictable lifetimes. As I understand the
rules for auto, you can't write a refcounting smart pointer in D.

I'm curious about this -- why couldn't you write such a thing? Kevin
cheers,
Mike

May 10 2005
prev sibling next sibling parent Kevin Bealer <Kevin_member pathlink.com> writes:
In article <d5r8q4$1ugr$1 digitaldaemon.com>, Mike Capp says...

In many (possibly most) cases you can use an auto class/variable, which
guarantees to run the dtor when it goes out of scope. However, this is no help
when you've got references with unpredictable lifetimes. As I understand the
rules for auto, you can't write a refcounting smart pointer in D.

I'm curious about this -- why couldn't you write such a thing? Kevin
cheers,
Mike

May 10 2005
prev sibling parent reply "Andrew Fedoniouk" <news terrainformatica.com> writes:
"Mike Capp" <mike.capp gmail.com> wrote in message 
news:d5r8q4$1ugr$1 digitaldaemon.com...
 In article <d5r662$1s9o$1 digitaldaemon.com>, Jarrett Billingsley says...
So.. when else are we supposed to release these other resources?  The dtor
seems like a rather logical place to do this, especially if these 
resources
are created in the ctor..

Welcome to GC hell.

Voice from there: already here.... :)
 In many (possibly most) cases you can use an auto class/variable, which
 guarantees to run the dtor when it goes out of scope. However, this is no 
 help
 when you've got references with unpredictable lifetimes. As I understand 
 the
 rules for auto, you can't write a refcounting smart pointer in D.

I do not see any problems with refcounting implementation in D.
 GC has its place and is a great help when you don't care about timing, but 
 it's
 emphatically NOT a substitute for generalized RAII, and it's usually 
 unsuitable
 for managing any resources except memory. This is (pretty much the only) 
 thing
 keeping me from switching from C++ to D.

Why? auto as a RAII just works. And other thing: new/delete also there and you can disable (I guess) GC by calling std.gc.disable(); so you can work with memory as in "plain C++" :) GC is good in general as it eliminates such stuff as shared_ptr<> and co. And about resources: Take a look on WinMain here: http://www.digitalmars.com/d/windows.html Based on this, e.g. typical Harmonia (any other win32 application can be made this way) looks like: //| //| static module ctor //| //| Harmonia way to define GUI application //| static this() { Application.onStart = // Will be called after runtime started and statics were intitalized. // Voulez-vous dancer? function void() { // creating and showing MainWindow here (new MyFrame()).state = Window.STATE.SHOWN; }; Application.onStop = // Application is about to quit, // all windows closed so do graceful quit. function void() { // close files, etc. }; } Last function (onStop) allows to do stuff *before* static destructors and close any open resources. Andrew.
May 10 2005
parent Mike Capp <mike.capp gmail.com> writes:
In article <d5r8q4$1ugr$1 digitaldaemon.com>, Mike Capp rants...
As I understand the
rules for auto, you can't write a refcounting smart pointer in D.


In article <d5s1e7$6jo$1 digitaldaemon.com>, Kevin Bealer says...
I'm curious about this -- why couldn't you write such a thing?

And in article <d5s5n6$9e7$1 digitaldaemon.com>, Andrew Fedoniouk says...
I do not see any problems with refcounting implementation in D.
[snip]
Why? auto as a RAII just works.

From http://www.digitalmars.com/d/attribute.html#auto : "Auto cannot be applied to globals, statics, data members, inout or out parameters. Arrays of autos are not allowed, and auto function return values are not allowed. Assignment to an auto, other than initialization, is not allowed." If there's a way to write a general-purpose refcounting smart pointer under those restrictions, I must be exceedingly dense, because I can't even begin to see it. You *need* to be able to assign to smart pointers. You *need* to be able to have smart pointer data members. And you probably need to be able to return smart pointers from functions. cheers Mike
May 11 2005
prev sibling parent reply "B.G." <gbatyan gmx.net> writes:
Jarrett Billingsley wrote:
 "Ben Hinkle" <ben.hinkle gmail.com> wrote in message 
 news:d5p0gn$3k0$1 digitaldaemon.com...
 
Current the GC is run at program exit. Many of us wish it weren't. To me 
the three main reasons why it shouldn't run is
1) it is run after the module dtors which means your destructors can't 
rely on anything that depends on the module being "initialized"
2) it sucks up significant time to quit when the OS will reclaim 
"everything" anyway (people may disagree on what "everything" means)
3) dtors aren't guaranteed to run anyway so in particular people can't 
count on them to run at program exit.


 
 Well, what I'm suggesting is that the spec *change* so that dtors _are_ 
 guaranteed to be run.  I find that to be rather important behavior.
 

What does it actually mean that dtors are not guaranteed to be run? If excluding following conditions from consideration: - unplugging PC's power, etc :) - segfault - Ctrl-C - reaching end of main() function - calling exit etc. etc. what happens when a program executes for hours, how do dtors behave during 'normal' operation? ARE they still not guaranteed to be called? Regards
May 13 2005
parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
"B.G." <gbatyan gmx.net> wrote in message 
news:d62qm8$2krb$1 digitaldaemon.com...
 Jarrett Billingsley wrote:
 "Ben Hinkle" <ben.hinkle gmail.com> wrote in message 
 news:d5p0gn$3k0$1 digitaldaemon.com...

Current the GC is run at program exit. Many of us wish it weren't. To me 
the three main reasons why it shouldn't run is
1) it is run after the module dtors which means your destructors can't 
rely on anything that depends on the module being "initialized"
2) it sucks up significant time to quit when the OS will reclaim 
"everything" anyway (people may disagree on what "everything" means)
3) dtors aren't guaranteed to run anyway so in particular people can't 
count on them to run at program exit.


 Well, what I'm suggesting is that the spec *change* so that dtors _are_ 
 guaranteed to be run.  I find that to be rather important behavior.

What does it actually mean that dtors are not guaranteed to be run?

The second-to-last paragraph (and the last paragraph, really) in http://www.digitalmars.com/d/class.html#destructors says that the GC doesn't have to reclaim unreferenced objects. This is to allow conservative collectors that interpret ambiguous pointers (which are values that the GC scans that could be pointers to an object) as live references. Since this is very rare during normal use the destuctor will usually run. The debate above is about requiring destructors to run at some point before program exit. My own view is that the garbage collector's job is to manage memory resource and any other resource should be managed by hand or let the OS clean it up at exit. Letting the GC manage the resource is like getting a "don't call us - we'll call you" from a client. You never know if or when they are actually going to call. :-P
 If excluding following conditions from consideration:

 - unplugging PC's power, etc :)
 - segfault
 - Ctrl-C
 - reaching end of main() function
 - calling exit
 etc. etc.

 what happens when a program executes for hours, how do dtors behave during 
 'normal' operation? ARE they still not guaranteed to be called?

It could happen - ambiguous pointers can occur any time.
May 13 2005
parent reply "B.G." <gbatyan gmx.net> writes:
Ben Hinkle wrote:
 "B.G." <gbatyan gmx.net> wrote in message 
 news:d62qm8$2krb$1 digitaldaemon.com...
 
Jarrett Billingsley wrote:

"Ben Hinkle" <ben.hinkle gmail.com> wrote in message 
news:d5p0gn$3k0$1 digitaldaemon.com...


Current the GC is run at program exit. Many of us wish it weren't. To me 
the three main reasons why it shouldn't run is
1) it is run after the module dtors which means your destructors can't 
rely on anything that depends on the module being "initialized"
2) it sucks up significant time to quit when the OS will reclaim 
"everything" anyway (people may disagree on what "everything" means)
3) dtors aren't guaranteed to run anyway so in particular people can't 
count on them to run at program exit.


Well, what I'm suggesting is that the spec *change* so that dtors _are_ 
guaranteed to be run.  I find that to be rather important behavior.

OH! I wanted to ask this question since ages. What does it actually mean that dtors are not guaranteed to be run?

The second-to-last paragraph (and the last paragraph, really) in http://www.digitalmars.com/d/class.html#destructors says that the GC doesn't have to reclaim unreferenced objects. This is to allow conservative collectors that interpret ambiguous pointers (which are values that the GC scans that could be pointers to an object) as live references. Since this is very rare during normal use the destuctor will usually run. The debate above is about requiring destructors to run at some point before program exit. My own view is that the garbage collector's job is to manage memory resource and any other resource should be managed by hand or let the OS clean it up at exit. Letting the GC manage the resource is like getting a "don't call us - we'll call you" from a client. You never know if or when they are actually going to call. :-P
If excluding following conditions from consideration:

- unplugging PC's power, etc :)
- segfault
- Ctrl-C
- reaching end of main() function
- calling exit
etc. etc.

what happens when a program executes for hours, how do dtors behave during 
'normal' operation? ARE they still not guaranteed to be called?

It could happen - ambiguous pointers can occur any time.

different problem. So there are for sure no other conditions why a destructor wouldn't be called, right? Apropos ambigous pointers, I was always wondering... Do you know how inteligent is the GC about areas where it looks for pointers? for example, does it see the entire stack as an array of pointers or it is aware of stack ranges containing only pointers or ranges definitely not containing pointers? What about class members? Any Ideas?
May 13 2005
parent "Ben Hinkle" <ben.hinkle gmail.com> writes:
what happens when a program executes for hours, how do dtors behave 
during 'normal' operation? ARE they still not guaranteed to be called?

It could happen - ambiguous pointers can occur any time.

different problem. So there are for sure no other conditions why a destructor wouldn't be called, right?

Well, technically the GC calls the destructor when it collects a given piece of memory and there are no guarantees that the GC will collect a piece of memory - for whatever reason. In practice the only reason I know of why the GC doesn't collect something is due to ambiguous pointers and program-exit behavior (it scans with the data segment as roots). But that doesn't mean some other GC won't come along that could arbitrarily decide to never collect something or other.
 Apropos ambigous pointers, I was always wondering... Do you know how 
 inteligent is the GC about areas where it looks for pointers? for example, 
 does it see the entire stack as an array of pointers or it is aware of 
 stack ranges containing only pointers or ranges definitely not containing 
 pointers? What about class members? Any Ideas?

The dmd GC doesn't know if a piece of memory contains pointers or not. The boehm collector has two memory pool - one for types that might contain pointers and one that doesn't. Eventually the dmd compiler could detect if an allocation could be made from a pointer-free pool (eg allocating a string) but for now the only API assumes every allocation can contain pointers. The stack doesn't have type info on it so the GC scanning code can't skip any ranges of it.
May 13 2005