Thread overview
Reference counting questions
Jul 22, 2011
Johannes Pfau
Jul 22, 2011
Jesse Phillips
Jul 23, 2011
Johannes Pfau
Jul 23, 2011
Johannes Pfau
Jul 24, 2011
Andrej Mitrovic
Jul 25, 2011
Johannes Pfau
July 22, 2011
I have some problems understanding the reference counting code in
std.stdio. The reduced code my questions refer to is here:
https://gist.github.com/1099229
(ignore the not-working struct default constructor, that's just to
simplify the code)

in line 5: why is 'refs' initialized to 'uint.max / 2'?

in line 40: what does 'swap(p, rhs.p);' do? Does it decrease the reference count of the 'original' File and increase the reference count of the 'new' File? Or does the compiler automatically call the copy constructor and the destructor when it calls opAssign?

There was some discussion some time ago whether atomic ops have to be used for ref-counting (something about finalizers of heap allocated structs?). What happened to that issue?

How's ref-counting supposed to behave with global variables?
--------------------------
module test;
File file;

void main()
{
    file = File("/blah");
    test();
}

void test()
{
    file.read(); //<-- will/should this work?
}
--------------------------

What happens if a ref-counted struct is used as a member in a class? Will the GC decrease the struct's reference count when the class gets collected?

Is it possible to add explicit de-referencing to allow a global struct / struct in class to be manually dereferenced? Is detach meant to do that? Would this naive implementation work? https://gist.github.com/1099360

Maybe with the detach addition the copy-ctor and opAssign should throw on p == null? So that copies of a detached object cannot be made?
-- 
Johannes Pfau

July 22, 2011
I don't really think stdio is the place to see modern D ref counting. The container class has an Array which is built to use RefCounted. I had tried my and at explaining how to use it: http://stackoverflow.com/ questions/4632355/making-a-reference-counted-object-in-d-using- refcountedt/4635050#4635050

I'm not really sure if all the details have been worked out. Hopefully I explained it correctly and that it gives you an idea of how to make ref counting work.
July 23, 2011
Jesse Phillips wrote:
>I don't really think stdio is the place to see modern D ref counting. The container class has an Array which is built to use RefCounted. I had tried my and at explaining how to use it: http://stackoverflow.com/ questions/4632355/making-a-reference-counted-object-in-d-using- refcountedt/4635050#4635050
>
>I'm not really sure if all the details have been worked out. Hopefully I explained it correctly and that it gives you an idea of how to make ref counting work.

Thanks, I think I understand how to use RefCounted now.
But it still leaves this issue:
Is it possible to add explicit de-referencing to allow a global
struct / struct in class to be manually dereferenced?

And it adds a new one:
RefCounted calls addRange
https://github.com/D-Programming-Language/phobos/blob/master/std/typecons.d#L2304
if the _store size is >= size_t (In fact, as _store already has a size_t
for count, addRange will always get called?). This means even if I only
want RefCounting for a simple C handle, addRange will be called.

I don't understand the use of malloc & free in
RefCounted. If addRange is called anyway, why not just use GC.malloc
and GC.free?

One more thing: The removeRange call uses a different condition:

if (hasIndirections!T && RefCounted._store)
    GC.removeRange(RefCounted._store);

if (sz >= size_t.sizeof && p.ptr)
    GC.addRange(p.ptr, sz);

Now say, I store a  single byte, wouldn't addRange get called but removeRange not?
-- 
Johannes Pfau

July 23, 2011
Jesse Phillips wrote:
>I don't really think stdio is the place to see modern D ref counting. The container class has an Array which is built to use RefCounted. I had tried my and at explaining how to use it: http://stackoverflow.com/ questions/4632355/making-a-reference-counted-object-in-d-using- refcountedt/4635050#4635050
>
>I'm not really sure if all the details have been worked out. Hopefully I explained it correctly and that it gives you an idea of how to make ref counting work.

Totally overlooked a 'small' problem with RefCounted: It can't work for cairoD ;-) In cairo the c handle is already reference-counted, so I should use cairo_*_[de]reference(ptr) instead of my own counter, but that can't be done with RefCounted. I think I'll have to adapt the RefCounted implementation for cairoD.

But I can still use RefCounted in other projects.

BTW: RefCounted also uses std.algorithm.swap in it's opAssign. Still wondering what that's supposed to do.
-- 
Johannes Pfau

July 24, 2011
I'm don't understand why this code calls the dtor before it calls the ctor:

import std.stdio;
import std.typecons;

void main()
{
    auto wrap1 = Wrapper(1);
}

void free(ref int _payload)
{
    writeln("dealloc");
    _payload = 0;
}

struct Wrapper
{
    struct Payload
    {
        int _payload;
        this(int h) { writeln("alloc"); _payload = h; }
        ~this() { free(_payload); }

        this(this) { assert(false); }
        void opAssign(Wrapper.Payload rhs) { assert(false); }
    }

    private alias RefCounted!(Payload, RefCountedAutoInitialize.no) Data;
    private Data _data;

    this(int h) { _data = Data(h); }
}

prints:
dealloc
alloc
dealloc

What's with the first dtor call?
July 25, 2011
Andrej Mitrovic wrote:
>I'm don't understand why this code calls the dtor before it calls the ctor:
>
>import std.stdio;
>import std.typecons;
>
>void main()
>{
>    auto wrap1 = Wrapper(1);
>}
>
>void free(ref int _payload)
>{
>    writeln("dealloc");
>    _payload = 0;
>}
>
>struct Wrapper
>{
>    struct Payload
>    {
>        int _payload;
>        this(int h) { writeln("alloc"); _payload = h; }
>        ~this() { free(_payload); }
>
>        this(this) { assert(false); }
>        void opAssign(Wrapper.Payload rhs) { assert(false); }
>    }
>
>    private alias RefCounted!(Payload, RefCountedAutoInitialize.no)
> Data; private Data _data;
>
>    this(int h) { _data = Data(h); }
>}
>
>prints:
>dealloc
>alloc
>dealloc
>
>What's with the first dtor call?

Strange, I can confirm this behavior and it seems to happen all the time when using RefCounted.

I guess this was never an issue for the RefCounted code in phobos (for example std.container.Array) as RefCounted was only used to manage memory, not handles from C libraries. free simply does nothing if called with a null pointer, so this problem never showed up.

As a workaround, check your handle against null in the destructor before calling the function to close the handle.

-- 
Johannes Pfau