May 02, 2014
On Friday, 2 May 2014 at 16:20:47 UTC, Andrei Alexandrescu wrote:
> On 5/2/14, 9:04 AM, fra wrote:
>> On Wednesday, 30 April 2014 at 20:21:33 UTC, Andrei Alexandrescu wrote:
>>> 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).
>>
>> I think I (we) need a bit of clarification.
>> Docs in http://dlang.org/class.html#destructors states that: "The
>> garbage collector calls the destructor function when the object is
>> deleted."
>> As far as I understand, this means that destructors are always called
>> when an instance is collected. Is this right?
>> Doesn't this mean that destructors are guaranteed to run for
>> unreferenced objects if we force the GC to do a full collect cycle?
>
> False pointers make it seem like unreferenced objects are in fact referenced, so fewer destructors will run than there should. -- Andrei

Yeah, you have to read the "fine print": "collection implies destruction" *but* "no guarantees the collection will actually ever happen".
May 02, 2014
On Fri, May 02, 2014 at 11:44:47PM +0200, Jonathan M Davis via Digitalmars-d wrote:
> On Fri, 02 May 2014 21:03:15 +0000
> monarch_dodra via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> 
> > On Friday, 2 May 2014 at 15:06:59 UTC, Andrei Alexandrescu wrote:
> > > So now it looks like dynamic arrays also can't contain structs with destructors :o). -- Andrei
> >
> > Well, that's always been the case, and even worst, since in a dynamic array, destructor are guaranteed to *never* be run. Furthermore, given the "append causes relocation which duplicates", you are almost *guaranteed* to leak your destructors. You just can't keep track of the usage of a "naked" dynamic array.
> >
> > This usually comes as a great surprise to users in ".learn". It's also the reason why using "File[]" never ends well...
> 
> Heck, I probably knew that before, but I had completely forgotten. If you'd asked me yesterday whether struct destructors were run in dynamic arrays, I'd have said yes. And if someone like me doesn't remember that, would you expect the average D programmer to? The current situation is just plain bug-prone.
> 
> Honestly, I really think that we need to figure out how to make it so that struct destructors are guaranteed to be run so long as the memory that they're in is collected. Without that, having destructors in structs anywhere other than directly on the stack is pretty much broken.
[...]

Thank you, that's what I've been trying to say. Having dtors sometimes run and sometimes not, is very bug-prone -- if for a seasoned D programmer, then how much more for an average D programmer? We need some kind of guarantees. The current situation sux. I might even say we'd have been better off having no dtors in the first place -- at least then it's consistent, you know you always have to cleanup. But the current situation of being neither here nor there, neither always cleaning up nor never, is not a good place to be in.

I've to say that the more I look at this, the more I don't like this part of the language. :-/


T

-- 
MASM = Mana Ada Sistem, Man!
May 03, 2014
On Friday, 2 May 2014 at 20:59:46 UTC, monarch_dodra wrote:
> Yeah, you have to read the "fine print": "collection implies destruction" *but* "no guarantees the collection will actually ever happen".

That sound like the right balance.

Also, make construction of object with destructor @system as there is no way to ensure destructor won't resurrect the object or do some goofy thing with finalized reference it has.
May 03, 2014
On Friday, 2 May 2014 at 15:03:47 UTC, Andrei Alexandrescu wrote:
> On 5/2/14, 1:34 AM, Paolo Invernizzi wrote:
>> On Thursday, 1 May 2014 at 21:29:19 UTC, Andrei Alexandrescu wrote:
>>> On 5/1/14, 1:19 PM, H. S. Teoh via Digitalmars-d wrote:
>>>> On Thu, May 01, 2014 at 01:03:06PM -0700, Andrei Alexandrescu via
>>>> Digitalmars-d wrote:
>>>>> On 5/1/14, 12:52 PM, "Nordlöw" wrote:
>>>>>>>> into a class. I'm inclined to say that we should outright
>>>>>>>> prohibit that,
>>>>>>>
>>>>>>> That can't happen.
>>>>>>
>>>>>> Why is that?
>>>>>
>>>>> (1) Too much breakage, (2) would disallow a ton of correct code, (3)
>>>>> no reasonable alternative to propose. We'd essentially hang our users
>>>>> out to dry. -- Andrei
>>>>
>>>> Isn't this what we're already doing by (eventually) getting rid of class
>>>> dtors?
>>>
>>> Not even close. (1) A lot less breakage, (2) disallowed code was
>>> already not guaranteed to work, (3) reasonable alternatives exist.
>>>
>>> Andrei
>>
>> I have 165k lines of code to review for that change... I would not call
>> it a minor breakage...
>
> I didn't. I said a lot less that straight out disallowing struct members. -- Andrei

I would also add one point, just because it is not so obvious: I'll be more than happy to review my company code, if the proposed solutions about finalisation in class/struct turn out to take D one step forward being a better programming language.

/Paolo
May 03, 2014
Am 30.04.2014 22:21, schrieb Andrei Alexandrescu:
> 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).
>
> 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.
>
> Also, we're considering a revamp of built-in slices, as follows. Slices
> of types without destructors stay as they are.
>
> 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.
>
> RCSlice!T will not convert implicitly to void[]. Explicit cast(void[])
> will be allowed, and will ignore the reference count (so if a void[]
> extracted from a T[] via a cast outlives all slices, dangling pointers
> will ensue).
>
> I foresee any number of theoretical and practical issues with this
> approach. Let's discuss some of them here.
>
>
> Thanks,
>
> Andrei

Honestly, that sounds like the entierly wrong apporach to me. Your approaching the problem in this way:

"We can not implement a propper GC in D because the language design prevents us from doing so. So lets remove destructors to migate the issue of false pointers."

While the approach should be.

"The language does not allow to implement a propper GC (anything else then dirty mark & sweep), what needs to be changed to allow a implementation of a more sophisticated GC."

Also let me tell you that at work we have a large C# codebase which heavily relies on resource management. So basically every class in there inherits from C#'s IDisposable interface which is used to manually call the finalizer on the class (but the C# GC will also call that finalizer!). Basically the entire codebase feels like manual memory management. You have to think about manually destroying every class and the entire advantage of having a GC, e.g. not having to think about memory management and thus beeing more productive, vanished. It really feels like writing C++ with C# syntax. Do we really want that for D?

And what if I want unsafe slices of structs with destructors, for performance? Maybe I perfectly know that the memory behind the slice will outlive the slice, and I don't want the overhead of all the reference counthing behind it?

If you actually deprecate ~this, there would be two options for me.
1) Migrate my entire codebase to some user defiend finalizer function (which doesn't have compiler support), which would be a lot of work.
2) Quit D. (which is becomeing more and more an option when reading the recent news group discussions.)

-- 
Kind Regards
Benjamin Thaut
May 03, 2014
> feels like writing C++ with C# syntax.

 Ahem. C++ has RAII k thanks.  C++ > C# Proof.
May 03, 2014
On Friday, 2 May 2014 at 00:45:42 UTC, Andrei Alexandrescu wrote:
> Here's where the point derails. A struct may be preexisting; the decision to define a destructor for it and the decision to use polymorphism for an object that needs that structure are most of the time distinct.
>
> Andrei

I wonder how common the pattern of putting a struct with a destructor in a class actually is. It might be a case for defining data structures with GC allocation rather than reference counting (as in std.container). I suppose a choice of allocator will change this quite a lot. That which is allocated with reference counting could uniquely hold its container member and then call the destructor when it dies, or similar.

I find it kind of a funny thing to put something like a File inside of a class. I have always seen the mix of GC and resource management as more managing resources like Files in scopes and reading data from the resources which turn into objects in memory which are garbage collected. Not allocating garbage collected objects which contain resources.
May 03, 2014
Am 01.05.2014 19:35, schrieb "Marc Schütz" <schuetzm@gmx.net>":
>
> But conceptually, this is _not_ the same as classes! As others have
> mentioned, it's possible to created structs with `new`, or have them in
> dynamic arrays, as well as managing class objects manually.
>
> Maybe the language should have some way to distinguish between
> GC-managed and manually-managed objects, preferably in the type system.
> Then it could be statically checked whether an object is supposed to be
> GC-managed, and consequentially shouldn't have a destructor.
>
> The difference between classes and structs should then be reference vs.
> value semantics, and polymorphic vs static (which correlate nicely). It
> should, however, not imply whether the object is managed by the GC or
> not. Some kind of ownership mechanism would be more suited for that.

+1
May 03, 2014
On Friday, 2 May 2014 at 20:59:46 UTC, monarch_dodra wrote:
> Yeah, you have to read the "fine print": "collection implies destruction" *but* "no guarantees the collection will actually ever happen".

Which make destructors dangerous constructs. It means you now risk getting random bugs-reports after deployment. Acceptable for a hobby language, not acceptable for a systems programming language.
May 03, 2014
On 2014-05-01 17:35:36 +0000, "Marc Schütz" <schuetzm@gmx.net> said:

> Maybe the language should have some way to distinguish between GC-managed and manually-managed objects, preferably in the type system. Then it could be statically checked whether an object is supposed to be GC-managed, and consequentially shouldn't have a destructor.

Or turn the rule on its head: make it so having a destructor makes the heap memory block reference counted. With this adding a destructor always cause deterministic destruction.

The compiler knows statically whether a struct has a destructor. For a class you need a runtime trick because the root object which can be either. Use a virtual call or a magic value in the reference count field to handle the reference count management. You also need a way to tag a class to be guarantied it has no derived class with a destructor (to provide a static proof for the compiler it can omit ARC code), perhaps @disable ~this().

Then remains the problem of cycles. It could be a hard error if the destructor is @safe (error thrown when the GC collects it). The destructor could be allowed to run (in any thread) if the destructor is @system or @trusted.

The interesting thing with this is that the current D semantics are preserved, destructors become deterministic (except in the presence of cycles, which the GC will detect for you), and if you're manipulating pointers to pure memory (memory blocks having no destructor) there's no ARC overhead. And finally, no new pointer attributes; Walter will like this last one.

-- 
Michel Fortin
michel.fortin@michelf.ca
http://michelf.ca

3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19