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
Name | Description |
---|---|
this
(rhs, )
|
Forward constructor (merge move and copy constructor). |
this
(nil)
|
Constructs a IntrusivePtr without managed object. Same as IntrusivePtr
|
this
(rhs)
|
Constructs a IntrusivePtr which shares ownership of the object managed by rhs .
|
Properties
Name | Type | Description |
---|---|---|
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]
|
bool | Equivalent to useCount() == 0 (must be IntrusivePtr ).
|
get [get]
|
inout(IntrusivePtr | Get reference to managed object of ElementType or value if ElementType is reference type (class or interface) or dynamic array.
|
toHash [get]
|
size_t | Generate hash |
useCount [get]
|
ControlType | Returns the number of different IntrusivePtr instances
|
weakCount [get]
|
ControlType | Returns the number of different IntrusivePtr instances
|
Methods
Name | Description |
---|---|
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 ).
|
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
Name | Description |
---|---|
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
|
is same as
|
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);