Re: Destructors vs. Finalizers

2017-07-27 Thread Guillaume Piolat via Digitalmars-d
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.


Re: Destructors vs. Finalizers

2017-07-27 Thread Dgame via Digitalmars-d
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?




Re: Destructors vs. Finalizers

2017-07-27 Thread Steven Schveighoffer via Digitalmars-d

On 7/27/17 4:23 AM, Guillaume Piolat wrote:

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


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?

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.


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


-Steve


Re: Destructors vs. Finalizers

2017-07-27 Thread Guillaume Piolat via Digitalmars-d
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/pmulowxpikjjffkrs...@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.


Re: Destructors vs. Finalizers

2017-07-27 Thread Swoorup Joshi via Digitalmars-d
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.


Re: Destructors vs. Finalizers

2017-07-26 Thread Steven Schveighoffer via Digitalmars-d

On 7/26/17 8:20 PM, Moritz Maxeiner wrote:

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:


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.


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.




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.


-Steve


Re: Destructors vs. Finalizers

2017-07-26 Thread Moritz Maxeiner via Digitalmars-d
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:


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.


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.






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.


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.


I concede the point.


Re: Destructors vs. Finalizers

2017-07-26 Thread Steven Schveighoffer via Digitalmars-d

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:

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.


Actually, it's the opposite.


It's a matter of definition and this is the behaviour I would define, 
because


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.


Yes, I should have qualified GC members.

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.




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.


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.

-Steve


Re: Destructors vs. Finalizers

2017-07-26 Thread Moritz Maxeiner via Digitalmars-d

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.


Re: Destructors vs. Finalizers

2017-07-26 Thread Moritz Maxeiner via Digitalmars-d
On Wednesday, 26 July 2017 at 22:33:23 UTC, Steven Schveighoffer 
wrote:

On 7/26/17 2:33 PM, Moritz Maxeiner wrote:

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.


Actually, it's the opposite.


It's a matter of definition and this is the behaviour I would 
define, because


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.




Re: Destructors vs. Finalizers

2017-07-26 Thread Steven Schveighoffer via Digitalmars-d

On 7/26/17 2:33 PM, Moritz Maxeiner wrote:

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.


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.


-Steve



Re: Destructors vs. Finalizers

2017-07-26 Thread Steven Schveighoffer via Digitalmars-d

On 7/26/17 12:01 PM, Guillaume Piolat wrote:

On Wednesday, 26 July 2017 at 15:15:03 UTC, Steven Schveighoffer wrote:

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.


The thing to do in a finalizer that is useful is to release any non-GC 
resources.


Interesting case.

Do we assume the finalizer is always called after the destructor?


Yes, finalizer and destructor should be called on deterministic 
destruction (no need to repeat the finalizer code in 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).


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


Re: Destructors vs. Finalizers

2017-07-26 Thread Dgame via Digitalmars-d

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:


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?


Built-in Ownership/RC for objects.


Re: Destructors vs. Finalizers

2017-07-26 Thread Moritz Maxeiner via Digitalmars-d

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?


Re: Destructors vs. Finalizers

2017-07-26 Thread Dgame via Digitalmars-d

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


Alright, thanks for the clarification. I've briefly hoped for 
some sort of miracle such as deterministic object lifetime 
without manual interaction. :)


Re: Destructors vs. Finalizers

2017-07-26 Thread Moritz Maxeiner via Digitalmars-d

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.


Re: Destructors vs. Finalizers

2017-07-26 Thread Dgame via Digitalmars-d
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.


Re: Destructors vs. Finalizers

2017-07-26 Thread Guillaume Piolat via Digitalmars-d
On Wednesday, 26 July 2017 at 15:15:03 UTC, Steven Schveighoffer 
wrote:

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.


The thing to do in a finalizer that is useful is to release any 
non-GC resources.


-Steve


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






Re: Destructors vs. Finalizers

2017-07-26 Thread Guillaume Piolat via Digitalmars-d

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:


But then no transition path.



Does seem a bit like a nightmare...

It may facilitate a transition to add a destructor member 
function as well (C# has dispose as an interface, maybe that's 
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.


Well I like this idea best.
To split C in A and B, deprecate C and tell what to do instead.



Re: Destructors vs. Finalizers

2017-07-26 Thread jmh530 via Digitalmars-d
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 
function as well (C# has dispose as an interface, maybe that's 
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.


Re: Destructors vs. Finalizers

2017-07-26 Thread Steven Schveighoffer via Digitalmars-d

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


Re: Destructors vs. Finalizers

2017-07-26 Thread Guillaume Piolat via Digitalmars-d

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.


Re: Destructors vs. Finalizers

2017-07-26 Thread Daniel Kozak via Digitalmars-d
@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:
>
>> ---
>>> ~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.
>>
>>
>>
> Some other options:
> ~~this() {}
> !this() {}
> !~this() {}
> this!(true) () {} //not really a big fan of this version
>


Re: Destructors vs. Finalizers

2017-07-26 Thread Moritz Maxeiner via Digitalmars-d
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:
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.


Note that Andrei once proposed in 2014 that the GC wouldn't 
call destructors/finalizers at all:


[...]


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.


Re: Destructors vs. Finalizers

2017-07-26 Thread Guillaume Piolat via Digitalmars-d

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:


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.


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

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.


Re: Destructors vs. Finalizers

2017-07-26 Thread Steven Schveighoffer via Digitalmars-d

On 7/26/17 8:57 AM, Mike Parker wrote:

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



Yeah, I've seen that.

https://issues.dlang.org/show_bug.cgi?id=17563

-Steve


Re: Destructors vs. Finalizers

2017-07-26 Thread jmh530 via Digitalmars-d

On Wednesday, 26 July 2017 at 12:55:17 UTC, Mike Parker wrote:

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





Some other options:
~~this() {}
!this() {}
!~this() {}
this!(true) () {} //not really a big fan of this version


Re: Destructors vs. Finalizers

2017-07-26 Thread Mike Parker via Digitalmars-d
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




Re: Destructors vs. Finalizers

2017-07-26 Thread Mike Parker via Digitalmars-d

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.



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:


Indeed! Let's pretend I didn't write that.



Re: Destructors vs. Finalizers

2017-07-26 Thread Steven Schveighoffer via Digitalmars-d

On 7/26/17 8:19 AM, Guillaume Piolat wrote:

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.


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.


-Steve


Re: Destructors vs. Finalizers

2017-07-26 Thread Mike Parker via Digitalmars-d
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.


Re: Destructors vs. Finalizers

2017-07-26 Thread Guillaume Piolat via Digitalmars-d

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.


Re: Destructors vs. Finalizers

2017-07-26 Thread Moritz Maxeiner via Digitalmars-d

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 hierarchy


As 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`.


Re: Destructors vs. Finalizers

2017-07-25 Thread Mike Parker via Digitalmars-d

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.




Destructors vs. Finalizers

2017-07-25 Thread Mike Parker via Digitalmars-d
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?