April 30, 2014
On Wed, Apr 30, 2014 at 03:55:38PM -0700, Andrei Alexandrescu via Digitalmars-d wrote:
> On 4/30/14, 3:47 PM, H. S. Teoh via Digitalmars-d wrote:
[...]
> >I don't like the sound of that. I haven't found myself in a place where I needed to do something like this, but if I had to, I'd be very unhappy if struct dtors only work when they're not class members. Can we make them always work, and if necessary prohibit using them as class members?
> 
> Then we're back to effectively class destructors. I think we've gathered quite a bit of evidence there are pernicious issues associated with them. -- Andrei
[...]

How so? If we prohibit structs with dtors from being class members, then it could work. Of course, then you'd have to deal with the backlash from users who inevitably find some use case for it... but at least in theory it's possible.

But if we're going to go in the direction of lack of dtor guarantees, then I might suggest we kill off struct dtors as well, since they also have their own set of pathologies.  Like passing structs with dtors by value (this includes returning them from a function) causing double destruction if the dtor isn't written carefully, or putting structs with dtors into an array or std.container.*, and they stop working as one might expect.  Wasn't this whole fiasco the whole reason std.stdio.ByLine was recently rewritten to use ref counting? It relied on struct dtors before, and look how well that turned out.

Basically, you can't allow something to have dtors, yet have no way to guarantee it will be called at the expected time. That kind of under-specification is the source of endless hard-to-trace bugs, gotchas, unfixable issues, and holes in the type system. If we're going to have dtors at all, let's do it *right*. Guarantee they always work, and reject all usages that break this guarantee (like putting a struct with dtor inside a class, putting it inside a dynamic array in GC memory, etc.). If we can't guarantee anything at all, then let's not have dtors at all.  Trying to stay in the gray zone in between does nothing but cause endless problems down the road. Not to mention language smells. What's the use of a feature that only sometimes works, where "sometimes" is un(der)specified, left to implementation, or depends on undefined behaviour?


T

-- 
In a world without fences, who needs Windows and Gates? -- Christian Surchi
April 30, 2014
 Here is an alternative:

 1. classes are unique by default, they cannot be shared. No GC or RF required, and covers 90% of objects.
 2. To share, retype as GC, the requirement being that it does not have a destructor.





April 30, 2014
On 4/30/14, 4:14 PM, deadalnix wrote:
> On Wednesday, 30 April 2014 at 23:05:29 UTC, Andrei Alexandrescu wrote:
>> Sorry, I misread your code. I meant to say on x86 there's no need to
>> do any handshake on the single-threaded case. -- Andrei
>
> You don't need it on most arch. Except alpha, I don't know any that
> would require it.

So much the better. How about ARM? All I know is it's very relaxed. -- Andrei
April 30, 2014
On 4/30/14, 4:17 PM, H. S. Teoh via Digitalmars-d wrote:
> How so? If we prohibit structs with dtors from being class members,
> then it could work. Of course, then you'd have to deal with the backlash
> from users who inevitably find some use case for it... but at least in
> theory it's possible.

Oh I see. I'd eliminated that ex officio because (a) it's a frequent case so lotsa breakage, and (b) we lack a replacement to recommend. -- Andrei
April 30, 2014
On 4/30/14, 4:17 PM, H. S. Teoh via Digitalmars-d wrote:
> But if we're going to go in the direction of lack of dtor guarantees,
> then I might suggest we kill off struct dtors as well, since they also
> have their own set of pathologies.  Like passing structs with dtors by
> value (this includes returning them from a function) causing double
> destruction if the dtor isn't written carefully,

That sounds like a compiler bug.

> or putting structs with
> dtors into an array or std.container.*,

What's wrong with that? std.container.Array should destroy things properly, if not that's a library bug.

> and they stop working as one
> might expect.  Wasn't this whole fiasco the whole reason
> std.stdio.ByLine was recently rewritten to use ref counting? It relied
> on struct dtors before, and look how well that turned out.

But ref counting IS destructors.

> Basically, you can't allow something to have dtors, yet have no way to
> guarantee it will be called at the expected time.

I don't think so - it's navigating close enough to the "so let's not use cars anymore" fallacy. There are plenty many situations in which dtors are working fabulously well, and throwing them away because we can't guarantee they'll work absolutely well seems bad decision making to me.

> That kind of
> under-specification is the source of endless hard-to-trace bugs,
> gotchas, unfixable issues, and holes in the type system. If we're going
> to have dtors at all, let's do it *right*. Guarantee they always work,
> and reject all usages that break this guarantee (like putting a struct
> with dtor inside a class, putting it inside a dynamic array in GC
> memory, etc.). If we can't guarantee anything at all, then let's not
> have dtors at all.  Trying to stay in the gray zone in between does
> nothing but cause endless problems down the road. Not to mention
> language smells. What's the use of a feature that only sometimes works,
> where "sometimes" is un(der)specified, left to implementation, or
> depends on undefined behaviour?

A lot of other languages have such imperfections, and nobody raises a brow.


Andrei


April 30, 2014
On 4/30/14, 4:19 PM, froglegs wrote:
>   Here is an alternative:
>
>   1. classes are unique by default, they cannot be shared. No GC or RF
> required, and covers 90% of objects.

Where does 90% come from? On the contrary, it seems to me unique references are rather rare and fleeting. -- Andrei

April 30, 2014
On Wednesday, 30 April 2014 at 23:27:49 UTC, Andrei Alexandrescu wrote:
> On 4/30/14, 4:14 PM, deadalnix wrote:
>> On Wednesday, 30 April 2014 at 23:05:29 UTC, Andrei Alexandrescu wrote:
>>> Sorry, I misread your code. I meant to say on x86 there's no need to
>>> do any handshake on the single-threaded case. -- Andrei
>>
>> You don't need it on most arch. Except alpha, I don't know any that
>> would require it.
>
> So much the better. How about ARM? All I know is it's very relaxed. -- Andrei

It is expected that a barrier (dmb on ARM) is inserted when sharing information (in std.concurency for instance). So you should have some initialized value in there. It can be anything though.

That mean that the load can get any value that was there since the last sharing done properly. It doesn't matter which one we get as they all have the same parity.

This value can't be used in the atomic branch. That is why the pseudo-assembly code I posted do not use the counter value in the atomic branch.
April 30, 2014
On Wednesday, 30 April 2014 at 23:34:33 UTC, Andrei Alexandrescu wrote:
> On 4/30/14, 4:19 PM, froglegs wrote:
>>  Here is an alternative:
>>
>>  1. classes are unique by default, they cannot be shared. No GC or RF
>> required, and covers 90% of objects.
>
> Where does 90% come from? On the contrary, it seems to me unique references are rather rare and fleeting. -- Andrei

Yeah, 90% doesn't feel right to me. In C++ I try to use unique_ptr instead of shared_ptr wherever possible but I find I'm often not able to because my references need to be held in several places for efficiency. I'd say I'm more like 90% reference counted, 10% unique throughout my 300kloc codebase.
April 30, 2014
Andrei Alexandrescu:

> Where does 90% come from? On the contrary, it seems to me unique references are rather rare and fleeting. -- Andrei

I think unique references (linear types) are used rather commonly among the reference types of Rust.

Bye,
bearophile
May 01, 2014
On Wed, 30 Apr 2014 16:21:33 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> Walter and I have had a long chat in which we figured our current offering of abstractions could be improved. Here are some thoughts. There's a lot of work ahead of us on that and I wanted to make sure we're getting full community buy-in and backup.
>
> First off, we're considering eliminating destructor calls from within the GC entirely. It makes for a faster and better GC, but the real reason here is that destructors are philosophically bankrupt in a GC environment. I think there's no need to argue that in this community. The GC never guarantees calling destructors even today, so this decision would be just a point in the definition space (albeit an extreme one).

destructors are for cleaning up non-GC resources. File handles, malloc'd memory, etc. I don't see why these need to be eliminated.

What about changing destructors so that they DON'T implicitly call member struct dtors? I'd prefer that, since struct dtors executed on the heap I agree are an issue. One can always call the dtor in the class dtor if necessary. Right now, you have no choice.

> That means classes that need cleanup (either directly or by having fields that are structs with destructors) would need to garner that by other means, such as reference counting or manual. We're considering deprecating ~this() for classes in the future.

So essentially, any class with a dtor needs reference counting? How does one clean up a cycle of them?

> Slices T[] of structs with destructors shall be silently lowered into RCSlice!T, defined inside object.d. That type would occupy THREE words, one of which being a pointer to a reference count. That type would redefine all slice primitives to update the reference count accordingly.

This is a possibility, but I think you would need to prove performance is not severely affected.

> I foresee any number of theoretical and practical issues with this approach. Let's discuss some of them here.

I'd rather see a RC array type, which specifically supports reference counting for opt-in support.

But we are getting WAY ahead of ourselves. A workable RC solution has to be proven first. I'd feel much more comfortable making such a change with a working, tested solution to look at.

-Steve