1 module memutils.refcounted; 2 3 import memutils.allocators; 4 import memutils.helpers; 5 import std.conv : to, emplace; 6 import std.traits : hasIndirections, Unqual, isImplicitlyConvertible; 7 import memutils.utils; 8 import std.algorithm : countUntil; 9 10 struct RefCounted(T, ALLOC = ThreadMem) 11 { 12 import core.memory : GC; 13 mixin Embed!m_object; 14 15 enum isRefCounted = true; 16 17 enum ElemSize = AllocSize!T; 18 alias TR = RefTypeOf!T; 19 private TR m_object; 20 private ulong* m_refCount; 21 private void function(void*) m_free; 22 23 static RefCounted opCall(ARGS...)(auto ref ARGS args) 24 { 25 RefCounted ret; 26 ret.m_object = ObjectAllocator!(T, ALLOC).alloc(args); 27 ret.m_refCount = ObjectAllocator!(ulong, ALLOC).alloc(); 28 (*ret.m_refCount) = 1; 29 logTrace("Allocating: ", cast(void*)ret.m_object, " of ", T.stringof, " sz: ", ElemSize, " allocator: ", ALLOC.stringof); 30 return ret; 31 } 32 33 const ~this() 34 { 35 //logDebug("RefCounted dtor: ", T.stringof); 36 dtor((cast(RefCounted*)&this)); 37 } 38 39 static void dtor(U)(U* ctxt) { 40 static if (!is (U == typeof(this))) { 41 typeof(this)* this_ = cast(typeof(this)*)ctxt; 42 this_.m_object = cast(typeof(this.m_object)) ctxt.m_object; 43 this_._deinit(); 44 } 45 else { 46 ctxt._clear(); 47 } 48 } 49 50 const this(this) 51 { 52 //logDebug("RefCounted copy ctor"); 53 (cast(RefCounted*)&this).copyctor(); 54 } 55 56 void copyctor() { 57 58 if (!m_object) 59 defaultInit(); 60 61 checkInvariants(); 62 if (m_object) (*m_refCount)++; 63 64 } 65 66 void opAssign(U : RefCounted)(in U other) const 67 { 68 if (other.m_object is this.m_object) return; 69 static if (is(U == RefCounted)) 70 (cast(RefCounted*)&this).opAssignImpl(*cast(U*)&other); 71 } 72 73 ref typeof(this) opAssign(U : RefCounted)(in U other) const 74 { 75 if (other.m_object is this.m_object) return; 76 static if (is(U == RefCounted)) 77 (cast(RefCounted*)&this).opAssignImpl(*cast(U*)&other); 78 return this; 79 } 80 81 private void opAssignImpl(U)(U other) { 82 _clear(); 83 m_object = cast(typeof(this.m_object))other.m_object; 84 m_refCount = other.m_refCount; 85 static if (!is (U == typeof(this))) { 86 static void destr(void* ptr) { 87 U.dtor(cast(typeof(&this))ptr); 88 } 89 m_free = &destr; 90 } else 91 m_free = other.m_free; 92 if( m_object ) 93 (*m_refCount)++; 94 } 95 96 private void _clear() 97 { 98 checkInvariants(); 99 if( m_object ){ 100 if( --(*m_refCount) == 0 ){ 101 //logTrace("RefCounted clear: ", T.stringof); 102 logTrace("Clearing Object: ", cast(void*)m_object); 103 if (m_free) 104 m_free(cast(void*)&this); 105 else { 106 _deinit(); 107 } 108 } 109 } 110 111 m_object = null; 112 m_refCount = null; 113 m_free = null; 114 } 115 116 bool opCast(U : bool)() const nothrow 117 { 118 //try logTrace("RefCounted opcast: bool ", T.stringof); catch {} 119 return !(m_object is null && !m_refCount && !m_free); 120 } 121 122 U opCast(U)() const nothrow 123 if (__traits(hasMember, U, "isRefCounted") && (isImplicitlyConvertible!(U.T, T) || isImplicitlyConvertible!(T, U.T))) 124 { 125 //try logTrace("RefCounted opcast: ", T.stringof, " => ", U.stringof); catch {} 126 static assert(U.sizeof == typeof(this).sizeof, "Error, U: "~ U.sizeof.to!string~ " != this: " ~ typeof(this).sizeof.to!string); 127 try { 128 U ret = U.init; 129 ret.m_object = cast(U.TR)this.m_object; 130 131 static if (!is (U == typeof(this))) { 132 if (!m_free) { 133 static void destr(void* ptr) { 134 dtor(cast(U*)ptr); 135 } 136 ret.m_free = &destr; 137 } 138 else 139 ret.m_free = m_free; 140 } 141 else ret.m_free = m_free; 142 143 ret.m_refCount = cast(ulong*)this.m_refCount; 144 (*ret.m_refCount) += 1; 145 return ret; 146 } catch(Throwable e) { try logError("Error in catch: ", e.toString()); catch {} } 147 return U.init; 148 } 149 150 private void _deinit() { 151 //logTrace("Freeing: ", T.stringof, " ptr ", cast(void*) m_object, " sz: ", ElemSize, " allocator: ", ALLOC.stringof); 152 ObjectAllocator!(T, ALLOC).free(m_object); 153 //logTrace("Freeing refCount: ", cast(void*)m_refCount); 154 ObjectAllocator!(ulong, ALLOC).free(m_refCount); 155 m_refCount = null; 156 m_object = null; 157 } 158 159 160 private @property ulong refCount() const { 161 if (!m_refCount) return 0; 162 return *m_refCount; 163 } 164 165 166 private void defaultInit() const { 167 static if (__traits(compiles, { this.opCall(); }())) { 168 if (!m_object) { 169 auto newObj = this.opCall(); 170 (cast(RefCounted*)&this).m_object = newObj.m_object; 171 (cast(RefCounted*)&this).m_refCount = newObj.m_refCount; 172 newObj.m_object = null; 173 } 174 } 175 176 } 177 178 private void checkInvariants() 179 const { 180 assert(!m_object || refCount > 0, (!m_object) ? "No m_object" : "Zero Refcount: " ~ refCount.to!string ~ " for " ~ T.stringof); 181 } 182 }