Thread overview
Understanding RefCounted
May 12, 2021
JG
May 12, 2021
JG
May 13, 2021
JG
May 12, 2021

Reading the documentation on RefCounted I get the impression that the following can lead to memory errors. Could someone explain exactly how that could happen? I suppose that problem would be the call something to do with front?

private struct RefCountedRangeReturnType(R)
{
    import std.typecons : RefCounted;
    private RefCounted!R r;
    auto empty() { return r.refCountedPayload.empty; }
    auto front() { return r.refCountedPayload.front; }
    void popFront() { r.refCountedPayload.popFront; }
    auto save() { return typeof(this)(RefCounted!R(r.refCountedPayload.save)); }
}

auto refCountedRange(R)(R r)
{
    import std.typecons : RefCounted;
    return  RefCountedRangeReturnType!R(RefCounted!R(r));
}
May 12, 2021
On 5/12/21 3:28 AM, JG wrote:
> Reading the documentation on RefCounted I get the impression that the following can lead to memory errors. Could someone explain exactly how that could happen? I suppose that problem would be the call something to do with front?
> 
> 
> ```
> private struct RefCountedRangeReturnType(R)
> {
>      import std.typecons : RefCounted;
>      private RefCounted!R r;
>      auto empty() { return r.refCountedPayload.empty; }
>      auto front() { return r.refCountedPayload.front; }
>      void popFront() { r.refCountedPayload.popFront; }
>      auto save() { return typeof(this)(RefCounted!R(r.refCountedPayload.save)); }
> }
> 
> auto refCountedRange(R)(R r)
> {
>      import std.typecons : RefCounted;
>      return  RefCountedRangeReturnType!R(RefCounted!R(r));
> }
> ```

You don't need to access refCountedPayload. RefCounted is supposed to be like a transparent reference type, and should forward all calls to the referenced item.

I don't see how you will get memory errors from your code. Maybe you can elaborate why you think that is?

-Steve
May 12, 2021
On Wednesday, 12 May 2021 at 13:38:10 UTC, Steven Schveighoffer wrote:
> On 5/12/21 3:28 AM, JG wrote:
>> Reading the documentation on RefCounted I get the impression that the following can lead to memory errors. Could someone explain exactly how that could happen? I suppose that problem would be the call something to do with front?
>> 
>> 
>> ```
>> private struct RefCountedRangeReturnType(R)
>> {
>>      import std.typecons : RefCounted;
>>      private RefCounted!R r;
>>      auto empty() { return r.refCountedPayload.empty; }
>>      auto front() { return r.refCountedPayload.front; }
>>      void popFront() { r.refCountedPayload.popFront; }
>>      auto save() { return typeof(this)(RefCounted!R(r.refCountedPayload.save)); }
>> }
>> 
>> auto refCountedRange(R)(R r)
>> {
>>      import std.typecons : RefCounted;
>>      return  RefCountedRangeReturnType!R(RefCounted!R(r));
>> }
>> ```
>
> You don't need to access refCountedPayload. RefCounted is supposed to be like a transparent reference type, and should forward all calls to the referenced item.
>
> I don't see how you will get memory errors from your code. Maybe you can elaborate why you think that is?
>
> -Steve

To be honest I can't see the problem. But the following from the documentation made me wonder if I was doing something that could lead to memory problems:

"RefCounted is unsafe and should be used with care. No references to the payload should be escaped outside the RefCounted object."

In particular I wondered if in some special case holding a reference to front might cause a problem, but perhaps that is incorrect.


May 12, 2021
On 5/12/21 1:16 PM, JG wrote:
> On Wednesday, 12 May 2021 at 13:38:10 UTC, Steven Schveighoffer wrote:
>> On 5/12/21 3:28 AM, JG wrote:
>>> Reading the documentation on RefCounted I get the impression that the following can lead to memory errors. Could someone explain exactly how that could happen? I suppose that problem would be the call something to do with front?
>>>
>>>
>>> ```
>>> private struct RefCountedRangeReturnType(R)
>>> {
>>>      import std.typecons : RefCounted;
>>>      private RefCounted!R r;
>>>      auto empty() { return r.refCountedPayload.empty; }
>>>      auto front() { return r.refCountedPayload.front; }
>>>      void popFront() { r.refCountedPayload.popFront; }
>>>      auto save() { return typeof(this)(RefCounted!R(r.refCountedPayload.save)); }
>>> }
>>>
>>> auto refCountedRange(R)(R r)
>>> {
>>>      import std.typecons : RefCounted;
>>>      return  RefCountedRangeReturnType!R(RefCounted!R(r));
>>> }
>>> ```
>>
>> You don't need to access refCountedPayload. RefCounted is supposed to be like a transparent reference type, and should forward all calls to the referenced item.
>>
>> I don't see how you will get memory errors from your code. Maybe you can elaborate why you think that is?
>>
> 
> To be honest I can't see the problem. But the following from the documentation made me wonder if I was doing something that could lead to memory problems:
> 
> "RefCounted is unsafe and should be used with care. No references to the payload should be escaped outside the RefCounted object."
> 
> In particular I wondered if in some special case holding a reference to front might cause a problem, but perhaps that is incorrect.
> 
> 

Ah, ok. So reference counting provides a single thing you can point at and pass around without worrying about memory cleanup. But only as long as you refer to it strictly through a RefCounted struct. If you keep a pointer to something in the payload that isn't wrapped in a RefCounted struct (and specifically the original RefCounted struct), then it's possible the RefCounted struct will free the memory while you still hold a reference.

You are returning from front by value, so there shouldn't be a problem with lifetime issues. However, there are possible exceptions, but they would be really rare.

As an example of something that would be a bad idea:

```d
struct S
{
   int x;
}

int *bad;
{
   auto rc = S(1).refCounted;
   bad = &rc.x; // escape a reference to the payload
} // here, the scope closes and rc is freed

*bad = 5; // leaving a dangling pointer that can be used
```

-Steve
May 13, 2021
On Thursday, 13 May 2021 at 00:53:50 UTC, Steven Schveighoffer wrote:
> On 5/12/21 1:16 PM, JG wrote:
>> [...]
>
> Ah, ok. So reference counting provides a single thing you can point at and pass around without worrying about memory cleanup. But only as long as you refer to it strictly through a RefCounted struct. If you keep a pointer to something in the payload that isn't wrapped in a RefCounted struct (and specifically the original RefCounted struct), then it's possible the RefCounted struct will free the memory while you still hold a reference.
>
> [...]

Thank you. I was just wondering if something like what you wrote could be achieved using the range above accidentally.