October 04, 2015
On Sunday, 4 October 2015 at 17:30:39 UTC, rsw0x wrote:
> I still say it's worth investigating a thread-local GC by taking advantage of the fact that shared has never really been properly fleshed out. This would heavily play to D's TLS by default, and preferring message passing over shared data.

That would be ideal but gets really nasty for a number of reasons - primarily having to do with casting. It's perfectly possible to cast to and from shared and much easier to create something as thread-local and then cast it to shared than to create it as shared, so you very quickly run into problems with objects being created on heap but really needing to be used on another. Another major issue is message passing, because in order to avoid copying, you basically need a way to move an object from one thread-local heap to another. Right now the way you'd do it is to cast to immutable to pass the object and cast it back to mutable on the other side (which is _far_ from ideal). Using shared would be better, but the last time I checked, Variant has issues with it so that that didn't work with std.concurrency. Regardless, the fact that you're casting to pass across threads again runs into issues with objects being created on a particular heap but then needing to be moved to another. _Maybe_ that could be resolved by making casting to and from shared do a lot more work underneath the hood, but that get's _really_ complicated when you start having objects that refer to other objects and the like.

So, having a thread-local GC _sounds_ like a great idea, but it's not at all clear that we can actually pull it off given how much you're allowed to do in D. There can be some serious downsides to not placing major restrictions on the user like languages like Java or Go tend to do.

- Jonathan M Davis
October 04, 2015
On Sunday, 4 October 2015 at 21:01:45 UTC, Jonathan M Davis wrote:
> On Sunday, 4 October 2015 at 17:30:39 UTC, rsw0x wrote:
>> I still say it's worth investigating a thread-local GC by taking advantage of the fact that shared has never really been properly fleshed out. This would heavily play to D's TLS by default, and preferring message passing over shared data.
>
> That would be ideal but gets really nasty for a number of reasons - primarily having to do with casting. It's perfectly possible to cast to and from shared and much easier to create something as thread-local and then cast it to shared than to create it as shared, so you very quickly run into problems with objects being created on heap but really needing to be used on another. Another major issue is message passing, because in order to avoid copying, you basically need a way to move an object from one thread-local heap to another. Right now the way you'd do it is to cast to immutable to pass the object and cast it back to mutable on the other side (which is _far_ from ideal). Using shared would be better, but the last time I checked, Variant has issues with it so that that didn't work with std.concurrency. Regardless, the fact that you're casting to pass across threads again runs into issues with objects being created on a particular heap but then needing to be moved to another. _Maybe_ that could be resolved by making casting to and from shared do a lot more work underneath the hood, but that get's _really_ complicated when you start having objects that refer to other objects and the like.
>
> So, having a thread-local GC _sounds_ like a great idea, but it's not at all clear that we can actually pull it off given how much you're allowed to do in D. There can be some serious downsides to not placing major restrictions on the user like languages like Java or Go tend to do.
>
> - Jonathan M Davis

Most of these issues are addressed by fleshing out shared, it was already put as a 'priority' last year by Andrei but nothing was ever done.

If you design a language around having a GC, but do not provide GC friendly mechanisms, the GC will never be good for the language. Java would never have a GC better than a simple mark and sweep GC if it didn't provide help to the GC.
Designing shared around the GC just happens to be a way to do it without affecting the runtime performance of D at all unlike e.g, pointer barriers. If D has no intentions of aiding the GC, then the GC should just be dropped because it's basically just slapping Boehm on C++ right now.

bye.
October 04, 2015
On Sunday, 4 October 2015 at 21:41:00 UTC, rsw0x wrote:
> If D has no intentions of aiding the GC, then the GC should just be dropped because it's basically just slapping Boehm on C++ right now.

I don't understand this attitude at all (and you're not the only one to voice it lately). D has a ton to offer and so little of it has anything to do with the GC. The delegate/lambda/closure situation is generally saner thanks to the GC (at least as far as safety goes), and arrays have some fantastic features thanks to the GC, but D has _way_ more to offer than that, and most of it has nothing to do with the GC. D's templates alone blow C++ totally out of the water. C++ is a great language, and I love it. But at this point, I only use it when I have to. D is just _so_ much more pleasant to program in that I have no interest in programming in C++ anymore. It's been years since I've done any kind of pet project in C++.

- Jonathan M Davis
October 05, 2015
On Sunday, 4 October 2015 at 23:28:48 UTC, Jonathan M Davis wrote:
> D's templates alone blow C++ totally out of the water. - Jonathan M Davis

I have to agree here. This is one feature of D that I can't complain about...at all. I honestly can't think of a single issue I have had with D's templates. Everything just works.

    Bit
October 05, 2015
On 04/10/15 19:14, welkam wrote:
> On Sunday, 4 October 2015 at 12:40:00 UTC, rsw0x wrote:
>> these tools are not very good and they don't help when the standard
>> library(...or builtin language features...) use the GC and tie your hands
>
> IMO tools for memory management in D are way better than that of other
> languages. Game developers who use c++ dont use all of c++
> features(templates, exceptions), because they care about performance the
> same can be said about D. Yes some features use GC heap, but you can
> just not use it. Or you can use so little that GC collection wont even
> kick in. With Go you have no real option but to use GC.

When D structs has a destructor that is guaranteed to run for any instance that finished construction, no matter what is the use case, then we can have that discussion.

Until then, no, D's mechanisms for non-heap allocations are vastly inferior to C++'s.

Shachar
October 05, 2015
On 05-Oct-2015 09:52, Shachar Shemesh wrote:
> On 04/10/15 19:14, welkam wrote:
>> On Sunday, 4 October 2015 at 12:40:00 UTC, rsw0x wrote:
>>> these tools are not very good and they don't help when the standard
>>> library(...or builtin language features...) use the GC and tie your
>>> hands
>>
>> IMO tools for memory management in D are way better than that of other
>> languages. Game developers who use c++ dont use all of c++
>> features(templates, exceptions), because they care about performance the
>> same can be said about D. Yes some features use GC heap, but you can
>> just not use it. Or you can use so little that GC collection wont even
>> kick in. With Go you have no real option but to use GC.
>
> When D structs has a destructor that is guaranteed to run for any
> instance that finished construction, no matter what is the use case,
> then we can have that discussion.
>

Supposed to be the case for structs except for any bugs.

> Until then, no, D's mechanisms for non-heap allocations are vastly
> inferior to C++'s.

Well need some helpers for RC/on stack classes plus kill that horrible extra monitor field.

Otherwise I fail to see how. Plus with D's everything is movable by default. I mean I can memcpy D's structs just fine _by spec_ provided that I bitblit T.init over the source.

In C++ you can't - not only self-references are permitted but also there is no T.init so there is no bit-pattern that is guaranteed to not explode in dtor.


> Shachar


-- 
Dmitry Olshansky
October 05, 2015
On Monday, 5 October 2015 at 07:01:35 UTC, Dmitry Olshansky wrote:
> Supposed to be the case for structs except for any bugs.
>

Not on the heap. There are many cases where the destructor won't run and it is allowed by spec. We should do better.

>> Until then, no, D's mechanisms for non-heap allocations are vastly
>> inferior to C++'s.
>
> Well need some helpers for RC/on stack classes plus kill that horrible extra monitor field.
>
> Otherwise I fail to see how. Plus with D's everything is movable by default. I mean I can memcpy D's structs just fine _by spec_ provided that I bitblit T.init over the source.
>
> In C++ you can't - not only self-references are permitted but also there is no T.init so there is no bit-pattern that is guaranteed to not explode in dtor.
>

Guaranteed construct/destruction is actually a desirable feature IMO. You get all kind of extra case int he dtor to care for when you can't ensure proper construction, and it is not always possible to have a meaningful .init value, leading to many useless runtime checks or an invalid dtor.

October 05, 2015
On 05-Oct-2015 10:40, deadalnix wrote:
>> In C++ you can't - not only self-references are permitted but also
>> there is no T.init so there is no bit-pattern that is guaranteed to
>> not explode in dtor.
>>
>
> Guaranteed construct/destruction is actually a desirable feature IMO.
> You get all kind of extra case int he dtor to care for when you can't
> ensure proper construction, and it is not always possible to have a
> meaningful .init value, leading to many useless runtime checks or an
> invalid dtor.
>

Yes and no, if your type is movable in C++ you have to have a T.init equivalent to leave something behind after move (oops!).

Now if the type is not movable it doesn't really burden much to have an invalid state and trigger an assert in dtor to make sure the type is properly initialized. Then:

T a; // would trigger assert on dtor
T a = T(...); // shouldn't

If all states are valid and you still want to guarantee construction that gets tricky. For example for "seconds" struct if we allow negative seconds as in diff of time. (Though I'd just use long.min for the "wrong" flag in this case).

-- 
Dmitry Olshansky
October 05, 2015
On 05/10/15 10:01, Dmitry Olshansky wrote:

>> When D structs has a destructor that is guaranteed to run for any
>> instance that finished construction, no matter what is the use case,
>> then we can have that discussion.
>>
>
> Supposed to be the case for structs except for any bugs.
>

Check this one out (no instances on heap):
import std.stdio;

struct destructible {
    int id;

    @disable this();
    this( int id ) {
        writeln("Id ", id, " constructed");
        this.id = id;
    }

    ~this() {
        writeln("Id ", id, " destructed");
    }
}

void main() {
    struct container {
        destructible d;

        @disable this();
        this( int id )
        {
            this.d = destructible(id);
            throw new Exception("some random exception");
        }
    }

    try {
        container(2);
    } catch( Exception ex ) {
        writeln("Caught ", ex.msg);
    }
}

As of dmd 2.068.2, the output is:
Id 2 constructed
Caught Some random exception

Of course, if I do not disable this(), things are even worse.
October 05, 2015
On 05/10/15 10:40, deadalnix wrote:

> Guaranteed construct/destruction is actually a desirable feature IMO.
> You get all kind of extra case int he dtor to care for when you can't
> ensure proper construction, and it is not always possible to have a
> meaningful .init value, leading to many useless runtime checks or an
> invalid dtor.
>

What's more, init is used even if you @disable this(). The following compile and does what you'd expect (but not what you want):
struct S {
   int d;

   @disable this();
   this( int d ) {
      this.d = d;
   }
}


...

   S d = S.init;


Shachar