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 return scope @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 return scope @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() return 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() return 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 if (_thisPtr is null) 113 return rhs._thisPtr is null; 114 if (rhs._thisPtr is null) 115 return false; 116 return _get_value == rhs._get_value; 117 } 118 119 /// 120 auto opCmp(Y)(auto ref scope const ThisTemplate!Y rhs) @trusted scope const pure nothrow @nogc 121 { 122 if (_thisPtr is null) 123 return (rhs._thisPtr is null) - 1; 124 if (rhs._thisPtr is null) 125 return 1; 126 return _get_value.opCmp(rhs._get_value); 127 } 128 129 /// 130 size_t toHash() @trusted scope const pure nothrow @nogc 131 { 132 if (_thisPtr is null) 133 return 0; 134 return hashOf(_get_value); 135 } 136 137 /// 138 ~this() nothrow 139 { 140 static if (hasElaborateDestructor!T || hasDestructor!T) 141 { 142 if (false) // break @safe and pure attributes 143 { 144 Unqual!T* object; 145 (*object).__xdtor; 146 } 147 } 148 if (this) 149 { 150 (() @trusted { mir_rc_decrease_counter(context); })(); 151 debug _reset; 152 } 153 } 154 155 static if (is(T == const) || is(T == immutable)) 156 this(return ref scope const typeof(this) rhs) @trusted pure nothrow @nogc 157 { 158 if (rhs) 159 { 160 this._value = cast(typeof(this._value))rhs._value; 161 this._context = cast(typeof(this._context))rhs._context; 162 mir_rc_increase_counter(context); 163 } 164 } 165 166 static if (is(T == immutable)) 167 this(return ref scope const typeof(this) rhs) immutable @trusted pure nothrow @nogc 168 { 169 if (rhs) 170 { 171 this._value = cast(typeof(this._value))rhs._value; 172 this._context = cast(typeof(this._context))rhs._context; 173 mir_rc_increase_counter(context); 174 } 175 } 176 177 static if (is(T == immutable)) 178 this(return ref scope const typeof(this) rhs) const @trusted pure nothrow @nogc 179 { 180 if (rhs) 181 { 182 this._value = cast(typeof(this._value))rhs._value; 183 this._context = cast(typeof(this._context))rhs._context; 184 mir_rc_increase_counter(context); 185 } 186 } 187 188 this(return ref scope inout typeof(this) rhs) inout @trusted pure nothrow @nogc 189 { 190 if (rhs) 191 { 192 this._value = rhs._value; 193 this._context = rhs._context; 194 mir_rc_increase_counter(context); 195 } 196 } 197 198 /// 199 ref opAssign(typeof(null)) scope return @trusted // pure nothrow @nogc 200 { 201 this = typeof(this).init; 202 } 203 204 /// 205 ref opAssign(return typeof(this) rhs) scope return @trusted // pure nothrow @nogc 206 { 207 this.proxySwap(rhs); 208 return this; 209 } 210 211 /// 212 ref opAssign(Q)(return ThisTemplate!Q rhs) scope return @trusted // pure nothrow @nogc 213 if (isImplicitlyConvertible!(Q*, T*)) 214 { 215 this.proxySwap(*()@trusted{return cast(typeof(this)*)&rhs;}()); 216 return this; 217 } 218 } 219 220 /// 221 alias RCPtr = mir_rcptr; 222 223 /++ 224 Returns: shared pointer of the member and the context from the current pointer. 225 +/ 226 auto shareMember(string member, T, Args...)(return mir_rcptr!T context, auto ref Args args) @safe 227 { 228 import core.lifetime: move; 229 void foo(A)(auto ref A) {} 230 assert(context != null); 231 static if (args.length) 232 { 233 // breaks safaty 234 if (false) foo(__traits(getMember, T.init, member)(forward!args)); 235 return (()@trusted => createRCWithContext(__traits(getMember, context._get_value, member)(forward!args), context.move))(); 236 } 237 else 238 { 239 // breaks safaty 240 if (false) foo(__traits(getMember, T.init, member)); 241 return (()@trusted => createRCWithContext(__traits(getMember, context._get_value, member), context.move))(); 242 } 243 } 244 245 /++ 246 Returns: shared pointer constructed with current context. 247 +/ 248 @system .mir_rcptr!R createRCWithContext(R, F)(return R value, return const mir_rcptr!F context) 249 if (is(R == class) || is(R == interface)) 250 { 251 typeof(return) ret; 252 ret._value = cast()value; 253 ret._context = cast(mir_rc_context*)context._context; 254 (*cast(mir_rcptr!F*)&context)._value = null; 255 (*cast(mir_rcptr!F*)&context)._context = null; 256 return ret; 257 } 258 259 ///ditto 260 @system .mir_rcptr!R createRCWithContext(R, F)(return ref R value, return const mir_rcptr!F context) 261 if (!is(R == class) && !is(R == interface)) 262 { 263 typeof(return) ret; 264 ret._value = &value; 265 ret._context = cast(mir_rc_context*)context._context; 266 (*cast(mir_rcptr!F*)&context)._value = null; 267 (*cast(mir_rcptr!F*)&context)._context = null; 268 return ret; 269 } 270 271 /++ 272 Construct a shared pointer of a required type with a current context. 273 Provides polymorphism abilities for classes and structures with `alias this` syntax. 274 +/ 275 mir_rcptr!R castTo(R, T)(return mir_rcptr!T context) @trusted 276 if (isImplicitlyConvertible!(T, R)) 277 { 278 import core.lifetime: move; 279 return createRCWithContext(cast(R)context._get_value, move(context)); 280 } 281 282 /// ditto 283 mir_rcptr!(const R) castTo(R, T)(return const mir_rcptr!T context) @trusted 284 if (isImplicitlyConvertible!(const T, const R)) 285 { 286 import core.lifetime: move; 287 return createRCWithContext(cast(const R)context._get_value, move(*cast(mir_rcptr!T*)&context)); 288 } 289 290 /// ditto 291 mir_rcptr!(immutable R) castTo(R, T)(return immutable mir_rcptr!T context) @trusted 292 if (isImplicitlyConvertible!(immutable T, immutable R)) 293 { 294 import core.lifetime: move; 295 return createRCWithContext(cast(immutable R)context._get_value, move(*cast(mir_rcptr!T*)&context)); 296 } 297 298 /// 299 template createRC(T) 300 if (!is(T == interface) && !__traits(isAbstractClass, T)) 301 { 302 /// 303 mir_rcptr!T createRC(Args...)(auto ref Args args) 304 { 305 typeof(return) ret; 306 with (ret) () @trusted { 307 _context = mir_rc_create(mir_get_type_info!T, 1, mir_get_payload_ptr!T); 308 if (!_context) 309 { 310 version(D_Exceptions) 311 { import mir.exception : toMutable; throw allocationError.toMutable; } 312 else 313 assert(0, allocationExcMsg); 314 } 315 _value = cast(typeof(_value))(_context + 1); 316 } (); 317 import core.lifetime: forward; 318 import mir.conv: emplace; 319 cast(void) emplace!T(ret._value, forward!args); 320 return ret; 321 } 322 } 323 324 /// 325 version(mir_test) 326 @safe pure @nogc nothrow 327 unittest 328 { 329 auto a = createRC!double(10); 330 auto b = a; 331 assert(*b == 10); 332 *b = 100; 333 assert(*a == 100); 334 } 335 336 /// Classes with empty constructor 337 version(mir_test) 338 @safe pure @nogc nothrow 339 unittest 340 { 341 static class C 342 { 343 int index = 34; 344 345 override size_t toHash() const scope @safe pure nothrow @nogc 346 { 347 return index; 348 } 349 } 350 assert(createRC!C.index == 34); 351 } 352 353 /// 354 version(mir_test) 355 @safe pure @nogc nothrow 356 unittest 357 { 358 static interface I { 359 ref double bar() @safe pure nothrow @nogc; 360 size_t toHash() @trusted scope const pure nothrow @nogc; 361 } 362 static abstract class D 363 { 364 int index; 365 366 override size_t toHash() const scope @safe pure nothrow @nogc 367 { 368 return index; 369 } 370 } 371 static class C : D, I 372 { 373 double value; 374 ref double bar() @safe pure nothrow @nogc { return value; } 375 this(double d){ value = d; } 376 377 override size_t toHash() const scope @safe pure nothrow @nogc 378 { 379 return hashOf(value, super.toHash); 380 } 381 } 382 auto a = createRC!C(10); 383 assert(a._counter == 1); 384 auto b = a; 385 assert(a._counter == 2); 386 assert((*b).value == 10); 387 b.value = 100; // access via alias this syntax 388 assert(a.value == 100); 389 assert(a._counter == 2); 390 391 auto d = a.castTo!D; //RCPtr!D 392 assert(d._counter == 3); 393 d.index = 234; 394 assert(a.index == 234); 395 auto i = a.castTo!I; //RCPtr!I 396 assert(i.bar == 100); 397 assert(i._counter == 4); 398 399 auto v = a.shareMember!"value"; //RCPtr!double 400 auto w = a.shareMember!"bar"; //RCPtr!double 401 assert(i._counter == 6); 402 assert(*v == 100); 403 ()@trusted{assert(&*w is &*v);}(); 404 } 405 406 /// 'Alias This' support 407 version(mir_test) 408 @safe pure @nogc nothrow 409 unittest 410 { 411 struct S 412 { 413 double e; 414 } 415 struct C 416 { 417 int i; 418 S s; 419 // 'alias' should be accesable by reference 420 // or a class/interface 421 alias s this; 422 } 423 424 auto a = createRC!C(10, S(3)); 425 auto s = a.castTo!S; // RCPtr!S 426 assert(s._counter == 2); 427 assert(s.e == 3); 428 } 429 430 version(unittest): 431 432 package struct _test_unpure_system_dest_s__ { 433 static int numStructs; 434 int i; 435 436 this(this This)(int i) { 437 this.i = i; 438 ++numStructs; 439 } 440 441 ~this() @system nothrow { 442 auto d = new int[2]; 443 --numStructs; 444 } 445 } 446 447 version(mir_test) 448 @system nothrow 449 unittest 450 { 451 import mir.rc.array; 452 auto ptr = createRC!_test_unpure_system_dest_s__(42); 453 auto arr = rcarray!_test_unpure_system_dest_s__(3); 454 }