digitalmars.D - Destructors vs. Finalizers
- Mike Parker (18/18) Jul 25 2017 Regarding the issue with `destroy` not being @nogc, I my
- Mike Parker (4/12) Jul 25 2017 And an important bit I left out -- destroy would still call
- Moritz Maxeiner (27/45) Jul 26 2017 Possible, yes, and I agree that separating destruction
- Mike Parker (4/20) Jul 26 2017 Yeah, this brings with it more flexibility. I'd prefer to avoid
- jmh530 (6/14) Jul 26 2017 Some other options:
- Daniel Kozak via Digitalmars-d (4/23) Jul 26 2017 @delete() {}
- Guillaume Piolat (3/7) Jul 26 2017 I don't get the distinction between destructors and "finalizers"
- Mike Parker (4/7) Jul 26 2017 Destructors are deterministic, finalizers are not. At least,
- Guillaume Piolat (6/15) Jul 26 2017 Note that Andrei once proposed in 2014 that the GC wouldn't call
- Moritz Maxeiner (8/21) Jul 26 2017 AFAICT that was mostly because it would have broken plenty of
- Guillaume Piolat (16/22) Jul 26 2017 The validity or purposefulness of such code is up to debate, a
- Steven Schveighoffer (7/9) Jul 26 2017 For instance, a destructor could destroy recursively all members at that...
- Guillaume Piolat (30/37) Jul 26 2017 Interesting case.
- Steven Schveighoffer (23/63) Jul 26 2017 Yes, finalizer and destructor should be called on deterministic
- Guillaume Piolat (21/31) Jul 27 2017 My point is that this "last chance" to clean up resources should
- Steven Schveighoffer (12/29) Jul 27 2017 Consider reference counting with cycles. The proposal from Walter/Andrei...
- Dgame (4/7) Jul 27 2017 That is the PHP way to do things. :) It's neat but I still think
- Guillaume Piolat (16/22) Jul 27 2017 For cycle in RC you either do it like other RC system and break
- jmh530 (8/9) Jul 26 2017 Does seem a bit like a nightmare...
- Guillaume Piolat (3/15) Jul 26 2017 Well I like this idea best.
- Steven Schveighoffer (12/21) Jul 26 2017 In D, the difference is when the thing is called. If it's called
- Mike Parker (5/10) Jul 26 2017 That's essentially what Guillaume's "GC-proof resource class"
- Steven Schveighoffer (4/18) Jul 26 2017 Yeah, I've seen that.
- Dgame (6/6) Jul 26 2017 I don't get it. The GC collects the objects which aren't in use
- Moritz Maxeiner (10/14) Jul 26 2017 After the split:
- Dgame (4/18) Jul 26 2017 Alright, thanks for the clarification. I've briefly hoped for
- Moritz Maxeiner (3/6) Jul 26 2017 I'm not sure what scheme you are trying to describe here, could
- Dgame (2/9) Jul 26 2017 Built-in Ownership/RC for objects.
- Steven Schveighoffer (6/21) Jul 26 2017 Actually, it's the opposite. A finalizer can never call anything on its
- Moritz Maxeiner (14/36) Jul 26 2017 It's a matter of definition and this is the behaviour I would
- Moritz Maxeiner (3/8) Jul 26 2017 * freely with the exception of not allocating using the GC, of
- Steven Schveighoffer (15/54) Jul 26 2017 No, because a destructor can safely assume it can look at GC members'
- Moritz Maxeiner (8/37) Jul 26 2017 No, whether a finalizer can safely call a destructor depends on
- Steven Schveighoffer (9/29) Jul 26 2017 I suppose this is true, but in practice, you wouldn't in a destructor
- Swoorup Joshi (3/3) Jul 27 2017 I might be hated for saying this but I still think the GC problem
Regarding the issue with `destroy` not being nogc, I my understanding is it comes down to `rt_finalize` not being nogc. I haven't dug too deeply into the discussions around it, but I'm wondering if it's possible to separate the concept of destruction from finalization in the implementation? Externally, we can do it with the existing language: class { ~this() {} // Finalizer ~this nogc {} // Destructor } Internally, the runtime will treat each differently. an rt_destruct would call all every __dtor in a hierarchy and rt_finalize would be changed to call every __finalizer (a new addition) in a hierarchy. When cleaning up, the GC will ensure that all destructors are run where they exist, followed by all finalizers. And destroy would be changed to call rt_destruct instead of rt_finalize. Thoughts?
Jul 25 2017
On Wednesday, 26 July 2017 at 02:58:00 UTC, Mike Parker wrote:Internally, the runtime will treat each differently. an rt_destruct would call all every __dtor in a hierarchy and rt_finalize would be changed to call every __finalizer (a new addition) in a hierarchy. When cleaning up, the GC will ensure that all destructors are run where they exist, followed by all finalizers. And destroy would be changed to call rt_destruct instead of rt_finalize. Thoughts?And an important bit I left out -- destroy would still call rt_finalize if no destructor is present, which would preserve current behavior.
Jul 25 2017
On Wednesday, 26 July 2017 at 02:58:00 UTC, Mike Parker wrote:Regarding the issue with `destroy` not being nogc, I my understanding is it comes down to `rt_finalize` not being nogc. I haven't dug too deeply into the discussions around it, but I'm wondering if it's possible to separate the concept of destruction from finalization in the implementation?Possible, yes, and I agree that separating destruction (deterministic end of object lifetime) and finalization (GC collection caused end of object lifetime) for classes is something we should do.Externally, we can do it with the existing language: class { ~this() {} // Finalizer ~this nogc {} // Destructor }As class destructors (in contrast to class finalizers) are then called exclusively in a deterministic fashion, there's no reason to forbid them from allocating using the GC, so I don't think using the nogc attribute would be appropriate; I would much rather see another attribute in the likes of disable, e.g. deterministic, so --- ~this() {} // Finalizer ~this() nogc {} // Finalizer ~this deterministic {} // Destructor ~this nogc deterministic {} // Destructor } ---Internally, the runtime will treat each differently. an rt_destruct would call all every __dtor in a hierarchyAs long as finalizers are then not part of __dtor.and rt_finalize would be changed to call every __finalizer (a new addition) in a hierarchy.When cleaning up, the GC will ensure that all destructors are run where they exist, followed by all finalizers.Having the GC directly call destructors defeats the point of separating them from finalizers in the first place: If a destructor should be run on GC finalication, the finalizer must manually call the destructor (using e.g. `destroy`). The GC must *never* call destructors automatically after splitting off finalizers, because that would turn destructors back into finalizers.And destroy would be changed to call rt_destruct instead of rt_finalize.Yes, that would then be the correct behaviour for `destroy`.
Jul 26 2017
On Wednesday, 26 July 2017 at 09:29:15 UTC, Moritz Maxeiner wrote:As class destructors (in contrast to class finalizers) are then called exclusively in a deterministic fashion, there's no reason to forbid them from allocating using the GC, so I don't think using the nogc attribute would be appropriate; I would much rather see another attribute in the likes of disable, e.g. deterministic, so --- ~this() {} // Finalizer ~this() nogc {} // Finalizer ~this deterministic {} // Destructor ~this nogc deterministic {} // Destructor }Yeah, this brings with it more flexibility. I'd prefer to avoid adding a new attribute for it, but this looks more interesting.Indeed! Let's pretend I didn't write that.When cleaning up, the GC will ensure that all destructors are run where they exist, followed by all finalizers.Having the GC directly call destructors defeats the point of separating them from finalizers in the first place:
Jul 26 2017
On Wednesday, 26 July 2017 at 12:55:17 UTC, Mike Parker wrote:Some other options: ~~this() {} !this() {} !~this() {} this!(true) () {} //not really a big fan of this version--- ~this() {} // Finalizer ~this() nogc {} // Finalizer ~this deterministic {} // Destructor ~this nogc deterministic {} // Destructor }Yeah, this brings with it more flexibility. I'd prefer to avoid adding a new attribute for it, but this looks more interesting.
Jul 26 2017
delete() {} delete() {} On Wed, Jul 26, 2017 at 3:08 PM, jmh530 via Digitalmars-d < digitalmars-d puremagic.com> wrote:On Wednesday, 26 July 2017 at 12:55:17 UTC, Mike Parker wrote:---Some other options: ~~this() {} !this() {} !~this() {} this!(true) () {} //not really a big fan of this version~this() {} // Finalizer ~this() nogc {} // Finalizer ~this deterministic {} // Destructor ~this nogc deterministic {} // Destructor }Yeah, this brings with it more flexibility. I'd prefer to avoid adding a new attribute for it, but this looks more interesting.
Jul 26 2017
On Wednesday, 26 July 2017 at 02:58:00 UTC, Mike Parker wrote:When cleaning up, the GC will ensure that all destructors are run where they exist, followed by all finalizers. And destroy would be changed to call rt_destruct instead of rt_finalize. Thoughts?I don't get the distinction between destructors and "finalizers" but imho the problem is very much that the GC is calling ~this.
Jul 26 2017
On Wednesday, 26 July 2017 at 12:19:15 UTC, Guillaume Piolat wrote:I don't get the distinction between destructors and "finalizers" but imho the problem is very much that the GC is calling ~this.Destructors are deterministic, finalizers are not. At least, that's how I understand the terms are commonly used.
Jul 26 2017
On Wednesday, 26 July 2017 at 12:35:19 UTC, Mike Parker wrote:On Wednesday, 26 July 2017 at 12:19:15 UTC, Guillaume Piolat wrote:Note that Andrei once proposed in 2014 that the GC wouldn't call destructors/finalizers at all: http://forum.dlang.org/post/ljrm0d$28vf$1 digitalmars.comI don't get the distinction between destructors and "finalizers" but imho the problem is very much that the GC is calling ~this.Destructors are deterministic, finalizers are not. At least, that's how I understand the terms are commonly used.We're considering deprecating ~this() for classes in the future.Instead the forum community pushed back and what has been done is extending the calls to GC-allocated structs.
Jul 26 2017
On Wednesday, 26 July 2017 at 13:54:15 UTC, Guillaume Piolat wrote:On Wednesday, 26 July 2017 at 12:35:19 UTC, Mike Parker wrote:AFAICT that was mostly because it would have broken plenty of existing code. Properly separating destruction and finalization from each other with the current syntax remaining as finalizers and the new one for destructors would allow this to be done without major code breakage.On Wednesday, 26 July 2017 at 12:19:15 UTC, Guillaume Piolat wrote:Note that Andrei once proposed in 2014 that the GC wouldn't call destructors/finalizers at all: [...]I don't get the distinction between destructors and "finalizers" but imho the problem is very much that the GC is calling ~this.Destructors are deterministic, finalizers are not. At least, that's how I understand the terms are commonly used.
Jul 26 2017
On Wednesday, 26 July 2017 at 14:10:19 UTC, Moritz Maxeiner wrote:AFAICT that was mostly because it would have broken plenty of existing code.The validity or purposefulness of such code is up to debate, a separate debate granted. Let's assume there is working code that such a change will break.Properly separating destruction and finalization from each other with the current syntax remaining as finalizers and the new one for destructors would allow this to be done without major code breakage.Sure, in the event D would like to transition towards a state where the GC doesn't call finalizers, it seems useful. From a marketing point of view having two destructors and keyword/syntax just for this would be hard to defend, and it would also need to explain the whole story. Personally I'd be for: ~this() { /* deterministic one */ } void finalize() { /* the one called by GC */ } But then no transition path. I'll defend the view point that there is _nothing_ useful to do in a finalizer except to check if the destructor has already been called.
Jul 26 2017
On 7/26/17 10:57 AM, Guillaume Piolat wrote:I'll defend the view point that there is _nothing_ useful to do in a finalizer except to check if the destructor has already been called.For instance, a destructor could destroy recursively all members at that time. A finalizer would not be able to. The thing to do in a finalizer that is useful is to release any non-GC resources. -Steve
Jul 26 2017
On Wednesday, 26 July 2017 at 15:15:03 UTC, Steven Schveighoffer wrote:On 7/26/17 10:57 AM, Guillaume Piolat wrote:Interesting case. Do we assume the finalizer is always called after the destructor? - If yes, then releasing these non-GC resources could have been possible in the destructor too. The only lost generality would be if releasing such non-GC resources would be faster from the GC thread (could well be since pooled). - else, it's a case of the finalizer being calld by the GC and the destructor not being called. Is this considered a bug? | | - If yes, then point of releasing resources is moot since we have a bug. | | - If not, it means we want to allow not calling destructors. | | | | => this implies we think finalizers will be called | | I'll make claim this works for process-wide resources somehow (we stopped the last debate here), but not transient ones (eg: mutex) because of false pointers. The finalizer might be released late. From these premises I conclude that the instructions given to new D programmers would be: 1. you should destroy resources deterministically 2. however GC objects owning resources may release them in their finalizer * except if you can't release them from any thread * except if these resources should be released before the GC shutdown (and then you have to explain why finalizer might not be called right now).I'll defend the view point that there is _nothing_ useful to do in a finalizer except to check if the destructor has already been called.The thing to do in a finalizer that is useful is to release any non-GC resources. -Steve
Jul 26 2017
On 7/26/17 12:01 PM, Guillaume Piolat wrote:On Wednesday, 26 July 2017 at 15:15:03 UTC, Steven Schveighoffer wrote:Yes, finalizer and destructor should be called on deterministic destruction (no need to repeat the finalizer code in the destructor).On 7/26/17 10:57 AM, Guillaume Piolat wrote:Interesting case. Do we assume the finalizer is always called after the destructor?I'll defend the view point that there is _nothing_ useful to do in a finalizer except to check if the destructor has already been called.The thing to do in a finalizer that is useful is to release any non-GC resources.- If yes, then releasing these non-GC resources could have been possible in the destructor too. The only lost generality would be if releasing such non-GC resources would be faster from the GC thread (could well be since pooled).But the destructor can't be called from the GC. In cases where the GC is being used to clean up the object (whether on purpose or by accident), then if you didn't have a finalizer, the resource leaks.- else, it's a case of the finalizer being calld by the GC and the destructor not being called. Is this considered a bug?No.| | - If yes, then point of releasing resources is moot since we have a bug. | | - If not, it means we want to allow not calling destructors.Of course, the idea is that a destructor does what the GC would have done (recursively call the finalizers of members, and optionally clean up memory if wholly owned).| | | | => this implies we think finalizers will be called | | I'll make claim this works for process-wide resources somehow (we stopped the last debate here), but not transient ones (eg: mutex) because of false pointers. The finalizer might be released late. From these premises I conclude that the instructions given to new D programmers would be: 1. you should destroy resources deterministically 2. however GC objects owning resources may release them in their finalizer * except if you can't release them from any thread * except if these resources should be released before the GC shutdown (and then you have to explain why finalizer might not be called right now).Today, the finalizer is essentially a last-effort to clean up resources that would otherwise leak. The reason deterministic destruction sucks today is because once you go 2 levels deep, the only tool available (~this) cannot release any resources (because it's not legal to access GC-allocated members inside ~this). Adding a destructor concept would fix this issue, and make deterministic destruction more pleasant. Look at any library that contains such non-memory resources. These things inevitably implement some sort of "close()" or "release()" function, which does the deterministic destruction. It's just a destructor with a different name. -Steve
Jul 26 2017
On Wednesday, 26 July 2017 at 22:29:50 UTC, Steven Schveighoffer wrote:Today, the finalizer is essentially a last-effort to clean up resources that would otherwise leak.My point is that this "last chance" to clean up resources should only be used to tell deterministic destruction wasn't acheived (for reasons given in my former post: it's _too hard to explain_ how to do resource management else).The reason deterministic destruction sucks today is because once you go 2 levels deep, the only tool available (~this) cannot release any resources (because it's not legal to access GC-allocated members inside ~this).You already know it, but if you commit to not doing anything in the finalizer case, then ~this doesn't suck, it works as a destructor and the restrictions lift. So "suck" is a bit strong.Look at any library that contains such non-memory resources. These things inevitably implement some sort of "close()" or "release()" function, which does the deterministic destruction. It's just a destructor with a different name.You could also merge the close() function with ~this() and remove ~this()-called-as-finalizer, which is the position I defend. If I count correctly there are 3 restrictions on the type of resource that can be GC-finalized: - can be destroyed from any thread - do not depend on another resource released by GC finalization, for it's own release https://forum.dlang.org/post/pmulowxpikjjffkrscct forum.dlang.org - can have a delayed finalization That's the reason why I thnk deterministic destruction for non-memory resources is easier than more mixed strategies.
Jul 27 2017
On 7/27/17 4:23 AM, Guillaume Piolat wrote:On Wednesday, 26 July 2017 at 22:29:50 UTC, Steven Schveighoffer wrote:Consider reference counting with cycles. The proposal from Walter/Andrei is to do reference counting to clean up everything but cycles. For cycles, the GC will take care of it. So how do you do this correctly without having two separate mechanisms?Today, the finalizer is essentially a last-effort to clean up resources that would otherwise leak.My point is that this "last chance" to clean up resources should only be used to tell deterministic destruction wasn't acheived (for reasons given in my former post: it's _too hard to explain_ how to do resource management else).This is an unworkable solution. One simple time you forget to clean up deterministically and then you corrupt memory by using members that are already cleaned up. Or, you disable calling destructors in the GC, and instead leak resources. Or I suppose you could crash your application. This would be a fun bug to figure out... -SteveThe reason deterministic destruction sucks today is because once you go 2 levels deep, the only tool available (~this) cannot release any resources (because it's not legal to access GC-allocated members inside ~this).You already know it, but if you commit to not doing anything in the finalizer case, then ~this doesn't suck, it works as a destructor and the restrictions lift. So "suck" is a bit strong.
Jul 27 2017
Consider reference counting with cycles. The proposal from Walter/Andrei is to do reference counting to clean up everything but cycles. For cycles, the GC will take care of it.That is the PHP way to do things. :) It's neat but I still think the only real thing to deal with resources is built-in ownership like Rust does it. But that's a bit out of question for D2 I suppose. What's the state of the RC approach?
Jul 27 2017
For cycle in RC you either do it like other RC system and break cycles manually, or create a parent owner to own every things pointing to each other and having cycles. On Thursday, 27 July 2017 at 11:43:37 UTC, Steven Schveighoffer wrote:This is an unworkable solution.Not at all unworkable, it's much easier than mixed strategies.One simple time you forget to clean up deterministically and then you corrupt memory by using members that are already cleaned up.Once again, this is the reason of the existence of the GC-Proof-resource-class, which have the GC warn you of non-deterministic destruction at debug time. In the very case you mention it will tell you "you have forgot to release one resource T deterministically".Or, you disable calling destructors in the GC, and instead leak resources.As I said in previous messages, not all resources can be destroyed by the GC: they have to fit into 3 different constraints. I'll leave the discussion, you seem to ignore my arguments in what seems as an attempt to have the last word.
Jul 27 2017
On Wednesday, 26 July 2017 at 14:57:14 UTC, Guillaume Piolat wrote:But then no transition path.Does seem a bit like a nightmare... It may facilitate a transition to add a destructor member better?). Then have a deprecation for ~this and tell people to use finalize or destructor, and then add back ~this with the property that the default ~this calls destructor if that's there.
Jul 26 2017
On Wednesday, 26 July 2017 at 15:42:45 UTC, jmh530 wrote:On Wednesday, 26 July 2017 at 14:57:14 UTC, Guillaume Piolat wrote:Well I like this idea best. To split C in A and B, deprecate C and tell what to do instead.But then no transition path.Does seem a bit like a nightmare... It may facilitate a transition to add a destructor member better?). Then have a deprecation for ~this and tell people to use finalize or destructor, and then add back ~this with the property that the default ~this calls destructor if that's there.
Jul 26 2017
On 7/26/17 8:19 AM, Guillaume Piolat wrote:On Wednesday, 26 July 2017 at 02:58:00 UTC, Mike Parker wrote:In D, the difference is when the thing is called. If it's called deterministically, either by function or by the compiler, then you have a lot more flexibility. If it's by the GC, it can be out of order with regards to your members, it could be on a different thread than the one that owned it, etc. Regarding the OP, I think we really should strive to have something to fix this issue. A poor-mans distinction could be done by checking whether the GC is currently the one destroying (a flag is available, but isn't publicly accessible), though that could get expensive. -SteveWhen cleaning up, the GC will ensure that all destructors are run where they exist, followed by all finalizers. And destroy would be changed to call rt_destruct instead of rt_finalize. Thoughts?I don't get the distinction between destructors and "finalizers" but imho the problem is very much that the GC is calling ~this.
Jul 26 2017
On Wednesday, 26 July 2017 at 12:43:27 UTC, Steven Schveighoffer wrote:Regarding the OP, I think we really should strive to have something to fix this issue. A poor-mans distinction could be done by checking whether the GC is currently the one destroying (a flag is available, but isn't publicly accessible), though that could get expensive.That's essentially what Guillaume's "GC-proof resource class" idiom does now. https://p0nce.github.io/d-idioms/#GC-proof-resource-class
Jul 26 2017
On 7/26/17 8:57 AM, Mike Parker wrote:On Wednesday, 26 July 2017 at 12:43:27 UTC, Steven Schveighoffer wrote:Yeah, I've seen that. https://issues.dlang.org/show_bug.cgi?id=17563 -SteveRegarding the OP, I think we really should strive to have something to fix this issue. A poor-mans distinction could be done by checking whether the GC is currently the one destroying (a flag is available, but isn't publicly accessible), though that could get expensive.That's essentially what Guillaume's "GC-proof resource class" idiom does now. https://p0nce.github.io/d-idioms/#GC-proof-resource-class
Jul 26 2017
I don't get it. The GC collects the objects which aren't in use anymore. The order in which this is currently happening is not specified. So, how are the destructors supposed to be called in the right order? Manually? ARC? As far as I understand it, the GC can't do it, otherwise we wouldn't have the "random" order in which the finalizer are called in the first place.
Jul 26 2017
On Wednesday, 26 July 2017 at 17:38:28 UTC, Dgame wrote:I don't get it. The GC collects the objects which aren't in use anymore. The order in which this is currently happening is not specified. So, how are the destructors supposed to be called in the right order? Manually? ARC?After the split: Destructors are only for deterministic end of object lifetime, so yes, they are to be called by any scheme (such as manual management via destroy and reference counting - which is likely also implemented as calling destroy) that is deterministic. Finalizers are for nondeterministic schemes such as the GC. The GC *never* calls destructors directly, only finalizers. A finalizer might manually call a destructor, but a destructor may never call a finalizer.
Jul 26 2017
On Wednesday, 26 July 2017 at 18:33:58 UTC, Moritz Maxeiner wrote:On Wednesday, 26 July 2017 at 17:38:28 UTC, Dgame wrote:Alright, thanks for the clarification. I've briefly hoped for some sort of miracle such as deterministic object lifetime without manual interaction. :)I don't get it. The GC collects the objects which aren't in use anymore. The order in which this is currently happening is not specified. So, how are the destructors supposed to be called in the right order? Manually? ARC?After the split: Destructors are only for deterministic end of object lifetime, so yes, they are to be called by any scheme (such as manual management via destroy and reference counting - which is likely also implemented as calling destroy) that is deterministic. Finalizers are for nondeterministic schemes such as the GC. The GC *never* calls destructors directly, only finalizers. A finalizer might manually call a destructor, but a destructor may never call a finalizer.
Jul 26 2017
On Wednesday, 26 July 2017 at 19:18:48 UTC, Dgame wrote:Alright, thanks for the clarification. I've briefly hoped for some sort of miracle such as deterministic object lifetime without manual interaction. :)I'm not sure what scheme you are trying to describe here, could you give a code example of what you hoped for?
Jul 26 2017
On Wednesday, 26 July 2017 at 20:02:02 UTC, Moritz Maxeiner wrote:On Wednesday, 26 July 2017 at 19:18:48 UTC, Dgame wrote:Built-in Ownership/RC for objects.Alright, thanks for the clarification. I've briefly hoped for some sort of miracle such as deterministic object lifetime without manual interaction. :)I'm not sure what scheme you are trying to describe here, could you give a code example of what you hoped for?
Jul 26 2017
On 7/26/17 2:33 PM, Moritz Maxeiner wrote:On Wednesday, 26 July 2017 at 17:38:28 UTC, Dgame wrote:Actually, it's the opposite. A finalizer can never call anything on its members because it doesn't know if it's being destroyed by the GC. The destructor is ensured that the entire structure is intact, so it can do whatever it wants. -SteveI don't get it. The GC collects the objects which aren't in use anymore. The order in which this is currently happening is not specified. So, how are the destructors supposed to be called in the right order? Manually? ARC?After the split: Destructors are only for deterministic end of object lifetime, so yes, they are to be called by any scheme (such as manual management via destroy and reference counting - which is likely also implemented as calling destroy) that is deterministic. Finalizers are for nondeterministic schemes such as the GC. The GC *never* calls destructors directly, only finalizers. A finalizer might manually call a destructor, but a destructor may never call a finalizer.
Jul 26 2017
On Wednesday, 26 July 2017 at 22:33:23 UTC, Steven Schveighoffer wrote:On 7/26/17 2:33 PM, Moritz Maxeiner wrote:It's a matter of definition and this is the behaviour I would define, becauseOn Wednesday, 26 July 2017 at 17:38:28 UTC, Dgame wrote:Actually, it's the opposite.I don't get it. The GC collects the objects which aren't in use anymore. The order in which this is currently happening is not specified. So, how are the destructors supposed to be called in the right order? Manually? ARC?After the split: Destructors are only for deterministic end of object lifetime, so yes, they are to be called by any scheme (such as manual management via destroy and reference counting - which is likely also implemented as calling destroy) that is deterministic. Finalizers are for nondeterministic schemes such as the GC. The GC *never* calls destructors directly, only finalizers. A finalizer might manually call a destructor, but a destructor may never call a finalizer.A finalizer can never call anything on its members because it doesn't know if it's being destroyed by the GC.This falsely assumes that all members point into the GC pool. A finalizer may freely work on non-pointer members and pointer members that target objects outside the GC pool which the programmer knows to be valid at finalization (e.g. they are manually managed). Whether or not it makes sense for the finalizer to call the destructor is something the programmer has to decide on a per use case basis.The destructor is ensured that the entire structure is intact, so it can do whatever it wants.The point is that I saw (and see) no reason for a destructor to ever call a finalizer.
Jul 26 2017
On Wednesday, 26 July 2017 at 23:28:38 UTC, Moritz Maxeiner wrote:This falsely assumes that all members point into the GC pool. A finalizer may freely work on non-pointer members and pointer members that target objects outside the GC pool which the programmer knows to be valid at finalization (e.g. they are manually managed).* freely with the exception of not allocating using the GC, of course.
Jul 26 2017
On 7/26/17 7:28 PM, Moritz Maxeiner wrote:On Wednesday, 26 July 2017 at 22:33:23 UTC, Steven Schveighoffer wrote:Yes, I should have qualified GC members.On 7/26/17 2:33 PM, Moritz Maxeiner wrote:It's a matter of definition and this is the behaviour I would define, becauseOn Wednesday, 26 July 2017 at 17:38:28 UTC, Dgame wrote:Actually, it's the opposite.I don't get it. The GC collects the objects which aren't in use anymore. The order in which this is currently happening is not specified. So, how are the destructors supposed to be called in the right order? Manually? ARC?After the split: Destructors are only for deterministic end of object lifetime, so yes, they are to be called by any scheme (such as manual management via destroy and reference counting - which is likely also implemented as calling destroy) that is deterministic. Finalizers are for nondeterministic schemes such as the GC. The GC *never* calls destructors directly, only finalizers. A finalizer might manually call a destructor, but a destructor may never call a finalizer.A finalizer can never call anything on its members because it doesn't know if it's being destroyed by the GC.This falsely assumes that all members point into the GC pool.A finalizer may freely work on non-pointer members and pointer members that target objects outside the GC pool which the programmer knows to be valid at finalization (e.g. they are manually managed). Whether or not it makes sense for the finalizer to call the destructor is something the programmer has to decide on a per use case basis.No, because a destructor can safely assume it can look at GC members' data. So a finalizer can never call it.An example: class File { int fd; ubyte[] buffer; // avoiding bikeshed issues by using clear names destructor() { finalizer(); delete buffer; } finalizer() { close(fd); } } No reason to repeat the finalizer code in the destructor. -SteveThe destructor is ensured that the entire structure is intact, so it can do whatever it wants.The point is that I saw (and see) no reason for a destructor to ever call a finalizer.
Jul 26 2017
On Thursday, 27 July 2017 at 00:00:08 UTC, Steven Schveighoffer wrote:On 7/26/17 7:28 PM, Moritz Maxeiner wrote:On Wednesday, 26 July 2017 at 22:33:23 UTC, Steven Schveighoffer wrote:On 7/26/17 2:33 PM, Moritz Maxeiner wrote:No, whether a finalizer can safely call a destructor depends on the destructor's body. If the destructor doesn't access GC members and doesn't allocate using the GC, the finalizer can safely call it. That's why it's the programmer's job to determine this on a use case basis.A finalizer may freely work on non-pointer members and pointer members that target objects outside the GC pool which the programmer knows to be valid at finalization (e.g. they are manually managed). Whether or not it makes sense for the finalizer to call the destructor is something the programmer has to decide on a per use case basis.No, because a destructor can safely assume it can look at GC members' data. So a finalizer can never call it.I concede the point.An example: class File { int fd; ubyte[] buffer; // avoiding bikeshed issues by using clear names destructor() { finalizer(); delete buffer; } finalizer() { close(fd); } } No reason to repeat the finalizer code in the destructor.The destructor is ensured that the entire structure is intact, so it can do whatever it wants.The point is that I saw (and see) no reason for a destructor to ever call a finalizer.
Jul 26 2017
On 7/26/17 8:20 PM, Moritz Maxeiner wrote:On Thursday, 27 July 2017 at 00:00:08 UTC, Steven Schveighoffer wrote:I suppose this is true, but in practice, you wouldn't in a destructor that which is available for a finalizer. In other words, you simply wouldn't implement such a destructor. It's also very fragile as the maintainer of the destructor is working under different assumptions from the finalizer. If you call the destructor from the finalizer, then the rules of the finalizer infect the destructor. -SteveOn 7/26/17 7:28 PM, Moritz Maxeiner wrote:On Wednesday, 26 July 2017 at 22:33:23 UTC, Steven Schveighoffer wrote:On 7/26/17 2:33 PM, Moritz Maxeiner wrote:No, whether a finalizer can safely call a destructor depends on the destructor's body. If the destructor doesn't access GC members and doesn't allocate using the GC, the finalizer can safely call it. That's why it's the programmer's job to determine this on a use case basis.A finalizer may freely work on non-pointer members and pointer members that target objects outside the GC pool which the programmer knows to be valid at finalization (e.g. they are manually managed). Whether or not it makes sense for the finalizer to call the destructor is something the programmer has to decide on a per use case basis.No, because a destructor can safely assume it can look at GC members' data. So a finalizer can never call it.
Jul 26 2017
I might be hated for saying this but I still think the GC problem should not be apart of D language. Make it the user's or third party problem.
Jul 27 2017