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 }