1 module memutils.refcounted; 2 3 import memutils.allocators; 4 import memutils.helpers; 5 import memutils.utils; 6 import std.traits : hasMember; 7 struct RefCounted(T, ALLOC = ThreadMem) 8 { 9 10 nothrow: 11 @trusted: 12 mixin Embed!(m_object, false); 13 enum NOGC = true; 14 enum isRefCounted = true; 15 alias ThisType = RefCounted!(T, ALLOC); 16 enum ElemSize = AllocSize!T; 17 alias TR = RefTypeOf!T; 18 private TR m_object; 19 private ulong* m_refCount; 20 private void function(void*) m_free; 21 22 pragma(inline) 23 static RefCounted opCall(ARGS...)(auto ref ARGS args) nothrow 24 { 25 //logTrace("RefCounted opCall"); 26 //try { 27 RefCounted!(T, ALLOC) ret; 28 if (!ret.m_object) 29 ret.m_object = ObjectAllocator!(T, ALLOC)().alloc(args); 30 ret.m_refCount = ObjectAllocator!(ulong, ALLOC)().alloc(); 31 (*ret.m_refCount) = 1; 32 return ret; 33 //} catch (Throwable e) { assert(false, "RefCounted.opCall(args) Throw: " ~ e.toString()); } 34 assert(false, "Count not return from opCall"); 35 } 36 37 ~this() 38 { 39 dtor((cast(RefCounted*)&this)); 40 } 41 42 static void dtor(U)(U* ctxt) { 43 //logTrace("Call dtor ", U.stringof, " for ", typeof(this).stringof); 44 static if (!is (U == typeof(this))) { 45 ThisType* this_ = cast(ThisType*)ctxt; 46 this_.m_object = cast(TR) ctxt.m_object; 47 this_.m_refCount = cast(ulong*) ctxt.m_refCount; 48 this_._deinit(); 49 } 50 else { 51 ctxt._clear(); 52 } 53 } 54 55 this(this) 56 { 57 //logTrace("this(this)"); 58 (cast(RefCounted*)&this).copyctor(); 59 } 60 61 //@inline 62 void copyctor() { 63 64 if (!m_object) { 65 defaultInit(); 66 //checkInvariants(); 67 } 68 69 else if (m_object) { 70 //logTrace("copyctr ++", *m_refCount); 71 (*m_refCount)++; 72 } 73 } 74 75 void opAssign(U : RefCounted)(in U other) const nothrow 76 { 77 if (other.m_object is this.m_object) return; 78 static if (is(U == RefCounted)) 79 (cast(RefCounted*)&this).opAssignImpl(other); 80 } 81 82 ref typeof(this) opAssign(U : RefCounted)(in U other) const nothrow 83 { 84 if (other.m_object is this.m_object) return; 85 static if (is(U == RefCounted)) 86 (cast(RefCounted*)&this).opAssignImpl(other); 87 return this; 88 } 89 90 private void opAssignImpl(U)(U other) { 91 _clear(); 92 m_object = other.m_object; 93 m_refCount = other.m_refCount; 94 static if (!is (U == typeof(this))) { 95 static void destr(void* ptr) { 96 U.dtor(cast(ThisType*)ptr); 97 } 98 m_free = &destr; 99 } else 100 m_free = other.m_free; 101 if( m_object ) { 102 (*m_refCount)++; 103 //logTrace("Incr: ", U.stringof, " = ", *m_refCount); 104 } 105 } 106 107 private void _clear() 108 { 109 110 //logTrace("Clear: ", T.stringof, " = ", m_object ? *m_refCount : 9); 111 //checkInvariants(); 112 if( m_object ){ 113 if( --(*m_refCount) == 0 ){ 114 if (m_free) 115 m_free(cast(void*)&this); 116 else { 117 _deinit(); 118 } 119 } 120 } 121 122 m_object = null; 123 m_refCount = null; 124 m_free = null; 125 } 126 127 bool opCast(U : bool)() const nothrow 128 { 129 //try logTrace("RefCounted opcast: bool ", T.stringof); catch {} 130 return !(!m_object || !m_refCount); 131 } 132 133 U opCast(U)() const nothrow 134 if (__traits(hasMember, U, "isRefCounted")) 135 { 136 //static assert(U.sizeof == typeof(this).sizeof, "Error, U: != this: "); 137 138 U ret = U.init; 139 ret.m_object = cast(U.TR)this.m_object; 140 141 static if (!is (U == typeof(this))) { 142 if (!m_free) { 143 static void destr(void* ptr) @trusted { 144 dtor(cast(U*)ptr); 145 } 146 ret.m_free = &destr; 147 } 148 else 149 ret.m_free = m_free; 150 } 151 else ret.m_free = m_free; 152 153 ret.m_refCount = cast(ulong*)this.m_refCount; 154 //logTrace("OpCast++ ", *ret.m_refCount); 155 (*ret.m_refCount) += 1; 156 return ret; 157 } 158 159 U opCast(U : Object)() const nothrow 160 if (!__traits(hasMember, U, "isRefCounted")) 161 { 162 // todo: check this 163 return cast(U) m_object; 164 } 165 166 U opCast(U : TR)() nothrow 167 if (!__traits(hasMember, U, "isRefCounted")) 168 { 169 // todo: check this 170 return m_object; 171 } 172 //@inline 173 private @property ulong refCount() const { 174 if (!m_refCount) return 0; 175 return *m_refCount; 176 } 177 178 private void _deinit() { 179 //logTrace("_deinit"); 180 TR obj_ptr = m_object; 181 //static if (!isPointer!T) // call destructors but not for indirections... 182 // .destroy(m_object); 183 184 if (obj_ptr !is null) 185 ObjectAllocator!(T, ALLOC)().free(obj_ptr); 186 187 ObjectAllocator!(ulong, ALLOC)().free(m_refCount); 188 m_refCount = null; 189 m_object = null; 190 } 191 192 pragma(inline) 193 private void defaultInit(ARGS...)(ARGS args) const { 194 195 if (!m_object) { 196 //logTrace("DefaultInit1"); 197 auto newObj = this.opCall(args); 198 (cast()this).m_object = newObj.m_object; 199 (cast()this).m_refCount = newObj.m_refCount; 200 newObj.m_object = null; 201 newObj.m_refCount = null; 202 } 203 } 204 205 pragma(inline) 206 private void defaultInit() const { 207 208 if (!m_object) { 209 //logTrace("DefaultInit2"); 210 auto newObj = this.opCall(); 211 (cast()this).m_object = newObj.m_object; 212 (cast()this).m_refCount = newObj.m_refCount; 213 newObj.m_object = null; 214 newObj.m_refCount = null; 215 } 216 } 217 218 pragma(inline) 219 private void checkInvariants() 220 const { 221 //logTrace("Check invariants, m_object ", m_object ? '1' : '0', " refcount ", refCount); 222 assert(!m_object || refCount > 0); 223 } 224 }