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