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 }