www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Enforced nogc for dtors?

reply "bearophile" <bearophileHUGS lycos.com> writes:
If we keep class destructors in D, is it a good idea to require 
them to be  nogc?

This post comes after this thread in D.learn:
http://forum.dlang.org/thread/vlnjgtdmyolgoiofnfnl forum.dlang.org

Bye,
bearophile
May 04 2014
next sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Sunday, 4 May 2014 at 20:49:57 UTC, bearophile wrote:
 If we keep class destructors in D, is it a good idea to require 
 them to be  nogc?

 This post comes after this thread in D.learn:
 http://forum.dlang.org/thread/vlnjgtdmyolgoiofnfnl forum.dlang.org

 Bye,
 bearophile

Not sure that would be a good idea: "nogc" means "no interacting with the GC". In no way does it prevent accessing a ressource that itself is managed by the GC, which is what the original bug was about. Furthermore, classes *may* be deterministically desroyed, and preventing it from interacting with the GC, if only to remove scan pointers (think RefCounted/Array) would be needlessly restrictive.
May 05 2014
parent Paulo Pinto <pjmlp progtools.org> writes:
Am 05.05.2014 19:46, schrieb Orvid King via Digitalmars-d:
 The current GC cannot allocate within a destructor because of the fact
 that it has to acquire a global lock on the GC before calling the
 actual destructor, meaning that attempting to allocate or do anything
 that requires a global lock on the GC is impossible, because the lock
 has already been acquired by the thread. Admittedly this isn't the way
 it actually fails, but it is the flaw in the design that causes it to
 fail.

This is precisely the point. I see this whole discussion as going around in circles instead of fixing the GC. Which is fine, assuming that at the end of the day, D gets a sound automatic memory management model, be it RC/GC/compiler dataflow based, which doesn't keep be questioned all the time. Otherwise, I see this as the second coming of Tango vs Phobos. -- Paulo
May 05 2014
prev sibling next sibling parent Orvid King via Digitalmars-d <digitalmars-d puremagic.com> writes:
Also, the  nogc for destructors is specific to the current GC, and is
a limitation that isn't really needed were destructors implemented
properly in the current GC.

On 5/5/14, monarch_dodra via Digitalmars-d <digitalmars-d puremagic.com> wrote:
 On Sunday, 4 May 2014 at 20:49:57 UTC, bearophile wrote:
 If we keep class destructors in D, is it a good idea to require
 them to be  nogc?

 This post comes after this thread in D.learn:
 http://forum.dlang.org/thread/vlnjgtdmyolgoiofnfnl forum.dlang.org

 Bye,
 bearophile

Not sure that would be a good idea: "nogc" means "no interacting with the GC". In no way does it prevent accessing a ressource that itself is managed by the GC, which is what the original bug was about. Furthermore, classes *may* be deterministically desroyed, and preventing it from interacting with the GC, if only to remove scan pointers (think RefCounted/Array) would be needlessly restrictive.

May 05 2014
prev sibling next sibling parent "Brian Rogoff" <brogoff gmail.com> writes:
On Monday, 5 May 2014 at 14:17:04 UTC, Orvid King via 
Digitalmars-d wrote:
 Also, the  nogc for destructors is specific to the current GC, 
 and is a limitation that isn't really needed were destructors 
 implemented properly in the current GC.

How does one implement destructors (described below) properly in a garbage collector? I'm a bit puzzled by the recent storm over destructors. I think of garbage collected entities (classes in Java) as possibly having "finalizers", and scoped things as possibly having "destructors". The two concepts are related but distinct. Destructors are supposed to be deterministic, finalizers by being tied to a tracing GC are not. Java doesn't have stack allocated objects, but since 1.7 has try-'with resources' and AutoCloseable to cover some cases in RAII-like fashion. My terminology is from this http://www.hpl.hp.com/techreports/2002/HPL-2002-335.html IMO, since D has a GC, and stack allocated structs, it would make sense to use different terms for destruction and finalization, so what you really want is to properly implement finalizers in your GC. I'm a lot more reluctant to use classes in D now, and I'd like to see a lot more code with nogc or compiled with a the previously discussed and rejected no runtime switch. Interestingly, Ada finalization via 'controlled' types is actually what we call destructors here. The Ada approach is interesting, but I don't know if a similar approach would fit well with D, which is a much more pointer intensive language.
May 05 2014
prev sibling next sibling parent Orvid King via Digitalmars-d <digitalmars-d puremagic.com> writes:
The current GC cannot allocate within a destructor because of the fact
that it has to acquire a global lock on the GC before calling the
actual destructor, meaning that attempting to allocate or do anything
that requires a global lock on the GC is impossible, because the lock
has already been acquired by the thread. Admittedly this isn't the way
it actually fails, but it is the flaw in the design that causes it to
fail.

Destructors and finalizers are the same thing. They are declared the
same, function the same, and do the same things. In D, the
deterministic invocation of a destructor is a side-effect of the
optimization of allocations to occur on the stack rather than the
heap, whether this is done by the user by declaring a value a struct,
or by the compiler when it determines the value never escapes the
scope. Currently the GC doesn't invoke the destructor of a struct that
has been heap allocated, but I view this as a bug, because it is the
same thing as if it had been declared as a class instead, and a
destructor must take this into account, and not be dependent on the
deterministic destruction qualities of stack-allocated values.

On 5/5/14, Brian Rogoff via Digitalmars-d <digitalmars-d puremagic.com> wrote:
 On Monday, 5 May 2014 at 14:17:04 UTC, Orvid King via
 Digitalmars-d wrote:
 Also, the  nogc for destructors is specific to the current GC,
 and is a limitation that isn't really needed were destructors
 implemented properly in the current GC.

How does one implement destructors (described below) properly in a garbage collector? I'm a bit puzzled by the recent storm over destructors. I think of garbage collected entities (classes in Java) as possibly having "finalizers", and scoped things as possibly having "destructors". The two concepts are related but distinct. Destructors are supposed to be deterministic, finalizers by being tied to a tracing GC are not. Java doesn't have stack allocated objects, but since 1.7 has try-'with resources' and AutoCloseable to cover some cases in RAII-like fashion. My terminology is from this http://www.hpl.hp.com/techreports/2002/HPL-2002-335.html IMO, since D has a GC, and stack allocated structs, it would make sense to use different terms for destruction and finalization, so what you really want is to properly implement finalizers in your GC. I'm a lot more reluctant to use classes in D now, and I'd like to see a lot more code with nogc or compiled with a the previously discussed and rejected no runtime switch. Interestingly, Ada finalization via 'controlled' types is actually what we call destructors here. The Ada approach is interesting, but I don't know if a similar approach would fit well with D, which is a much more pointer intensive language.

May 05 2014
prev sibling next sibling parent "Brian Rogoff" <brogoff gmail.com> writes:
On Monday, 5 May 2014 at 17:46:35 UTC, Orvid King via 
Digitalmars-d wrote:
 Destructors and finalizers are the same thing.

That is exactly the point that I am arguing against. That they are confused in D (or 'unified', if you think is a good thing) I accept, but I think it's a language design error, or at least an unfortunate omission. Did you read the citation I provided? I think Boehm's argument is convincing; you've provided no rebuttal. The entire brouhaha going on now is because they're different: we assume that destructors will be called at a precise time so we can use them to manage constrained resources and we don't know that about finalizers.
May 05 2014
prev sibling next sibling parent "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Monday, 5 May 2014 at 18:32:30 UTC, Brian Rogoff wrote:
 On Monday, 5 May 2014 at 17:46:35 UTC, Orvid King via 
 Digitalmars-d wrote:
 Destructors and finalizers are the same thing.

That is exactly the point that I am arguing against. That they are confused in D (or 'unified', if you think is a good thing) I accept, but I think it's a language design error, or at least an unfortunate omission. Did you read the citation I provided? I think Boehm's argument is convincing; you've provided no rebuttal. The entire brouhaha going on now is because they're different: we assume that destructors will be called at a precise time so we can use them to manage constrained resources and we don't know that about finalizers.

+1
May 06 2014
prev sibling next sibling parent "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Monday, 5 May 2014 at 18:32:30 UTC, Brian Rogoff wrote:
 On Monday, 5 May 2014 at 17:46:35 UTC, Orvid King via 
 Digitalmars-d wrote:
 Destructors and finalizers are the same thing.

That is exactly the point that I am arguing against. That they are confused in D (or 'unified', if you think is a good thing) I accept, but I think it's a language design error, or at least an unfortunate omission.

Yes, if the compiler doesn't know the conceptual parent-child relationship. e.g.: the parent-connection may close before the child-transaction has flushed/commited. In order to prevent this you can introduce (or deduce if you have the semantics of ownership) parent-pointers from child to parent and only allow parent roots to have their destructors called. The result is that the parent-root-tree is not released until all destructible children are ready. Having class objects on a separate heap with finalizer support could be reasonable if the class construct is viewed as an application-level construct and not a system-level construct. With a dedicated finalizer datastructure you can record finalization dependencies at the cost of a heavier runtime. However, I think not calling destructors at all for GC memory is the better (more nimble) approach for a system level programming language. *shrug*
May 06 2014
prev sibling next sibling parent Orvid King via Digitalmars-d <digitalmars-d puremagic.com> writes:
I actually don't think finalizers are an issue in the GC, but I do
agree that there should be a way to ensure that a value is
deterministically finalized. The thing is though, that's currently
possible by simply declaring the value as a struct, unless it's been
heap allocated, in which case we do the best we can.

On 5/6/14, via Digitalmars-d <digitalmars-d puremagic.com> wrote:
 On Monday, 5 May 2014 at 18:32:30 UTC, Brian Rogoff wrote:
 On Monday, 5 May 2014 at 17:46:35 UTC, Orvid King via
 Digitalmars-d wrote:
 Destructors and finalizers are the same thing.

That is exactly the point that I am arguing against. That they are confused in D (or 'unified', if you think is a good thing) I accept, but I think it's a language design error, or at least an unfortunate omission.

Yes, if the compiler doesn't know the conceptual parent-child relationship. e.g.: the parent-connection may close before the child-transaction has flushed/commited. In order to prevent this you can introduce (or deduce if you have the semantics of ownership) parent-pointers from child to parent and only allow parent roots to have their destructors called. The result is that the parent-root-tree is not released until all destructible children are ready. Having class objects on a separate heap with finalizer support could be reasonable if the class construct is viewed as an application-level construct and not a system-level construct. With a dedicated finalizer datastructure you can record finalization dependencies at the cost of a heavier runtime. However, I think not calling destructors at all for GC memory is the better (more nimble) approach for a system level programming language. *shrug*

May 06 2014
prev sibling parent "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Tuesday, 6 May 2014 at 12:36:04 UTC, Orvid King via 
Digitalmars-d wrote:
 possible by simply declaring the value as a struct, unless it's 
 been heap allocated, in which case we do the best we can.

Best effort is worse than no effort...
May 06 2014