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 151 //static if (HasDebugAllocations && DebugUnique) 152 // debug memset(ptr, 0, AllocSize!T); 153 } 154 } 155 else { 156 static if (HasGCCheck) if (!gc_inFinalizer()) { 157 if (m_object) { 158 //logTrace("ptr in ptree: ", ptr in ptree); 159 160 static if (HasDebugAllocations && DebugUnique) { 161 mtx.lock(); scope(exit) mtx.unlock(); 162 ptree._defaultInitialize(); 163 if (ptr !in ptree){ 164 logDebug("Unknown pointer: " ~ ptr.to!string ~ " of type " ~ T.stringof); 165 assert(false); 166 } 167 ptree.remove(ptr); 168 } 169 170 static if (is(TR == T*)) .destroy(*m_object); 171 else .destroy(m_object); 172 } 173 } 174 } 175 } 176 /** Returns whether the resource exists. */ 177 @property bool isEmpty() const 178 { 179 return m_object is null; 180 } 181 182 /** Transfer ownership to a $(D Unique) rvalue. Nullifies the current contents. */ 183 TR release() 184 { 185 //logTrace("Release"); 186 if (!m_object) return null; 187 auto ret = m_object; 188 drop(); 189 return ret; 190 } 191 192 void drop() 193 { 194 //logTrace("Drop"); 195 if (!m_object) return; 196 static if (HasDebugAllocations && DebugUnique) { 197 mtx.lock(); scope(exit) mtx.unlock(); 198 ptree._defaultInitialize(); 199 assert(ptr in ptree); 200 ptree.remove(ptr); 201 } 202 m_object = null; 203 } 204 205 TR opUnary(string op)() if (op == "*") { return m_object; } 206 const(TR) opUnary(string op)() const if (op == "*") { return m_object; } 207 208 TR get() { return m_object; } 209 210 bool opCast(U : bool)() const { 211 return !isEmpty; 212 } 213 214 U opCast(U)() const nothrow 215 if (__traits(hasMember, U, "isUnique")) 216 { 217 if (!m_object) return Unique!(T, ALLOC)(); 218 return Unique!(U, ALLOC)(cast(U)this.m_object.release()); 219 } 220 221 U opCast(U)() const nothrow 222 if (!__traits(hasMember, U, "isUnique")) 223 { 224 if (!m_object) return cast(U)typeof(m_object).init; 225 return cast(U)this.m_object; 226 } 227 228 /** 229 Postblit operator is undefined to prevent the cloning of $(D Unique) objects. 230 */ 231 @disable this(this); 232 233 private: 234 235 @property void* ptr() const { 236 return cast(void*)m_object; 237 } 238 239 static if (HasDebugAllocations && DebugUnique) { 240 import memutils.rbtree; 241 __gshared RBTree!(void*, "a < b", true, Malloc) ptree; 242 __gshared Mutex mtx; 243 shared static this() { mtx = new Mutex; } 244 } 245 } 246 247 auto unique(T)(T obj) { 248 return Unique!T(obj); 249 }