1 /** 2 Taken from Phobos, tweaked for convenience 3 4 Copyright: Copyright the respective authors, 2008- 5 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 6 Authors: $(WEB erdani.org, Andrei Alexandrescu), 7 $(WEB bartoszmilewski.wordpress.com, Bartosz Milewski), 8 Don Clugston, 9 Shin Fujishiro, 10 Kenji Hara 11 */ 12 13 module memutils.unique; 14 15 import memutils.allocators; 16 import memutils.constants; 17 import memutils.utils; 18 import memutils.rbtree; 19 20 import memutils.helpers; 21 import std.conv : to; 22 23 static if (__VERSION__ >= 2071) { 24 extern (C) bool gc_inFinalizer(); 25 enum HasGCCheck = true; 26 } 27 else version(GCCheck) { 28 extern(C) bool gc_inFinalizer(); 29 enum HasGCCheck = true; 30 } else enum HasGCCheck = false; 31 enum DebugUnique = true; 32 33 // TODO: Move release() into Embed!, and add a releaseCheck() for refCounted (cannot release > 1 reference) 34 struct Unique(T, ALLOC = void) 35 { 36 alias TR = RefTypeOf!T; 37 private TR m_object; 38 39 mixin Embed!(m_object, false); 40 static if (!is(ALLOC == AppMem)) enum NOGC = true; 41 enum isRefCounted = false; 42 enum isUnique = true; 43 44 enum ElemSize = AllocSize!T; 45 46 public: 47 /** 48 Constructor that takes an rvalue. 49 It will ensure uniqueness, as long as the rvalue 50 isn't just a view on an lvalue (e.g., a cast). 51 Typical usage: 52 ---- 53 Unique!Foo f = new Foo; 54 ---- 55 */ 56 this(inout TR p) 57 { 58 opAssign(cast(TR)p); 59 } 60 /** 61 Constructor that takes an lvalue. It nulls its source. 62 The nulling will ensure uniqueness as long as there 63 are no previous aliases to the source. 64 */ 65 this(ref TR p) 66 { 67 opAssign(p); 68 } 69 70 /** 71 Constructor that takes a $(D Unique) of a type that is convertible to our type. 72 73 Typically used to transfer a $(D Unique) rvalue of derived type to 74 a $(D Unique) of base type. 75 Example: 76 --- 77 class C : Object {} 78 79 Unique!C uc = new C; 80 Unique!Object uo = uc.release; 81 --- 82 */ 83 this(U)(Unique!U u) 84 if (is(u.TR:TR)) 85 { 86 // logTrace("Unique constructor converting from ", U.stringof); 87 opAssign(u.m_object); 88 u.m_object = null; 89 } 90 91 void free() 92 { 93 TR p = null; 94 opAssign(p); 95 } 96 97 void opAssign()(auto ref TR p) 98 { 99 if (m_object) destroy(this); 100 if (!p) return; 101 //logTrace("Unique ctor of ", T.stringof, " : ", ptr.to!string); 102 static if (HasDebugAllocations && DebugUnique) { 103 mtx.lock(); scope(exit) mtx.unlock(); 104 ptree._defaultInitialize(); 105 if(cast(void*)p in ptree) 106 { 107 assert(false, "Already owned pointer: " ~ (cast(void*)p).to!string ~ " of type " ~ T.stringof); 108 } 109 ptree.insert(cast(void*)p); 110 } 111 m_object = p; 112 p = null; 113 } 114 /* 115 void opAssign(U)(in Unique!U p) 116 { 117 debug(Unique) logTrace("Unique opAssign converting from ", U.stringof); 118 // first delete any resource we own 119 destroy(this); 120 m_object = cast(TR)u.m_object; 121 cast(TR)u.m_object = null; 122 }*/ 123 124 /// Transfer ownership from a $(D Unique) of a type that is convertible to our type. 125 void opAssign(U)(Unique!U u) 126 if (is(u.TR:TR)) 127 { 128 opAssign(u.m_object); 129 u.m_object = null; 130 } 131 132 ~this() 133 { 134 //logDebug("Unique destructor of ", T.stringof, " : ", ptr); 135 import core.stdc.string : memset; 136 137 138 static if (ALLOC.stringof != "void") { 139 if (m_object) { 140 //logTrace("ptr in ptree: ", ptr in ptree); 141 142 static if (HasDebugAllocations && DebugUnique) { 143 mtx.lock(); scope(exit) mtx.unlock(); 144 ptree._defaultInitialize(); 145 assert(ptr in ptree); 146 ptree.remove(ptr); 147 } 148 149 ObjectAllocator!(T, ALLOC).free(m_object); 150 m_object = null; 151 152 //static if (HasDebugAllocations && DebugUnique) 153 // debug memset(ptr, 0, AllocSize!T); 154 } 155 } 156 else { 157 static if (HasGCCheck) if (!gc_inFinalizer()) { 158 if (m_object) { 159 //logTrace("ptr in ptree: ", ptr in ptree); 160 161 static if (HasDebugAllocations && DebugUnique) { 162 mtx.lock(); scope(exit) mtx.unlock(); 163 ptree._defaultInitialize(); 164 if (ptr !in ptree){ 165 logDebug("Unknown pointer: " ~ ptr.to!string ~ " of type " ~ T.stringof); 166 assert(false); 167 } 168 ptree.remove(ptr); 169 } 170 171 static if (is(TR == T*)) .destroy(*m_object); 172 else .destroy(m_object); 173 m_object = null; 174 } 175 } 176 } 177 } 178 /** Returns whether the resource exists. */ 179 @property bool isEmpty() const 180 { 181 return m_object is null; 182 } 183 184 /** Transfer ownership to a $(D Unique) rvalue. Nullifies the current contents. */ 185 TR release() 186 { 187 //logTrace("Release"); 188 if (!m_object) return null; 189 auto ret = m_object; 190 drop(); 191 return ret; 192 } 193 194 void drop() 195 { 196 //logTrace("Drop"); 197 if (!m_object) return; 198 static if (HasDebugAllocations && DebugUnique) { 199 mtx.lock(); scope(exit) mtx.unlock(); 200 ptree._defaultInitialize(); 201 assert(ptr in ptree); 202 ptree.remove(ptr); 203 } 204 m_object = null; 205 } 206 207 TR opUnary(string op)() if (op == "*") { return m_object; } 208 const(TR) opUnary(string op)() const if (op == "*") { return m_object; } 209 210 TR get() { return m_object; } 211 212 bool opCast(U : bool)() const { 213 return !isEmpty; 214 } 215 216 U opCast(U)() const nothrow 217 if (__traits(hasMember, U, "isUnique")) 218 { 219 if (!m_object) return Unique!(T, ALLOC)(); 220 return Unique!(U, ALLOC)(cast(U)this.m_object.release()); 221 } 222 223 U opCast(U)() const nothrow 224 if (!__traits(hasMember, U, "isUnique")) 225 { 226 if (!m_object) return cast(U)typeof(m_object).init; 227 return cast(U)this.m_object; 228 } 229 230 /** 231 Postblit operator is undefined to prevent the cloning of $(D Unique) objects. 232 */ 233 @disable this(this); 234 235 private: 236 237 @property void* ptr() const { 238 return cast(void*)m_object; 239 } 240 241 static if (HasDebugAllocations && DebugUnique) { 242 import memutils.rbtree; 243 __gshared RBTree!(void*, "a < b", true, Malloc) ptree; 244 __gshared Mutex mtx; 245 shared static this() { mtx = new Mutex; } 246 } 247 } 248 249 auto unique(T)(T obj) { 250 return Unique!T(obj); 251 }