1 /++ 2 $(H1 Thread-safe reference-counted shared pointers). 3 4 This implementation supports class and struct (`alias this`) polymorphism. 5 +/ 6 module mir.rc.ptr; 7 8 import mir.rc.context; 9 import mir.type_info; 10 import std.traits; 11 12 package static immutable allocationExcMsg = "mir_rcptr: out of memory error."; 13 package static immutable getExcMsg = "mir_rcptr: trying to use null value."; 14 15 version (D_Exceptions) 16 { 17 import core.exception: OutOfMemoryError, InvalidMemoryOperationError; 18 package static immutable allocationError = new OutOfMemoryError(allocationExcMsg); 19 } 20 21 /++ 22 Thread safe reference counting array. 23 24 This implementation supports class and struct (`alias this`) polymorphism. 25 26 `__xdtor` if any is used to destruct objects. 27 28 The implementation never adds roots into the GC. 29 +/ 30 struct mir_rcptr(T) 31 { 32 static if (is(T == class) || is(T == interface) || is(T == struct) || is(T == union)) 33 static assert(!__traits(isNested, T), "mir_rcptr does not support nested types."); 34 35 /// 36 static if (is(T == class) || is(T == interface)) 37 package Unqual!T _value; 38 else 39 package T* _value; 40 package mir_rc_context* _context; 41 42 package ref mir_rc_context context() inout scope return @trusted @property 43 { 44 return *cast(mir_rc_context*)_context; 45 } 46 47 package void _reset() 48 { 49 _value = null; 50 _context = null; 51 } 52 53 inout(void)* _thisPtr() inout scope return @trusted @property 54 { 55 return cast(inout(void)*) _value; 56 } 57 58 package alias ThisTemplate = .mir_rcptr; 59 60 /// ditto 61 alias opUnary(string op : "*") = _get_value; 62 /// ditto 63 alias _get_value this; 64 65 static if (is(T == class) || is(T == interface)) 66 /// 67 pragma(inline, true) 68 inout(T) _get_value() scope inout @property 69 { 70 assert(this, getExcMsg); 71 return _value; 72 } 73 else 74 /// 75 pragma(inline, true) 76 ref inout(T) _get_value() scope inout @property 77 { 78 assert(this, getExcMsg); 79 return *_value; 80 } 81 82 /// 83 void proxySwap(ref typeof(this) rhs) pure nothrow @nogc @safe 84 { 85 auto t0 = this._value; 86 auto t1 = this._context; 87 this._value = rhs._value; 88 this._context = rhs._context; 89 rhs._value = t0; 90 rhs._context = t1; 91 } 92 93 /// 94 this(typeof(null)) 95 { 96 } 97 98 /// 99 mixin CommonRCImpl; 100 101 102 /// 103 pragma(inline, true) 104 bool opEquals(typeof(null)) @safe scope const pure nothrow @nogc 105 { 106 return !this; 107 } 108 109 /// ditto 110 bool opEquals(Y)(auto ref scope const ThisTemplate!Y rhs) @safe scope const pure nothrow @nogc 111 { 112 return _thisPtr == rhs._thisPtr; 113 } 114 115 /// 116 sizediff_t opCmp(Y)(auto ref scope const ThisTemplate!Y rhs) @trusted scope const pure nothrow @nogc 117 { 118 return cast(void*)_thisPtr - cast(void*)rhs._thisPtr; 119 } 120 121 /// 122 size_t toHash() @trusted scope const pure nothrow @nogc 123 { 124 return cast(size_t) _thisPtr; 125 } 126 127 /// 128 ~this() nothrow 129 { 130 static if (hasElaborateDestructor!T || hasDestructor!T) 131 { 132 if (false) // break @safe and pure attributes 133 { 134 Unqual!T* object; 135 (*object).__xdtor; 136 } 137 } 138 if (this) 139 { 140 (() @trusted { mir_rc_decrease_counter(context); })(); 141 debug _reset; 142 } 143 } 144 145 static if (is(T == const) || is(T == immutable)) 146 this(return ref scope const typeof(this) rhs) @trusted pure nothrow @nogc 147 { 148 if (rhs) 149 { 150 this._value = cast(typeof(this._value))rhs._value; 151 this._context = cast(typeof(this._context))rhs._context; 152 mir_rc_increase_counter(context); 153 } 154 } 155 156 static if (is(T == immutable)) 157 this(return ref scope const typeof(this) rhs) immutable @trusted pure nothrow @nogc 158 { 159 if (rhs) 160 { 161 this._value = cast(typeof(this._value))rhs._value; 162 this._context = cast(typeof(this._context))rhs._context; 163 mir_rc_increase_counter(context); 164 } 165 } 166 167 static if (is(T == immutable)) 168 this(return ref scope const typeof(this) rhs) const @trusted pure nothrow @nogc 169 { 170 if (rhs) 171 { 172 this._value = cast(typeof(this._value))rhs._value; 173 this._context = cast(typeof(this._context))rhs._context; 174 mir_rc_increase_counter(context); 175 } 176 } 177 178 this(return ref scope inout typeof(this) rhs) inout @trusted pure nothrow @nogc 179 { 180 if (rhs) 181 { 182 this._value = rhs._value; 183 this._context = rhs._context; 184 mir_rc_increase_counter(context); 185 } 186 } 187 188 /// 189 ref opAssign(typeof(null)) return @trusted // pure nothrow @nogc 190 { 191 this = typeof(this).init; 192 } 193 194 /// 195 ref opAssign(return typeof(this) rhs) return @trusted // pure nothrow @nogc 196 { 197 this.proxySwap(rhs); 198 return this; 199 } 200 201 /// 202 ref opAssign(Q)(return ThisTemplate!Q rhs) return @trusted // pure nothrow @nogc 203 if (isImplicitlyConvertible!(Q*, T*)) 204 { 205 this.proxySwap(*()@trusted{return cast(typeof(this)*)&rhs;}()); 206 return this; 207 } 208 } 209 210 /// 211 alias RCPtr = mir_rcptr; 212 213 /++ 214 Returns: shared pointer of the member and the context from the current pointer. 215 +/ 216 auto shareMember(string member, T, Args...)(return mir_rcptr!T context, auto ref Args args) 217 { 218 import core.lifetime: move; 219 void foo(A)(auto ref A) {} 220 assert(context != null); 221 static if (args.length) 222 { 223 // breaks safaty 224 if (false) foo(__traits(getMember, context._get_value, member)(forward!args)); 225 return (()@trusted => createRCWithContext(__traits(getMember, context._get_value, member)(forward!args), context.move))(); 226 } 227 else 228 { 229 // breaks safaty 230 if (false) foo(__traits(getMember, context._get_value, member)); 231 return (()@trusted => createRCWithContext(__traits(getMember, context._get_value, member), context.move))(); 232 } 233 } 234 235 /++ 236 Returns: shared pointer constructed with current context. 237 +/ 238 @system .mir_rcptr!R createRCWithContext(R, F)(return R value, return const mir_rcptr!F context) 239 if (is(R == class) || is(R == interface)) 240 { 241 typeof(return) ret; 242 ret._value = cast()value; 243 ret._context = cast(mir_rc_context*)context._context; 244 (*cast(mir_rcptr!F*)&context)._value = null; 245 (*cast(mir_rcptr!F*)&context)._context = null; 246 return ret; 247 } 248 249 ///ditto 250 @system .mir_rcptr!R createRCWithContext(R, F)(return ref R value, return const mir_rcptr!F context) 251 if (!is(R == class) && !is(R == interface)) 252 { 253 typeof(return) ret; 254 ret._value = &value; 255 ret._context = cast(mir_rc_context*)context._context; 256 (*cast(mir_rcptr!F*)&context)._value = null; 257 (*cast(mir_rcptr!F*)&context)._context = null; 258 return ret; 259 } 260 261 /++ 262 Construct a shared pointer of a required type with a current context. 263 Provides polymorphism abilities for classes and structures with `alias this` syntax. 264 +/ 265 mir_rcptr!R castTo(R, T)(return mir_rcptr!T context) @trusted 266 if (isImplicitlyConvertible!(T, R)) 267 { 268 import core.lifetime: move; 269 return createRCWithContext(cast(R)context._get_value, move(context)); 270 } 271 272 /// ditto 273 mir_rcptr!(const R) castTo(R, T)(return const mir_rcptr!T context) @trusted 274 if (isImplicitlyConvertible!(const T, const R)) 275 { 276 import core.lifetime: move; 277 return createRCWithContext(cast(const R)context._get_value, move(*cast(mir_rcptr!T*)&context)); 278 } 279 280 /// ditto 281 mir_rcptr!(immutable R) castTo(R, T)(return immutable mir_rcptr!T context) @trusted 282 if (isImplicitlyConvertible!(immutable T, immutable R)) 283 { 284 import core.lifetime: move; 285 return createRCWithContext(cast(immutable R)context._get_value, move(*cast(mir_rcptr!T*)&context)); 286 } 287 288 /// 289 template createRC(T) 290 if (!is(T == interface) && !__traits(isAbstractClass, T)) 291 { 292 /// 293 mir_rcptr!T createRC(Args...)(auto ref Args args) 294 { 295 typeof(return) ret; 296 with (ret) () @trusted { 297 _context = mir_rc_create(mir_get_type_info!T, 1, mir_get_payload_ptr!T); 298 if (!_context) 299 { 300 version(D_Exceptions) 301 throw allocationError; 302 else 303 assert(0, allocationExcMsg); 304 } 305 _value = cast(typeof(_value))(_context + 1); 306 } (); 307 import core.lifetime: forward; 308 import mir.conv: emplace; 309 cast(void) emplace!T(ret._value, forward!args); 310 return ret; 311 } 312 } 313 314 /// 315 version(mir_test) 316 @safe pure @nogc nothrow 317 unittest 318 { 319 auto a = createRC!double(10); 320 auto b = a; 321 assert(*b == 10); 322 *b = 100; 323 assert(*a == 100); 324 } 325 326 /// Classes with empty constructor 327 version(mir_test) 328 @safe pure @nogc nothrow 329 unittest 330 { 331 static class C 332 { 333 int index = 34; 334 } 335 assert(createRC!C.index == 34); 336 } 337 338 /// 339 version(mir_test) 340 @safe pure @nogc nothrow 341 unittest 342 { 343 static interface I { ref double bar() @safe pure nothrow @nogc; } 344 static abstract class D { int index; } 345 static class C : D, I 346 { 347 double value; 348 ref double bar() @safe pure nothrow @nogc { return value; } 349 this(double d) { value = d; } 350 } 351 auto a = createRC!C(10); 352 assert(a._counter == 1); 353 auto b = a; 354 assert(a._counter == 2); 355 assert((*b).value == 10); 356 b.value = 100; // access via alias this syntax 357 assert(a.value == 100); 358 assert(a._counter == 2); 359 360 auto d = a.castTo!D; //RCPtr!D 361 assert(d._counter == 3); 362 d.index = 234; 363 assert(a.index == 234); 364 auto i = a.castTo!I; //RCPtr!I 365 assert(i.bar == 100); 366 assert(i._counter == 4); 367 368 auto v = a.shareMember!"value"; //RCPtr!double 369 auto w = a.shareMember!"bar"; //RCPtr!double 370 assert(i._counter == 6); 371 assert(*v == 100); 372 ()@trusted{assert(&*w is &*v);}(); 373 } 374 375 /// 'Alias This' support 376 version(mir_test) 377 @safe pure @nogc nothrow 378 unittest 379 { 380 struct S 381 { 382 double e; 383 } 384 struct C 385 { 386 int i; 387 S s; 388 // 'alias' should be accesable by reference 389 // or a class/interface 390 alias s this; 391 } 392 393 auto a = createRC!C(10, S(3)); 394 auto s = a.castTo!S; // RCPtr!S 395 assert(s._counter == 2); 396 assert(s.e == 3); 397 } 398 399 version(unittest): 400 401 package struct _test_unpure_system_dest_s__ { 402 static int numStructs; 403 int i; 404 405 this(this This)(int i) { 406 this.i = i; 407 ++numStructs; 408 } 409 410 ~this() @system nothrow { 411 auto d = new int[2]; 412 --numStructs; 413 } 414 } 415 416 version(mir_test) 417 @system nothrow 418 unittest 419 { 420 import mir.rc.array; 421 auto ptr = createRC!_test_unpure_system_dest_s__(42); 422 auto arr = rcarray!_test_unpure_system_dest_s__(3); 423 }