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 }