Thread overview
Reference counting with fat pointers
Mar 30, 2014
Kagamin
Jul 25, 2014
Kagamin
Jul 26, 2014
Marc Schütz
Jul 26, 2014
Kagamin
Jul 26, 2014
Marc Schütz
Jul 26, 2014
Kagamin
Jul 26, 2014
Marc Schütz
Jul 27, 2014
Kagamin
Jun 24, 2020
IGotD-
March 30, 2014
http://wiki.dlang.org/FatPointer
Thoughts on possible implementation of reference counting.
July 25, 2014
Looks like I got the core logic working.

The scheme takes advantage of D type system and distinction between shared and unshared data to decide on interlocked reference counting at compile time. Unshared mutable data and shallow shared data (like strings) are counted with the fastest possible non-atomic arithmetic. Cycles should be handled by the user, see an example at the end. Language interface is not considered, assess if the approach itself is ok.
July 26, 2014
On Friday, 25 July 2014 at 21:01:00 UTC, Kagamin wrote:
> Looks like I got the core logic working.
>
> The scheme takes advantage of D type system and distinction between shared and unshared data to decide on interlocked reference counting at compile time. Unshared mutable data and shallow shared data (like strings) are counted with the fastest possible non-atomic arithmetic. Cycles should be handled by the user, see an example at the end. Language interface is not considered, assess if the approach itself is ok.

Interesting proposal. It seems that you want to change the default for pointers in D, but Walter will probably not like that ;-). But I wonder how much language support this actually needs, much of it seems implementable with templates, which is probably much more acceptable.

Some random comments:

> Scoped: "The reference count is incremented when escaped or copied to a fat pointer."

Could you explain that? A scoped pointer can by definition never be escaped; therefore there should be no need to pass the reference counter along.

> Function parameters: "Caller doesn't increment the counter when passing a pointer as a function argument."

There are some corner cases here. For example when an r-value is moved into a function as an argument. The callee will not decrement the RC of its parameters, with the consequence that the RC never goes to 0. There might be other problems, in particular in the presence of optimizations.

It's probably safer just to keep the traditional behaviour of reference counting, without specific optimizations: Increment on copying, decrement on destruction. Of course this is inefficient, but `scope` can ameliorate that significantly. There needs to be language support for implicit conversion to scope for certain types (maybe as part of implicit conversion in general), and Phobos needs to use scope as much as possible. See also my writeup on borrowing, will add more examples there:
http://wiki.dlang.org/User:Schuetzm/scope
July 26, 2014
On Saturday, 26 July 2014 at 10:49:25 UTC, Marc Schütz wrote:
> Interesting proposal. It seems that you want to change the default for pointers in D, but Walter will probably not like that ;-). But I wonder how much language support this actually needs, much of it seems implementable with templates, which is probably much more acceptable.

What language interface should be provided for RC is up to debate. Getting it working at the binary level is problematic enough, so I focused on it.

> Some random comments:
>
>> Scoped: "The reference count is incremented when escaped or copied to a fat pointer."
>
> Could you explain that? A scoped pointer can by definition never be escaped; therefore there should be no need to pass the reference counter along.

Disallowing escaping of scoped parameters implies strict scoping can work. I was trying to get escaping working. It's easy to deny it if the need be.

>> Function parameters: "Caller doesn't increment the counter when passing a pointer as a function argument."
>
> There are some corner cases here. For example when an r-value is moved into a function as an argument. The callee will not decrement the RC of its parameters, with the consequence that the RC never goes to 0. There might be other problems, in particular in the presence of optimizations.

Ouch, probably there is the same problem with tail call optimization. As of now, I was trying to get sharing and casting work, protocol for function parameters needs more work, will think about conservative counting here.

> See also my writeup on borrowing, will add more examples there:
> http://wiki.dlang.org/User:Schuetzm/scope

Hmm, if you got strict scoping work, that will be interesting.
July 26, 2014
On Saturday, 26 July 2014 at 10:49:25 UTC, Marc Schütz wrote:
> There are some corner cases here. For example when an r-value is moved into a function as an argument. The callee will not decrement the RC of its parameters, with the consequence that the RC never goes to 0. There might be other problems, in particular in the presence of optimizations.

How about this: an r-value is moved to caller's stack, then passed to the callee? This way the callee still won't have to maintain the counter. And this probably prohibits tail call optimization.
July 26, 2014
On Saturday, 26 July 2014 at 13:01:13 UTC, Kagamin wrote:
> On Saturday, 26 July 2014 at 10:49:25 UTC, Marc Schütz wrote:
>> There are some corner cases here. For example when an r-value is moved into a function as an argument. The callee will not decrement the RC of its parameters, with the consequence that the RC never goes to 0. There might be other problems, in particular in the presence of optimizations.
>
> How about this: an r-value is moved to caller's stack, then passed to the callee? This way the callee still won't have to maintain the counter. And this probably prohibits tail call optimization.

Yes. And it needs stack unwinding on exceptions, but this is needed anyway.

On the other hand, if `scope` works and is used wherever possible, all of this probably doesn't matter too much. Most utility functions would just take `scope`, and won't need to pay the costs.
July 26, 2014
On Saturday, 26 July 2014 at 12:43:28 UTC, Kagamin wrote:
> On Saturday, 26 July 2014 at 10:49:25 UTC, Marc Schütz wrote:
>> See also my writeup on borrowing, will add more examples there:
>> http://wiki.dlang.org/User:Schuetzm/scope
>
> Hmm, if you got strict scoping work, that will be interesting.

Well, I'm pretty convinced it is possible, and it isn't even too complex, i.e. it doesn't need flow analysis. I'm just not in a position to implement it. But at least I can try to make a DIP. Walter seems not too opposed to it, he recently mentioned `scope` as one of the open tasks that need to be solved.
July 27, 2014
Added protocol for parameters, COM and Objective-C.

On Saturday, 26 July 2014 at 10:49:25 UTC, Marc Schütz wrote:
> Interesting proposal. It seems that you want to change the default for pointers in D, but Walter will probably not like that ;-). But I wonder how much language support this actually needs, much of it seems implementable with templates, which is probably much more acceptable.

Also added escaping and uniqueness checks. Because RC tracks all references it becomes possible to check if scoped data was escaped or non-unique unshared data was casted to shared. Code written for GC can benefit from these checks too, it would be beneficial, if these checks could be applicable to such code too without modification.
June 24, 2020
On Saturday, 26 July 2014 at 10:49:25 UTC, Marc Schütz wrote:
>
> Interesting proposal. It seems that you want to change the default for pointers in D, but Walter will probably not like that ;-). But I wonder how much language support this actually needs, much of it seems implementable with templates, which is probably much more acceptable.
>

We should not change the default behaviour of the pointers in D. However, we can add a fat pointer type, Let's say

int^ p = new int;

That would have zero breaking changes. I would even take further and allow several types of fat pointers in order to have allow several different types of fat pointers.

int^(RefCounted) = new int;

What happens if.

auto p = new int;

Well, in order not to break anything we have make p an int*.

Fat pointers are obviously needed but nothing happens. Why?