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 }