October 28, 2015
On Wednesday, 28 October 2015 at 00:45:00 UTC, Andrei Alexandrescu wrote:
> ...
>
> We need for D a form of shared ownership that is not backed up by a tracing garbage collector, and one obvious choice is reference counting.
> ...
>

> Here there are two options we discussed - an @rc attribute, or simple
> detection of a pair such as opInc/opDec. I've argued for the latter but Walter made a good argument for @rc: it gives the compiler complete
> control over "as-if" style optimizations.

opInc and opDec are still useful. E.g. one could @disable opInc to obtain unique class references. (opInc and opDec are basically just class reference based versions of this(this) and ~this().)

How does the compiler lose control with opInc/opDec? It could still fuse them.

> Such classes:
>
> * do not inherit Object. (Do we want a root of all @rc classes called RCObject?)
> ...

If we want to be able to elide the vptr, I don't think a common root class can work.

> * cannot be converted to interfaces (we may later add @rc interfaces)
>
> * embed an implementation-defined reference count as a hidden member
> ...

I think this should be configurable.

> * have a vptr only if they define non-final methods (in particular, I think we should disable "synchronized" for them - it seems an unnecessary complication at least for v1)
>
> * should they have a hidden pointer to the parent scope if nested?
> 

Probably not if the parent scope is @rc.

How to specify the allocator for a @rc class?


_______________________________________________
Dlang-study mailing list
Dlang-study@puremagic.com
http://lists.puremagic.com/cgi-bin/mailman/listinfo/dlang-study

October 28, 2015
On 10/28/2015 09:44 AM, Timon Gehr wrote:
> opInc and opDec are still useful. E.g. one could @disable opInc to
> obtain unique class references. (opInc and opDec are basically just
> class reference based versions of this(this) and ~this().)

Noted.

> How does the compiler lose control with opInc/opDec? It could still fuse
> them.

Fusing them relies on the compiler knowing the effects are additive (three successive opInc()s mean += 3 etc) and also that opInc() and opDec() cancel each other. Allowing the user to define them arbitrarily prevents the compiler from doing some of these things, or limits what the user can do drastically enough to just mandate one implementation.

> If we want to be able to elide the vptr, I don't think a common root
> class can work.

Makes sense.

> How to specify the allocator for a @rc class?

I don't know, it's a good topic for discussion. Ideas?


Andrei

_______________________________________________
Dlang-study mailing list
Dlang-study@puremagic.com
http://lists.puremagic.com/cgi-bin/mailman/listinfo/dlang-study

October 28, 2015
On Wednesday, 28 October 2015 at 00:46:34 UTC, Andrei Alexandrescu wrote:
> I should add here another pattern that turned problematic for our older attempts in DIP74:
>
> C c = new C();
> foo(c);
>
> int foo(scope C d) {
>      c = new C();    // c's old instance gets deleted
>      return d.i;        // oops! d is invalid
> }
>

That's because global variables have potentially shared ownership. They need special treatment (for example, in Rust many things you can do with them require unsafe blocks). If `c` were an argument to `foo`, the compiler could catch the problem at the call site.
_______________________________________________
Dlang-study mailing list
Dlang-study@puremagic.com
http://lists.puremagic.com/cgi-bin/mailman/listinfo/dlang-study

October 28, 2015
On 10/28/2015 11:15 AM, Marc Schütz wrote:
> On Wednesday, 28 October 2015 at 00:46:34 UTC, Andrei Alexandrescu wrote:
>> I should add here another pattern that turned problematic for our
>> older attempts in DIP74:
>>
>> C c = new C();
>> foo(c);
>>
>> int foo(scope C d) {
>>      c = new C();    // c's old instance gets deleted
>>      return d.i;        // oops! d is invalid
>> }
>>
>
> That's because global variables have potentially shared ownership. They
> need special treatment (for example, in Rust many things you can do with
> them require unsafe blocks). If `c` were an argument to `foo`, the
> compiler could catch the problem at the call site.

Interesting. Would it be sensible to add a restriction that no @rc object can be reachable from a global? (At least in @safe code.)

Without the restriction, it seems we always need to assume that any object is reachable via a global so we need to add a bunch of incs/decs.


Andrei
_______________________________________________
Dlang-study mailing list
Dlang-study@puremagic.com
http://lists.puremagic.com/cgi-bin/mailman/listinfo/dlang-study

October 28, 2015
On 10/28/2015 03:44 AM, Jacob Carlborg wrote:
> Are there plans to allow the implementor of a @rc class to decide the
> implementation of the reference counting? I'm thinking from the point of
> view of Objective-C interoperability.

Currently everything is flexible, hence this study group.

Andrei
_______________________________________________
Dlang-study mailing list
Dlang-study@puremagic.com
http://lists.puremagic.com/cgi-bin/mailman/listinfo/dlang-study

October 28, 2015
On Wednesday, 28 October 2015 at 15:24:21 UTC, Andrei Alexandrescu wrote:
> On 10/28/2015 11:15 AM, Marc Schütz wrote:
>> On Wednesday, 28 October 2015 at 00:46:34 UTC, Andrei Alexandrescu wrote:
>>> I should add here another pattern that turned problematic for our
>>> older attempts in DIP74:
>>>
>>> C c = new C();
>>> foo(c);
>>>
>>> int foo(scope C d) {
>>>      c = new C();    // c's old instance gets deleted
>>>      return d.i;        // oops! d is invalid
>>> }
>>>
>>
>> That's because global variables have potentially shared ownership. They
>> need special treatment (for example, in Rust many things you can do with
>> them require unsafe blocks). If `c` were an argument to `foo`, the
>> compiler could catch the problem at the call site.
>
> Interesting. Would it be sensible to add a restriction that no @rc object can be reachable from a global? (At least in @safe code.)
>
> Without the restriction, it seems we always need to assume that any object is reachable via a global so we need to add a bunch of incs/decs.

Globals are just a special case, though. Rewriting your example without globals:

void bar() {
    C c = new C();
    foo(c, c);
}

int foo(ref C c, scope C d) {
    c = new C();    // c's old instance gets deleted
    return d.i;        // oops! d is invalid
}

RC elision may only be done if the call doesn't create new mutable aliases to the owner.
_______________________________________________
Dlang-study mailing list
Dlang-study@puremagic.com
http://lists.puremagic.com/cgi-bin/mailman/listinfo/dlang-study

October 28, 2015
On 10/28/2015 12:24 PM, Marc Schütz wrote:
> Globals are just a special case, though. Rewriting your example without
> globals:
>
> void bar() {
>      C c = new C();
>      foo(c, c);
> }
>
> int foo(ref C c, scope C d) {
>      c = new C();    // c's old instance gets deleted
>      return d.i;        // oops! d is invalid
> }

Yah, the thing is direct passing is easily under the control of the compiler. With some made-up notation, the compiler could do something like this:

void bar() {
    C c = new C();
    [[auto __t = c; __t.refs += 2;]]
    foo(c, c);
    [[__t.refs -= 2;]]
}

> RC elision may only be done if the call doesn't create new mutable
> aliases to the owner.

Agreed. There are two complementary techniques that we want to look into:

a) RC elision, as we're already discussing
b) RC fusing, i.e. coalescing several incs/decs into one (a la ARC autorelease pools)

One major outcome I hope from this study group is to leverage the talent and knowledge of this group to devise a robust analysis that achieves (a) and (b).


Andrei
_______________________________________________
Dlang-study mailing list
Dlang-study@puremagic.com
http://lists.puremagic.com/cgi-bin/mailman/listinfo/dlang-study

October 28, 2015
On 10/28/2015 06:21 PM, Andrei Alexandrescu wrote:
> On 10/28/2015 12:24 PM, Marc Schütz wrote:
>> Globals are just a special case, though. Rewriting your example without
>> globals:
>>
>> void bar() {
>>      C c = new C();
>>      foo(c, c);
>> }
>>
>> int foo(ref C c, scope C d) {
>>      c = new C();    // c's old instance gets deleted
>>      return d.i;        // oops! d is invalid
>> }
>
> Yah, the thing is direct passing is easily under the control of the
> compiler. With some made-up notation, the compiler could do something
> like this:
>
> void bar() {
>      C c = new C();
>      [[auto __t = c; __t.refs += 2;]]
>      foo(c, c);
>      [[__t.refs -= 2;]]
> }

+=2 seems arbitrary. Only one new reference is generated.

Anyway, the solution is indeed to make it the responsibility of the caller to keep the borrowed object alive. I.e. when passing a non-scope reference to a scope argument, the reference count must be increased for the duration of the call. The benefit is that scope references can be duplicated arbitrarily without updating the reference count.
This applies to globals just as well as to other cases.

(I haven't read DIP74 though, so I might be misinterpreting your intentions.)

(Note that here, it would be unproblematic to allow assigning scope references to non-scope references given that the reference count is increased, but that's not the meaning of scope.)
_______________________________________________
Dlang-study mailing list
Dlang-study@puremagic.com
http://lists.puremagic.com/cgi-bin/mailman/listinfo/dlang-study

October 28, 2015
On 10/28/2015 04:07 PM, Andrei Alexandrescu wrote:
>
>> How does the compiler lose control with opInc/opDec? It could still fuse
>> them.
>
> Fusing them relies on the compiler knowing the effects are additive
> (three successive opInc()s mean += 3 etc) and also that opInc() and
> opDec() cancel each other. Allowing the user to define them arbitrarily
> prevents the compiler from doing some of these things, or limits what
> the user can do drastically enough to just mandate one implementation.

I disagree that it limits the user drastically enough to just mandate one implementation. E.g. (for whatever reason) a user might want to use reference counting with an allocator that does not allow individual deallocation of objects. (It is then sufficient to have only a single reference count, for the allocator itself.)

>
>> How to specify the allocator for a @rc class?
>
> I don't know, it's a good topic for discussion. Ideas?

It could be a special member that is either an alias or a reference to an allocator which is automatically filled by the typed part of std.allocator. Alternatively, one could make the references fatter and have an additional argument to (an overload of) opDec.
_______________________________________________
Dlang-study mailing list
Dlang-study@puremagic.com
http://lists.puremagic.com/cgi-bin/mailman/listinfo/dlang-study

October 28, 2015
On 10/28/2015 07:24 AM, Michel Fortin wrote:
> True. Note that "each reference" must also counts temporaries. So if you have this:
>
> 	foo(getObject());
>
> what you really have is this:
>
> 	foo(getObject(){-1});
>
> where {-1} is a notation I made up to denote that the counter of the object returned by getObject() is decremented at the end of the statement. (The returned value is already {+1} by the caller.)

Nice notation. There is an amendment I'd need to add (I think I mentioned this already): at least in v1.0 we're considering having the callER do the increment and the callEE do the decrement. This is how D currently handles copy construction and destruction, and it turns out it has a number of advantages over C++ (where the caller does all the work).

So, the protocol in foo(getObject()) is, foo's caller gets an rvalue with the already-incremented refcount and bitblits it into the call to foo, no additional operation. The code generated by foo includes the refcount decrement.

This will make for a simple implementation for v1.0 (uses the same backend as the ctors/dtors), but we need to figure whether that is the optimal approach for manipulating refcounts.

> And if you have this:
>
> 	c = getObject();
>
> it becomes this:
>
> 	c{-1}{+1} = getObject(){-1};

Applying the already-discussed manipulation, the temporary returned by getObject() comes with the already-bumped refcount so all we need to do is decref on c prior to the assignment. Then bitblt.


Andrei
_______________________________________________
Dlang-study mailing list
Dlang-study@puremagic.com
http://lists.puremagic.com/cgi-bin/mailman/listinfo/dlang-study