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