Disadvantages of ARC

Adam D. Ruppe destructionator at gmail.com
Thu Feb 6 12:23:48 PST 2014


On Thursday, 6 February 2014 at 19:33:39 UTC, Andrei Alexandrescu 
wrote:
> So if T is int[] and you have taken a slice into it...?

If you escape it, congratulations, you have a memory safety bug. 
Have fun tracking it down.

You could also offer refcounted slicing, of course (wrapping the 
Unique thing in a refcounter would work), or you could be 
converted to the church of scope where the compiler will help you 
catch these bugs without run time cost.

> I'm not sure I understand what you are talking about.

When the reference count reaches zero, what happens? This changes 
based on the allocation method: you might call GC.free, you might 
call free(), you might do nothing, The destructor needs to know, 
otherwise the refcounting achieves exactly nothing! We can encode 
this in the type or use a function pointer for it.

struct RefCounted(T) {
     private struct Impl {
         // potential double indirection lol
         private T payload;
         private size_t count;
         private void function(T) free;
         T getPayload() { return payload; } // so it is an lvalue
         alias getPayload this;
     }
     Impl* impl;
     alias impl this;
     this(T t, void function(T) free) {
        impl = new Impl; // some kind allocation at startup lol
                         // naturally, this could also be malloc
                         // or take a generic allocator form the 
user
                         // but refcounted definitely needs some 
pointer love
        impl.payload = t;
        impl.count = 1;
        impl.free = free; // gotta store this so we can free later
     }
     this(this) {
        impl.count++;
     }
     ~this() {
        impl.count--;
        if(impl.count == 0) {
            // how do we know how to free it?
            impl.free(impl.payload);

            // delete impl; GC.free(impl) 
core.stdc.stdlib.free(impl);
            // whatever
            impl = null;
        }
     }
}


In this example, we take the reference we're counting in the 
constructor... which means it is already allocated. So logically, 
the user code should tell it how to deallocate it too. We can't 
just call a global free, we take a pointer instead.


So this would work kinda like this:

import core.stdc.stdlib;
int[] stuff = malloc(int.sizeof * 5)[0 .. 5];
auto counted = RefCounted!(int[])(stuff, (int[] stuff) { 
free(stuff.ptr); });



The allocator is not encoded in the type, but ref counted does 
need to know what happens when the final reference is gone. It 
takes a function pointer from the user for that.


This is a generically refcounting type. It isn't maximally 
efficient but it also works with arbitrary inputs allocated by 
any means.

Unique!T could do something similar, but unique would disable its 
postblit instead of incrementing a refcount.


More information about the Digitalmars-d mailing list