IntrusivePtr - multiple declarations

Struct IntrusivePtr

Implementation of a ref counted pointer that points to an object with an embedded reference counter ControlBlock.

struct IntrusivePtr(_Type, bool _weakPtr = false) ;

IntrusivePtr retains shared ownership of an object through a pointer.

Several ref counted pointer objects may own the same object.

The object is destroyed and its memory deallocated when either of the following happens:

1. the last remaining ref counted pointer owning the object is destroyed.

2. the last remaining ref counted pointer owning the object is assigned another pointer via various methods like opAssign and store.

The object is destroyed using destructor of type _Type.

A IntrusivePtr can not share ownership of an object while storing a pointer to another object (use SharedPtr for that).

A IntrusivePtr may also own no objects, in which case it is called empty.

_Type must contain one property of type ControlBlock (this property contains ref counting). If this property is shared then ref counting is atomic.

If _Type is const/immutable then ControlBlock cannot be modified => ref counting doesn't work and IntrusivePtr can be only moved.

If multiple threads of execution access the same IntrusivePtr (shared IntrusivePtr) then only some methods can be called (load, store, exchange, compareExchange, useCount).

Template parameters:

_Type type of managed object

_weakPtr if true then IntrusivePtr represent weak ptr

Constructors

NameDescription
this (rhs, ) Forward constructor (merge move and copy constructor).
this (nil) Constructs a IntrusivePtr without managed object. Same as IntrusivePtr.init
this (rhs) Constructs a IntrusivePtr which shares ownership of the object managed by rhs.

Properties

NameTypeDescription
element[get] ElementReferenceTypeImpl!(GetElementType!This)Get pointer to managed object of ElementType or reference if ElementType is reference type (class or interface) or dynamic array
expired[get] boolEquivalent to useCount() == 0 (must be IntrusivePtr.WeakType).
get[get] inout(IntrusivePtr.ElementType)Get reference to managed object of ElementType or value if ElementType is reference type (class or interface) or dynamic array.
toHash[get] size_tGenerate hash
useCount[get] ControlType.SharedReturns the number of different IntrusivePtr instances
weakCount[get] ControlType.WeakReturns the number of different IntrusivePtr.WeakType instances

Methods

NameDescription
alloc (a, args) Constructs an object of type ElementType and wraps it in a IntrusivePtr using args as the parameter list for the constructor of ElementType.
compareExchangeStrong (expected, desired) Compares the IntrusivePtr pointers pointed-to by this and expected.
compareExchangeWeak (expected, desired) Same as compareExchangeStrong but may fail spuriously.
exchange () Stores the non shared IntrusivePtr pointer ptr in the shared(IntrusivePtr) pointed to by this and returns the value formerly pointed-to by this, atomically or with mutex.
load () Returns the non shared IntrusivePtr pointer pointed-to by shared this.
lock () Creates a new non weak IntrusivePtr that shares ownership of the managed object (must be IntrusivePtr.WeakType).
make (args) Constructs an object of type ElementType and wraps it in a IntrusivePtr using args as the parameter list for the constructor of ElementType.
opAssign (nil) Releases the ownership of the managed object, if any.
opAssign (desired) Shares ownership of the object managed by rhs.
opCast () Checks if this stores a non-null pointer, i.e. whether this != null.
opCast () Support for quelifier cast.
opCastImpl () Cast this to different type To when isIntrusivePtr!To.
opCmp (elm) Operators <, <=, >, >= for IntrusivePtr.
opEquals (nil) Operator == and != . Compare pointers.
proxySwap (rhs) Swap this with rhs
weak () Returns weak pointer (must have weak counter).

Aliases

NameDescription
ControlType Type of control block.
DestructorType Type of destructor (void function(void*)@attributes).
ElementReferenceType Same as ElementType* or ElementType if is class/interface/slice.
ElementType Type of element managed by IntrusivePtr.
isLockFree true if shared IntrusivePtr has lock free operations store, load, exchange, compareExchange, otherwise 'false'
isWeak true if IntrusivePtr is weak ptr.
opUnary Operator *, same as method 'get'.
ptr .ptr is same as .element
SharedType Type of non weak ptr.
store Stores the non shared IntrusivePtr parameter ptr to this.
WeakType Weak pointer

Example

static struct Struct{
    ControlBlock!(int, int) control;
    int i;

    this(int i)pure nothrow @safe @nogc{
        this.i = i;
    }
}

static class Base{
    int i;
    ControlBlock!(int, int) control;

    this(int i)pure nothrow @safe @nogc{
        this.i = i;
    }
}
static class Derived : Base{
    double d;

    this(int i, double d)pure nothrow @safe @nogc{
        super(i);
        this.d = d;
    }
}
static class Class : Derived{
    bool b;
    this(int i, double d, bool b)pure nothrow @safe @nogc{
        super(i, d);
        this.b = b;
    }
}

///simple:
{
    IntrusivePtr!Struct a = IntrusivePtr!Struct.make(42);
    assert(a.useCount == 1);

    IntrusivePtr!(const Struct) b = a;
    assert(a.useCount == 2);

    IntrusivePtr!Struct.WeakType w = a.weak;
    assert(a.useCount == 2);
    assert(a.weakCount == 1);

    IntrusivePtr!Struct c = w.lock;
    assert(a.useCount == 3);
    assert(a.weakCount == 1);

    assert(c.get.i == 42);
}

///polymorphism and aliasing:
{
    ///create IntrusivePtr
    IntrusivePtr!Base foo = IntrusivePtr!Derived.make(42, 3.14);
    IntrusivePtr!Class zee = IntrusivePtr!Class.make(42, 3.14, false);

    ///dynamic cast:
    IntrusivePtr!Derived bar = dynCast!Derived(foo);
    assert(bar != null);
    assert(foo.useCount == 2);

    ///this doesnt work because Foo destructor attributes are more restrictive then Class's:
    //IntrusivePtr!Class x = zee;

    ///this does work:
    IntrusivePtr!Base x = zee;
    assert(zee.useCount == 2);
}


///multi threading:
{
    ///create IntrusivePtr with atomic ref counting
    IntrusivePtr!(shared Base) foo = IntrusivePtr!(shared Derived).make(42, 3.14);

    ///this doesnt work:
    //foo.get.i += 1;

    import core.atomic : atomicFetchAdd;
    atomicFetchAdd(foo.get.i, 1);
    assert(foo.get.i == 43);


    ///creating `shared(IntrusivePtr)`:
    shared IntrusivePtr!(shared Derived) bar = share(dynCast!Derived(foo));

    ///`shared(IntrusivePtr)` is lock free (except `load` and `useCount`/`weakCount`).
    static assert(typeof(bar).isLockFree == true);

    ///multi thread operations (`load`, `store`, `exchange` and `compareExchange`):
    IntrusivePtr!(shared Derived) bar2 = bar.load();
    assert(bar2 != null);
    assert(bar2.useCount == 3);

    IntrusivePtr!(shared Derived) bar3 = bar.exchange(null);
    assert(bar3 != null);
    assert(bar3.useCount == 3);
}

Example

//make IntrusivePtr object
static struct Foo{
    ControlBlock!(int, int) c;
    int i;

    this(int i)pure nothrow @safe @nogc{
        this.i = i;
    }
}

{
    auto foo = IntrusivePtr!Foo.make(42);
    auto foo2 = IntrusivePtr!Foo.make!Mallocator(42);  //explicit stateless allocator
}

Example

import std.experimental.allocator : make, dispose, allocatorObject;

static struct Foo{
    ControlBlock!(int, int) c;
    int i;

    this(int i)pure nothrow @safe @nogc{
        this.i = i;
    }

    ~this(){
        if(false)
            auto allocator = allocatorObject(Mallocator.instance);
    }
}

//alloc IntrusivePtr object

auto allocator = allocatorObject(Mallocator.instance);

{
    auto x = IntrusivePtr!Foo.alloc(allocator, 42);
}

Alias IntrusivePtr

Alias to IntrusivePtr with additional template parameters for same interface as SharedPtr and RcPtr

alias IntrusivePtr(_Type, _DestructorType, _ControlType, bool _weakPtr = false) = IntrusivePtr!(_Type,_weakPtr);
alias IntrusivePtr(_Type, _ControlType, _DestructorType, bool _weakPtr = false) = IntrusivePtr!(_Type,_weakPtr);