1 /*
2  *******************************************************************************************
3  * Dgame (a D game framework) - Copyright (c) Randy Schütt
4  * 
5  * This software is provided 'as-is', without any express or implied warranty.
6  * In no event will the authors be held liable for any damages arising from
7  * the use of this software.
8  * 
9  * Permission is granted to anyone to use this software for any purpose,
10  * including commercial applications, and to alter it and redistribute it
11  * freely, subject to the following restrictions:
12  * 
13  * 1. The origin of this software must not be misrepresented; you must not claim
14  *    that you wrote the original software. If you use this software in a product,
15  *    an acknowledgment in the product documentation would be appreciated but is
16  *    not required.
17  * 
18  * 2. Altered source versions must be plainly marked as such, and must not be
19  *    misrepresented as being the original software.
20  * 
21  * 3. This notice may not be removed or altered from any source distribution.
22  *******************************************************************************************
23  */
24 module Dgame.Internal.Shared;
25 
26 debug import std.stdio;
27 private import Dgame.Internal.Allocator;
28 private import cstd = core.stdc.stdlib;
29 
30 @property
31 string idOf(T)() pure nothrow {
32 	static if (is(typeof(T.sizeof)))
33 		return T.stringof;
34 	else
35 		return __traits(identifier, T);
36 }
37 
38 void _def_del(T = void)(T* p) {
39 	cstd.free(p);
40 }
41 
42 struct shared_ptr(T) {
43 	T* ptr;
44 	int* rc;
45 	void function(T*) del_func;
46 	
47 	this(T* p, void function(T*) df = &_def_del!(T)) {
48 		this.ptr = p;
49 		this.del_func = df;
50 		
51 		this.retain();
52 	}
53 	
54 	this(this) {
55 		this.retain();
56 	}
57 	
58 	~this() {
59 		this.release();
60 	}
61 	
62 	void retain() {
63 		if (this.rc is null)
64 			this.rc = alloc_new!(int)(1);
65 		(*this.rc)++;
66 		
67 		debug writeln("Retain: ", this.usage(), "::", idOf!(T));
68 	}
69 	
70 	void release() {
71 		if (this.rc is null)
72 			return;
73 		(*this.rc)--;
74 
75 		debug writeln("Release: ", this.usage(), "::", idOf!(T));
76 
77 		if (this.usage() <= 0) {
78 			this.terminate();
79 		}
80 	}
81 
82 	void terminate() {
83 		if (this.del_func is null || this.ptr is null) {
84 			return;
85 		}
86 
87 		debug writeln("Terminate shared ", idOf!(T));
88 
89 		this.del_func(this.ptr);
90 		unmake(this.rc);
91 		this.ptr = null;
92 	}
93 	
94 	int usage() const pure nothrow {
95 		if (this.rc is null)
96 			return 0;
97 		return *this.rc;
98 	}
99 
100 	bool isValid() const pure nothrow {
101 		return this.ptr !is null;
102 	}
103 	
104 	alias ptr this;
105 }
106 
107 shared_ptr!(T) make_shared(T)(auto ref T value, void function(T*) df = &_def_del!(T)) if (!is(T : U*, U)) {
108 	T* p;
109 	make(value, p);
110 	
111 	return make_shared(p);
112 }
113 
114 shared_ptr!(T) make_shared(T, Args...)(void function(T*) df, Args args) {
115 	T* p = alloc_new!(int)(1);
116 	emplace(p, args);
117 
118 	return make_shared(p, df);
119 }
120 
121 shared_ptr!(T) make_shared(T, Args...)(Args args) {
122 	T* p = alloc_new!(int)(1);
123 	emplace(p, args);
124 	
125 	return make_shared(p);
126 }
127 
128 shared_ptr!(T) make_shared(T)(T* p, void function(T*) df = &_def_del!(T)) {
129 	return shared_ptr!(T)(p, df);
130 }
131 
132 shared_ptr!(T) allocate_shared(T)(size_t count, void function(T*) df = &_def_del!(T)) {
133 	T* p = alloc_new!(T)(count);
134 	
135 	return make_shared(p, df);
136 }
137 
138 unittest {
139 	import std.conv : to;
140 	
141 	struct A {
142 		int id;
143 	}
144 	
145 	{
146 		void test(shared_ptr!(A) rhs) {
147 			//assert(rhs.isCopy);
148 			assert(rhs.id == 42);
149 			assert(rhs.isValid());
150 			assert(rhs.usage == 2);
151 		}
152 		
153 		shared_ptr!(A) as = new A(42);
154 //		assert(_refCount == 1, to!string(_refCount));
155 		
156 		assert(as.id == 42);
157 		assert(as.usage == 1);
158 		assert(as.isValid());
159 		//assert(!as.isCopy);
160 		
161 		test(as);
162 		
163 		assert(as.id == 42);
164 		assert(as.usage == 1);
165 		assert(as.isValid());
166 		//assert(!as.isCopy);
167 		
168 		shared_ptr!(A) s1 = new A(111);
169 		shared_ptr!(A) s2 = s1;
170 //		assert(_refCount == 3, to!string(_refCount));
171 		
172 		assert(s1.usage == 2);
173 		assert(s2.usage == 2);
174 		//assert(s2.isCopy());
175 		assert(s1 == s2);
176 		
177 		s1 = shared_ptr!(A)(new A(222));
178 //		assert(_refCount == 3, to!string(_refCount));
179 		
180 		//debug writeln("\t\t", s1.usage, "::", s2.usage);
181 		
182 		assert(s1.isValid());
183 		assert(s2.isValid());
184 		assert(s1.usage == 1, to!string(s1.usage));
185 		assert(s2.usage == 1, to!string(s2.usage));
186 		//assert(s2.isCopy());
187 		assert(s1 != s2);
188 		
189 		void testDeleter(A* ptr) {
190 			
191 		}
192 		
193 		void test2(shared_ptr!(A) rhs, int id) {
194 			//assert(rhs.isCopy);
195 			assert(rhs.id == id);
196 			assert(rhs.isValid());
197 			assert(rhs.usage == 2);
198 		}
199 		
200 		shared_ptr!(A) s3 = new A(23);
201 //		assert(_refCount == 4, to!string(_refCount));
202 		
203 		assert(s3.isValid());
204 		assert(s3.id == 23);
205 		assert(s3.usage == 1);
206 		
207 		test2(s3, 23);
208 //		assert(_refCount == 4, to!string(_refCount));
209 		
210 		assert(s3.isValid());
211 		assert(s3.id == 23);
212 		assert(s3.usage == 1);
213 		
214 		s3 = shared_ptr!(A)(new A(42));
215 //		assert(_refCount == 4, to!string(_refCount));
216 		
217 		assert(s3.isValid());
218 		assert(s3.id == 42);
219 		assert(s3.usage == 1);
220 	}
221 	
222 //	assert(_refCount == 0);
223 }