May 10, 2005
"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:
http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D/20780, with
Walter's statement that he plans on commenting out the fullCollectNoStack
call: http://www.digitalmars.com/drn-bin/wwwnews?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
> 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
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 11, 2005
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 11, 2005
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 11, 2005
"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 11, 2005
>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 12, 2005
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 13, 2005
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?

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
"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.