May 01, 2014
Removing class destructors would break my DQt library as it currently stands, which wraps C++ classes with D classes, Qt uses classes for polymorphism, for protected overrides and slots. I would have to consider some other means of wrapping C++. It might also be confusing for a few people.

So, having said that... I am completely in favour of removing class destructors. It's a simple principle. When you have to do less work, things can run faster. So I can see removing class destructors as a feature leading to reduced pause times. Destructors exist only for resource management. Doing resource management through GC... is a bad idea.
May 01, 2014
On Thu, May 01, 2014 at 10:06:17AM +0000, monarch_dodra via Digitalmars-d wrote:
> On Wednesday, 30 April 2014 at 23:19:18 UTC, H. S. Teoh via Digitalmars-d wrote:
> >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.
> 
> Why would we prohibit structs with destructors from being class members?  That don't make sense to me.

The proposal was to get rid of class dtors at some point. Which will introduce the problem of what to do when a class member is a struct with a dtor, since that dtor will never run.


> "A class is allowed to have a destructor" <=> "A class can have members with destructors".
> 
> So we either kill off class destructor entirelly (which would mean no
> members with destructors) (But that seems like a bad idea), or we keep
> both.
> 
> Or did I miss something in the argument?

Yes that's the gist of it. But Andrei seems to be convinced that class dtors are a bad idea, so to me, that also implies that class members with dtors are an equally bad (or worse) idea, so therefore they must be gotten rid of too.


T

-- 
All problems are easy in retrospect.
May 01, 2014
On Thu, May 01, 2014 at 01:37:28AM -0700, Jonathan M Davis via Digitalmars-d wrote:
> On Wed, 30 Apr 2014 13:21:33 -0700
> Andrei Alexandrescu via Digitalmars-d <digitalmars-d@puremagic.com>
> wrote:
> 
[...]
> > 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).
> 
> I really don't like the fact that struct destructors are not called by the GC, and if anything, I'd be inclined to argue for finding a way to guarantee that they get run rather than guaranteeing that they never get run. It's just far too easy to have a struct expect that its destructor will be run and then have issues when it's not run. But it would be better to define struct destructors as never getting run rather than having it be undefined as it is now.

+1.


> > We're considering deprecating ~this() for classes in the future.
> 
> While it's not good to rely on finalizers, they're good to have as backup if the appropriate cleanup function doesn't get called like it's supposed to. They're not as critical as they'd be in Java, since we have structs, but I'd be disinclined to remove finalizers from D without a really good reason.
[...]

I'd like to hear an enumeration of those reasons as well.

While in principle I agree with the sentiment to get rid of class dtors, I'm concerned about the rippling side-effects this will have throughout the rest of the language. Such as class members that are structs with dtors, which will mean that the struct dtors will never get called.

Using another poster's argument: if dtors are used for resource management, then it's a bad idea to mix dtors with GC (i.e. classes). So in this sense I agree with getting rid of class dtors. But we have to consider what happens to the case where you stick a struct with a dtor into a class. I'm inclined to say that we should outright prohibit that, because again, the dtor (struct dtor this time) is used for resource management, and therefore shouldn't be mixed with a GC'd resource (i.e., classes). I find it unacceptable that the compiler will silently accept such a usage, and yet have no semantic guarantees (that dtor will run). This is the kind of behaviour I expect from C/C++, but not from D.  Next thing you know, somebody is gonna start wrapping structs inside classes as a way of suppressing the dtor. I don't think that's the kind of thing the language should allow. I contend that it will do the language a great deal of good to outright ban such a kind of usage.

(Structs with no dtors, OTOH, can be safely used as class members,
obviously.)


T

-- 
It only takes one twig to burn down a forest.
May 01, 2014
On 5/1/14, 3:06 AM, monarch_dodra wrote:
>
> "A class is allowed to have a destructor" <=> "A class can have members
> with destructors".

No equivalence, but implication. If a class has at least one member with a destructor, the compiler might need to generate a destructor for the class. -- Andrei
May 01, 2014
On 5/1/14, 3:10 AM, monarch_dodra wrote:
> On Thursday, 1 May 2014 at 01:04:08 UTC, Steven Schveighoffer wrote:
>>> 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?
>
> Yeah, what he said. This has me very worried. Making cycles is actually
> incredibly easy. Any "inteligently" implemented "node-based" data
> structure, implemented with classes, such as a linked list, a tree or a
> graph, is virtually guaranteed to have a cycle somewhere...

In my experience classes with destructors are simpler (files, sockets)... Lists, trees, and graphs tend to be in-memory objects. But of course this is just speculation.

> *Or*
>
> is the idea that reference counting only works for finalization, but the
> memory is still managed by the GC? In that case, the destructors would
> leak, but the memory still be released?
>
> That's still bad, of course, but not "as" bad...

Yah, that would be the case. Essentially we're talking about making the entire GC heap passive.


Andrei
May 01, 2014
On Thu, 01 May 2014 00:07:42 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> On 4/30/14, 7:20 PM, Steven Schveighoffer wrote:
>> On Wed, 30 Apr 2014 21:51:38 -0400, Andrei Alexandrescu
>> <SeeWebsiteForEmail@erdani.org> wrote:
>>
>>> On 4/30/14, 6:04 PM, Steven Schveighoffer wrote:
>>>> destructors are for cleaning up non-GC resources. File handles, malloc'd
>>>> memory, etc. I don't see why these need to be eliminated.
>>>
>>> Virtually all GCs are known to be horrible at managing scarce
>>> resources (including memory itself).
>>
>> The destructor can be a crutch, but it's not good to leave open
>> resources when the user of your code has not cleaned them up manually.
>
> Sounds like defensive programming to me.

Not at all. I've written programs that run for weeks at a time without ever crashing, who continually manage such resources via the GC.

If the GC cleans up your object, but your object wasn't finalized, you would have to make that a hard error. i.e. crash.

Still not talked about that I have seen, is the fact that with RC, we need a solution for cycles. If that's the GC then the GC WILL be cleaning up objects, even if they are ref counted.

>> I can see no reason to disallow destruction from cleaning up resources
>> that nobody else has managed to clean up.
>
> Make memory management faster and better for everyone. I'm not saying it's reason enough, but it's a reason.

It doesn't make it better. The GC still is cleaning up the memory. Having it call close(fd) doesn't delay or make worse anything.

>> The largest problem with D destructors comes from trying to clean up D
>> objects in destructors of structs, that you never expected to be done
>> via the GC.
>>
>> Cleaning up files and malloc'd memory is not an issue.
>
> Not getting this...

class C
{
   private int fd = -1;
   private ubyte[] buffer;
   this(string fname) {
      fd = open(fname, O_CREAT);
      buffer = (cast(ubyte*).malloc(100))[0..100];
   }

   ~this() {
      if(fd != -1) { close(fd); fd = -1;}
      if(buffer.ptr) { .free(buffer.ptr); buffer = null}
   }
}

There is no problem with this code. It works correctly in all cases. It's not inefficient. It's not cumbersome or awkward or confusing. It's exactly what class destructors are for.

However, this *is* a problem:

class C
{
   RCValue s;
}

I have no way to say how s will be destructed, unless I poison it somehow. Even still, s.~this() will be called when C.~this() is done. This can be run in a separate thread as another object that is calling s.~this() at the same time.

Removing ~this() for classes just ADDS complication that we don't need. You are trying to fix the problem for the second situation by introducing a problem for the first that is unnecessary.

Solutions for the second problem are not predicated on jettisoning class dtors. You can, for instance, try and run the dtor for the instance of C in the same thread where it is owned. You could, as you say, force C into a ref counted mechanism. But this doesn't mean you should prevent C from cleaning up its resources!

Please understand that I'm not arguing for the status quo. What I'm arguing is that class destructors DO have some valid uses, and removing them would introduce new problems. I agree we need to find a solution to the ref counting + GC problem.

-Steve
May 01, 2014
On 5/1/14, 5:04 AM, w0rp wrote:
> Removing class destructors would break my DQt library as it currently
> stands, which wraps C++ classes with D classes, Qt uses classes for
> polymorphism, for protected overrides and slots. I would have to
> consider some other means of wrapping C++. It might also be confusing
> for a few people.
>
> So, having said that... I am completely in favour of removing class
> destructors. It's a simple principle. When you have to do less work,
> things can run faster. So I can see removing class destructors as a
> feature leading to reduced pause times. Destructors exist only for
> resource management. Doing resource management through GC... is a bad idea.

I've decided what I'll do. I'll implement both and let the user choose. Push policy up, implementation down! -- Andrei
May 01, 2014
On 5/1/14, 7:30 AM, H. S. Teoh via Digitalmars-d wrote:
> On Thu, May 01, 2014 at 01:37:28AM -0700, Jonathan M Davis via Digitalmars-d wrote:
>> While it's not good to rely on finalizers, they're good to have as
>> backup if the appropriate cleanup function doesn't get called like
>> it's supposed to. They're not as critical as they'd be in Java, since
>> we have structs, but I'd be disinclined to remove finalizers from D
>> without a really good reason.
> [...]
>
> I'd like to hear an enumeration of those reasons as well.

1. Most scarce resources must be released eagerly. GC collections occur relatively rarely and are triggered by different signals (low on memory).

2. The notion of running the GC when one runs out of file handles or sockets is terribly inefficient.

3. Destructors are odd - they will run in a different thread than the one that created the object. They also are limited in surprising ways, i.e. may not allocate memory or block on other threads directly or indirectly.

4. Due to imprecision, there's no actual guarantee any given destructor will end up running. Leaving a scarce resources at the whim of a best-effort approach is poor design.

> While in principle I agree with the sentiment to get rid of class dtors,
> I'm concerned about the rippling side-effects this will have throughout
> the rest of the language. Such as class members that are structs with
> dtors, which will mean that the struct dtors will never get called.
>
> Using another poster's argument: if dtors are used for resource
> management, then it's a bad idea to mix dtors with GC (i.e. classes). So
> in this sense I agree with getting rid of class dtors. But we have to
> consider what happens to the case where you stick a struct with a dtor
> into a class. I'm inclined to say that we should outright prohibit that,

That can't happen.


Andrei
May 01, 2014
On Thursday, 1 May 2014 at 15:28:25 UTC, Andrei Alexandrescu wrote:
> On 5/1/14, 7:30 AM, H. S. Teoh via Digitalmars-d wrote:
>> On Thu, May 01, 2014 at 01:37:28AM -0700, Jonathan M Davis via Digitalmars-d wrote:
>>> While it's not good to rely on finalizers, they're good to have as
>>> backup if the appropriate cleanup function doesn't get called like
>>> it's supposed to. They're not as critical as they'd be in Java, since
>>> we have structs, but I'd be disinclined to remove finalizers from D
>>> without a really good reason.
>> [...]
>>
>> I'd like to hear an enumeration of those reasons as well.
>
> 1. Most scarce resources must be released eagerly. GC collections occur relatively rarely and are triggered by different signals (low on memory).
>
> 2. The notion of running the GC when one runs out of file handles or sockets is terribly inefficient.
>
> 3. Destructors are odd - they will run in a different thread than the one that created the object. They also are limited in surprising ways, i.e. may not allocate memory or block on other threads directly or indirectly.
>
> 4. Due to imprecision, there's no actual guarantee any given destructor will end up running. Leaving a scarce resources at the whim of a best-effort approach is poor design.

These are good arguments against destructors for GC managed objects, IMO.

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.
May 01, 2014
>> into a class. I'm inclined to say that we should outright prohibit that,
>
> That can't happen.

Why is that?

/Per