1 /** 2 Implementation of reference counted pointer `SharedPtr` (similar to c++ `std::shared_ptr`). 3 4 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 5 Authors: Adam Búš 6 */ 7 module btl.autoptr.shared_ptr; 8 9 import btl.traits.common; 10 11 import btl.internal.allocator; 12 import btl.internal.traits; 13 import btl.internal.gc; 14 15 import btl.autoptr.common; 16 17 18 19 /** 20 Check if type `T` is `SharedPtr`. 21 */ 22 public template isSharedPtr(T){ 23 import std.traits : isInstanceOf; 24 25 enum bool isSharedPtr = isInstanceOf!(SharedPtr, T); 26 } 27 28 /// 29 unittest{ 30 static assert(!isSharedPtr!long); 31 static assert(!isSharedPtr!(void*)); 32 33 static assert(isSharedPtr!(SharedPtr!long)); 34 static assert(isSharedPtr!(SharedPtr!long.WeakType)); 35 } 36 37 38 39 /** 40 Implementation of a ref counted pointer with support for aliasing. 41 42 `SharedPtr` retains shared ownership of an object through a pointer. 43 44 Several `SharedPtr` objects may own the same object. 45 46 The object is destroyed and its memory deallocated when either of the following happens: 47 48 1. the last remaining `SharedPtr` owning the object is destroyed. 49 50 2. the last remaining `SharedPtr` owning the object is assigned another pointer via various methods like `opAssign` and `store`. 51 52 The object is destroyed using delete-expression or a custom deleter that is supplied to `SharedPtr` during construction. 53 54 A `SharedPtr` can share ownership of an object while storing a pointer to another object. 55 This feature can be used to point to member objects while owning the object they belong to. 56 The stored pointer is the one accessed by `get()`, the dereference and the comparison operators. 57 The managed pointer is the one passed to the deleter when use count reaches zero. 58 59 A `SharedPtr` may also own no objects, in which case it is called empty (an empty `SharedPtr` may have a non-null stored pointer if the aliasing constructor was used to create it). 60 61 If template parameter `_ControlType` is `shared` then all member functions (including copy constructor and copy assignment) 62 can be called by multiple threads on different instances of `SharedPtr` without additional synchronization even if these instances are copies and share ownership of the same object. 63 64 If multiple threads of execution access the same `SharedPtr` (`shared SharedPtr`) then only some methods can be called (`load`, `store`, `exchange`, `compareExchange`, `useCount`). 65 66 Template parameters: 67 68 `_Type` type of managed object 69 70 `_DestructorType` function pointer with attributes of destructor, to get attributes of destructor from type use `btl.autoptr.common.DestructorType!T`. Destructor of type `_Type` must be compatible with `_DestructorType` 71 72 `_ControlType` represent type of counter, must by of type `btl.autoptr.common.ControlBlock`. if is shared then ref counting is atomic. 73 74 `_weakPtr` if `true` then `SharedPtr` represent weak ptr 75 76 */ 77 public template SharedPtr( 78 _Type, 79 _DestructorType = DestructorType!_Type, 80 _ControlType = ControlBlockDeduction!(_Type, SharedControlBlock), 81 bool _weakPtr = false 82 ) 83 if(isControlBlock!_ControlType && isDestructorType!_DestructorType){ 84 85 static assert(_ControlType.hasSharedCounter || is(_ControlType == immutable), 86 "_ControlType must be `ControlBlock` with shared counter or `ControlBlock` must be immutable." 87 ); 88 89 static assert(!_weakPtr || _ControlType.hasWeakCounter, 90 "weak pointer must have control block with weak counter" 91 ); 92 93 static if (is(_Type == class) || is(_Type == interface) || is(_Type == struct) || is(_Type == union)) 94 static assert(!__traits(isNested, _Type), 95 "SharedPtr does not support nested types." 96 ); 97 98 static assert(is(DestructorType!void : _DestructorType), 99 _Type.stringof ~ " wrong DestructorType " ~ DestructorType!void.stringof ~ 100 " : " ~ _DestructorType.stringof 101 ); 102 103 /*static assert(is(DestructorType!_Type : _DestructorType), 104 "destructor of type '" ~ _Type.stringof ~ 105 "' doesn't support specified finalizer " ~ _DestructorType.stringof 106 );*/ 107 108 void check_dtor()(){ 109 static assert(is(DestructorType!_Type : _DestructorType), 110 "destructor of type '" ~ _Type.stringof ~ 111 "' doesn't support specified finalizer " ~ _DestructorType.stringof 112 ); 113 } 114 115 import std.meta : AliasSeq; 116 import std.range : ElementEncodingType; 117 import std.traits: Unqual, Unconst, CopyTypeQualifiers, CopyConstness, 118 hasIndirections, hasElaborateDestructor, 119 isMutable, isAbstractClass, isDynamicArray, isStaticArray, isCallable, Select, isArray; 120 121 import core.atomic : MemoryOrder; 122 import core.lifetime : forward; 123 124 enum bool hasWeakCounter = _ControlType.hasWeakCounter; 125 126 enum bool hasSharedCounter = _ControlType.hasSharedCounter; 127 128 enum bool referenceElementType = isClassOrInterface!_Type || isDynamicArray!_Type; 129 130 static if(isDynamicArray!_Type) 131 alias ElementDestructorType() = .DestructorType!void; 132 else 133 alias ElementDestructorType() = .DestructorType!_Type; 134 135 136 enum bool _isLockFree = false; 137 138 struct SharedPtr{ 139 140 /** 141 Type of element managed by `SharedPtr`. 142 */ 143 public alias ElementType = _Type; 144 145 146 /** 147 Type of destructor (`void function(void*)@attributes`). 148 */ 149 public alias DestructorType = _DestructorType; 150 151 152 /** 153 Type of control block. 154 */ 155 public alias ControlType = _ControlType; 156 157 158 /** 159 `true` if `SharedPtr` is weak ptr. 160 */ 161 public alias isWeak = _weakPtr; 162 163 164 /** 165 Same as `ElementType*` or `ElementType` if is class/interface/slice. 166 */ 167 public alias ElementReferenceType = ElementReferenceTypeImpl!ElementType; 168 169 170 /** 171 Weak pointer 172 173 `SharedPtr.WeakType` is a smart pointer that holds a non-owning ("weak") reference to an object that is managed by `SharedPtr`. 174 It must be converted to `SharedPtr` in order to access the referenced object. 175 176 `SharedPtr.WeakType` models temporary ownership: when an object needs to be accessed only if it exists, and it may be deleted at any time by someone else, 177 `SharedPtr.WeakType` is used to track the object, and it is converted to `SharedPtr` to assume temporary ownership. 178 If the original `SharedPtr` is destroyed at this time, the object's lifetime is extended until the temporary `SharedPtr` is destroyed as well. 179 180 Another use for `SharedPtr.WeakType` is to break reference cycles formed by objects managed by `SharedPtr`. 181 If such cycle is orphaned (i,e. there are no outside shared pointers into the cycle), the `SharedPtr` reference counts cannot reach zero and the memory is leaked. 182 To prevent this, one of the pointers in the cycle can be made weak. 183 */ 184 static if(hasWeakCounter) 185 public alias WeakType = SharedPtr!( 186 _Type, 187 _DestructorType, 188 _ControlType, 189 true 190 ); 191 else 192 public alias WeakType = void; 193 194 195 /** 196 Type of non weak ptr. 197 */ 198 public alias SharedType = SharedPtr!( 199 _Type, 200 _DestructorType, 201 _ControlType, 202 false 203 ); 204 205 206 207 /** 208 `true` if shared `SharedPtr` has lock free operations `store`, `load`, `exchange`, `compareExchange`, otherwise 'false' 209 */ 210 public alias isLockFree = _isLockFree; 211 212 static if(isLockFree) 213 static assert(ElementReferenceType.sizeof == size_t.sizeof); 214 215 216 217 /** 218 Destructor 219 220 If `this` owns an object and it is the last `SharedPtr` owning it, the object is destroyed. 221 After the destruction, the smart pointers that shared ownership with `this`, if any, will report a `useCount()` that is one less than its previous value. 222 */ 223 public ~this(){ 224 this._release(); 225 } 226 227 228 229 /** 230 Constructs a `SharedPtr` without managed object. Same as `SharedPtr.init` 231 232 Examples: 233 -------------------- 234 SharedPtr!long x = null; 235 236 assert(x == null); 237 assert(x == SharedPtr!long.init); 238 -------------------- 239 */ 240 public this(this This)(typeof(null) nil, Forward = Forward.init)pure nothrow @safe @nogc{ 241 } 242 243 244 245 /** 246 Forward constructor (merge move and copy constructor). 247 */ 248 public this(Rhs, this This)(scope auto ref Rhs rhs, Forward)@trusted //if rhs is rvalue then dtor is called on empty rhs 249 if(isSmartPtr!Rhs && !is(Rhs == shared)){ 250 //error: 251 static if(!isConstructable!(rhs, This)){ 252 this.construct_error(forward!rhs); 253 } 254 //lock (copy): 255 else static if(weakLock!(Rhs, This)){ 256 if(rhs._control !is null && rhs._control.add_shared_if_exists()){ 257 this._control = rhs._control; 258 this._element = rhs._element; 259 } 260 /+else{ 261 this._control = null; 262 this._element = null; 263 }+/ 264 } 265 else if(rhs._element !is null){ 266 this._control = rhs._control; 267 this._element = rhs._element; 268 269 //copy or lock(copy): 270 static if(isRef!rhs || (isWeak && !Rhs.isWeak)){ 271 if(this._control !is null) 272 rhs._control.add!isWeak; 273 } 274 //move: 275 else{ 276 rhs._const_reset(); 277 } 278 279 } 280 } 281 282 // 283 private void construct_error(Rhs, this This)(scope auto ref Rhs rhs) 284 if(isSmartPtr!Rhs && !isConstructable!(rhs, This)){ 285 static if(isRef!rhs){ 286 import std.traits : isMutable; 287 288 static assert(isMutable!Rhs, 289 "rhs must be mutable." 290 ); 291 static assert(isMutable!(Rhs.ControlType), 292 "control type of rhs must be mutable." 293 ); 294 } 295 296 static assert(is(GetElementReferenceType!Rhs : GetElementReferenceType!This), 297 "element type '" ~ GetElementReferenceType!Rhs.stringof ~ "' is not compatible with '" ~ GetElementReferenceType!This.stringof ~ "'." 298 ); 299 static assert(is(Rhs.DestructorType : This.DestructorType), 300 "destructor attributes '" ~ Rhs.DestructorType.stringof ~ "' are not compatible with '" ~ This.DestructorType.stringof ~ "'." 301 ); 302 static assert(is(GetControlType!Rhs* : GetControlType!This*), 303 "control type " ~ GetControlType!Rhs.stringof ~ " is not compatible with '" ~ GetControlType!This.stringof ~ "'." 304 ); 305 306 static assert(0, "ShardPtr canno't be constructed from rhs of type " ~ Rhs.stringof); 307 } 308 309 310 311 /** 312 Constructs a `SharedPtr` which shares ownership of the object managed by `rhs` and pointing to `element`. 313 314 The aliasing constructor: constructs a `SharedPtr` which shares ownership information with the initial value of `rhs`, 315 but holds an unrelated and unmanaged pointer ptr. If this `SharedPtr` is the last of the group to go out of scope, 316 it will call the stored deleter for the object originally managed by `rhs`. 317 However, calling `get()` or `ptr()` on this `SharedPtr` will always return a copy of `element`. 318 It is the responsibility of the programmer to make sure that this ptr remains valid as long as this `SharedPtr` exists, 319 such as in the typical use cases where `element` is a member of the object managed by `rhs` or is an alias (e.g., downcast) of `rhs.get()`. 320 321 Examples: 322 -------------------- 323 static struct Foo{ 324 int i; 325 double d; 326 } 327 SharedPtr!Foo foo = SharedPtr!Foo.make(42, 3.14); 328 329 auto x = SharedPtr!double(foo, &foo.d); 330 assert(foo.useCount == 2); 331 assert(foo.get == 3.14); 332 -------------------- 333 */ 334 public this(Rhs, Elm, this This)(scope auto ref Rhs rhs, Elm element)@trusted //if rhs is rvalue then dtor is called on empty rhs 335 if( isSharedPtr!Rhs 336 && is(Elm : GetElementReferenceType!This) 337 && isAliasable!(Rhs, This) 338 && !weakLock!(Rhs, This) 339 && !is(Rhs == shared) 340 ){ 341 this._control = rhs._control; 342 this._element = element; 343 344 static if(isRef!rhs || (isWeak && !Rhs.isWeak)){ 345 if(this._control !is null) 346 rhs._control.add!isWeak; 347 } 348 else{ 349 rhs._const_reset(); 350 } 351 } 352 353 354 /** 355 Constructs a `SharedPtr` which shares ownership of the object managed by `rhs`. 356 357 If rhs manages no object, this manages no object too. 358 If rhs if rvalue then ownership is moved. 359 The template overload doesn't participate in overload resolution if ElementType of `typeof(rhs)` is not implicitly convertible to `ElementType`. 360 If rhs if `WeakType` then this ctor is equivalent to `this(rhs.lock())`. 361 362 Examples: 363 -------------------- 364 { 365 SharedPtr!long x = SharedPtr!long.make(123); 366 assert(x.useCount == 1); 367 368 SharedPtr!long a = x; //lvalue copy ctor 369 assert(a == x); 370 371 const SharedPtr!long b = x; //lvalue copy ctor 372 assert(b == x); 373 374 SharedPtr!(const long) c = x; //lvalue ctor 375 assert(c == x); 376 377 const SharedPtr!long d = b; //lvalue ctor 378 assert(d == x); 379 380 assert(x.useCount == 5); 381 } 382 383 { 384 import core.lifetime : move; 385 SharedPtr!long x = SharedPtr!long.make(123); 386 assert(x.useCount == 1); 387 388 SharedPtr!long a = move(x); //rvalue copy ctor 389 assert(a.useCount == 1); 390 391 const SharedPtr!long b = move(a); //rvalue copy ctor 392 assert(b.useCount == 1); 393 394 SharedPtr!(const long) c = b.load; //rvalue ctor 395 assert(c.useCount == 2); 396 397 const SharedPtr!long d = move(c); //rvalue ctor 398 assert(d.useCount == 2); 399 } 400 401 { 402 import core.lifetime : move; 403 auto u = UniquePtr!(long, SharedControlBlock).make(123); 404 405 SharedPtr!long s = move(u); //rvalue copy ctor 406 assert(s != null); 407 assert(s.useCount == 1); 408 409 SharedPtr!long s2 = UniquePtr!(long, SharedControlBlock).init; 410 assert(s2 == null); 411 } 412 413 { 414 import core.lifetime : move; 415 auto rc = RcPtr!(long).make(123); 416 assert(rc.useCount == 1); 417 418 SharedPtr!long s = rc; 419 assert(s != null); 420 assert(s.useCount == 2); 421 assert(rc.useCount == 2); 422 423 SharedPtr!long s2 = RcPtr!(long).init; 424 assert(s2 == null); 425 } 426 -------------------- 427 */ 428 public this(Rhs, this This)(scope auto ref Rhs rhs)@trusted //if rhs is rvalue then dtor is called on empty rhs 429 if(isSmartPtr!Rhs && !is(Rhs == shared) && !isMoveCtor!(This, rhs)){ 430 //static assert(isConstructable!(rhs, This)); 431 this(forward!rhs, Forward.init); 432 } 433 434 435 436 // 437 private this(C, Elm, this This)(C* control, Elm element)@safe pure nothrow @nogc 438 if(true 439 && is(C* : GetControlType!This*) 440 && is(Elm : GetElementReferenceType!This) 441 && !is(This == shared) 442 ){ 443 this._control = control; 444 this._element = element; 445 } 446 447 448 449 //copy ctors: 450 static if(isCopyConstructable!(typeof(this), typeof(this))) 451 this(ref scope typeof(this) rhs)@safe{this(rhs, Forward.init);} 452 else 453 @disable this(ref scope typeof(this) rhs)@safe; 454 455 static if(isCopyConstructable!(typeof(this), const typeof(this))) 456 this(ref scope typeof(this) rhs)const @safe{this(rhs, Forward.init);} 457 else 458 @disable this(ref scope typeof(this) rhs)const @safe; 459 460 static if(isCopyConstructable!(typeof(this), immutable typeof(this))) 461 this(ref scope typeof(this) rhs)immutable @safe{this(rhs, Forward.init);} 462 else 463 @disable this(ref scope typeof(this) rhs)immutable @safe; 464 465 static if(isCopyConstructable!(typeof(this), shared typeof(this))) 466 this(ref scope typeof(this) rhs)shared @safe{this(rhs, Forward.init);} 467 else 468 @disable this(ref scope typeof(this) rhs)shared @safe; 469 470 static if(isCopyConstructable!(typeof(this), const shared typeof(this))) 471 this(ref scope typeof(this) rhs)const shared @safe{this(rhs, Forward.init);} 472 else 473 @disable this(ref scope typeof(this) rhs)const shared @safe; 474 475 476 /+ 477 //Not neccesary: 478 static foreach(alias From; AliasSeq!( 479 const typeof(this), 480 immutable typeof(this), 481 shared typeof(this), 482 const shared typeof(this) 483 )){ 484 @disable this(ref scope From rhs)@safe; 485 @disable this(ref scope From rhs)const @safe; 486 @disable this(ref scope From rhs)immutable @safe; 487 @disable this(ref scope From rhs)shared @safe; 488 @disable this(ref scope From rhs)const shared @safe; 489 //@disable this(ref scope From rhs)pure nothrow @safe @nogc; 490 } 491 +/ 492 493 494 /** 495 Releases the ownership of the managed object, if any. 496 497 After the call, this manages no object. 498 499 Examples: 500 -------------------- 501 { 502 SharedPtr!long x = SharedPtr!long.make(1); 503 504 assert(x.useCount == 1); 505 x = null; 506 assert(x.useCount == 0); 507 assert(x == null); 508 } 509 510 { 511 SharedPtr!(shared long) x = SharedPtr!(shared long).make(1); 512 513 assert(x.useCount == 1); 514 x = null; 515 assert(x.useCount == 0); 516 assert(x == null); 517 } 518 519 { 520 shared SharedPtr!(long) x = SharedPtr!(shared long).make(1); 521 522 assert(x.useCount == 1); 523 x = null; 524 assert(x.useCount == 0); 525 assert(x == null); 526 } 527 -------------------- 528 */ 529 public void opAssign(MemoryOrder order = MemoryOrder.seq, this This)(typeof(null) nil)scope 530 if(isMutable!This){ 531 static if(is(This == shared)){ 532 this.lockSmartPtr!( 533 (ref scope self) => self.opAssign!order(null) 534 )(); 535 } 536 else{ 537 this._release(); 538 ()@trusted{ 539 this._reset(); 540 }(); 541 } 542 } 543 544 /** 545 Shares ownership of the object managed by `rhs`. 546 547 If `rhs` manages no object, `this` manages no object too. 548 If `rhs` is rvalue then move-assigns a `SharedPtr` from `rhs` 549 550 Examples: 551 -------------------- 552 { 553 SharedPtr!long px1 = SharedPtr!long.make(1); 554 SharedPtr!long px2 = SharedPtr!long.make(2); 555 556 assert(px2.useCount == 1); 557 px1 = px2; 558 assert(*px1 == 2); 559 assert(px2.useCount == 2); 560 } 561 562 563 { 564 SharedPtr!long px = SharedPtr!long.make(1); 565 SharedPtr!(const long) pcx = SharedPtr!long.make(2); 566 567 assert(px.useCount == 1); 568 pcx = px; 569 assert(*pcx == 1); 570 assert(pcx.useCount == 2); 571 572 } 573 574 575 { 576 const SharedPtr!long cpx = SharedPtr!long.make(1); 577 SharedPtr!(const long) pcx = SharedPtr!long.make(2); 578 579 assert(pcx.useCount == 1); 580 pcx = cpx; 581 assert(*pcx == 1); 582 assert(pcx.useCount == 2); 583 584 } 585 586 { 587 SharedPtr!(immutable long) pix = SharedPtr!(immutable long).make(123); 588 SharedPtr!(const long) pcx = SharedPtr!long.make(2); 589 590 assert(pix.useCount == 1); 591 pcx = pix; 592 assert(*pcx == 123); 593 assert(pcx.useCount == 2); 594 595 } 596 597 import btl.autoptr.rc_ptr; 598 { 599 SharedPtr!long sp = SharedPtr!long.make(10); 600 RcPtr!long rc = RcPtr!long.make(20); 601 602 assert(sp.useCount == 1); 603 assert(rc.useCount == 1); 604 sp = rc; 605 606 assert(sp.get == 20); 607 assert(rc.useCount == 2); 608 } 609 610 import btl.autoptr.intrusive_ptr; 611 { 612 static struct Foo{ 613 private SharedControlBlock control; 614 int i; 615 616 this(int i)pure nothrow @safe @nogc{ 617 this.i = i; 618 } 619 } 620 621 SharedPtr!Foo sp = SharedPtr!Foo.make(10); 622 IntrusivePtr!Foo ip = IntrusivePtr!Foo.make(20); 623 624 assert(sp.useCount == 1); 625 assert(ip.useCount == 1); 626 sp = ip; 627 628 assert(sp.get.i== 20); 629 assert(ip.useCount == 2); 630 } 631 -------------------- 632 */ 633 public void opAssign(MemoryOrder order = MemoryOrder.seq, Rhs, this This)(scope auto ref Rhs desired)scope 634 if(isSmartPtr!Rhs && !is(Rhs == shared)){ 635 //error 636 static if(!isAssignable!(desired, This)){ 637 this.assign_error(forward!desired); 638 } 639 //RcPtr and IntrusivePtr 640 else static if(!isSharedPtr!Rhs){ 641 this.opAssign!order(UnqualSmartPtr!This(forward!desired)); 642 } 643 // shared assign: 644 else static if(is(This == shared)){ 645 this.lockSmartPtr!( 646 (ref scope self, scope auto ref Rhs x) => self.opAssign!order(forward!x) 647 )(forward!desired); 648 } 649 // copy assign or non identity move assign: 650 else static if(isRef!desired || !is(This == Rhs)){ 651 static if(isRef!desired && (This.isWeak != Rhs.isWeak)){ 652 if((()@trusted => cast(const void*)&desired is cast(const void*)&this)()) 653 return; 654 } 655 656 this._release(); 657 658 auto tmp = This(forward!desired); 659 660 ()@trusted{ 661 this._set_element(tmp._element); 662 this._control = tmp._control; 663 664 tmp._const_set_counter(null); 665 }(); 666 } 667 //identity move assign: //separate case for core.lifetime.move 668 else{ 669 static assert(isMoveAssignable!(Rhs, This)); 670 static assert(!isRef!desired); 671 672 this._release(); 673 674 ()@trusted{ 675 this._control = desired._control; 676 this._set_element(desired._element); 677 678 desired._const_set_counter(null); 679 }(); 680 } 681 } 682 683 // 684 private void assign_error(Rhs, this This)(scope auto ref Rhs desired) 685 if(isSmartPtr!Rhs && !isAssignable!(desired, This)){ 686 import std.traits : isMutable; 687 688 static assert(!weakLock!(Rhs, This), 689 "weak error" 690 ); 691 static assert(isMutable!This, "assign must have mutable this"); 692 693 694 this.construct_error(forward!desired); 695 } 696 697 698 699 /** 700 Constructs an object of type `ElementType` and wraps it in a `SharedPtr` using args as the parameter list for the constructor of `ElementType`. 701 702 The object is constructed as if by the expression `emplace!ElementType(_payload, forward!args)`, where _payload is an internal pointer to storage suitable to hold an object of type `ElementType`. 703 The storage is typically larger than `ElementType.sizeof` in order to use one allocation for both the control block and the `ElementType` object. 704 705 Examples: 706 -------------------- 707 { 708 SharedPtr!long a = SharedPtr!long.make(); 709 assert(a.get == 0); 710 711 SharedPtr!(const long) b = SharedPtr!long.make(2); 712 assert(b.get == 2); 713 } 714 715 { 716 static struct Struct{ 717 int i = 7; 718 719 this(int i)pure nothrow @safe @nogc{ 720 this.i = i; 721 } 722 } 723 724 SharedPtr!Struct s1 = SharedPtr!Struct.make(); 725 assert(s1.get.i == 7); 726 727 SharedPtr!Struct s2 = SharedPtr!Struct.make(123); 728 assert(s2.get.i == 123); 729 } 730 731 { 732 static interface Interface{ 733 } 734 static class Class : Interface{ 735 int i; 736 737 this(int i)pure nothrow @safe @nogc{ 738 this.i = i; 739 } 740 } 741 742 SharedPtr!Interface x = SharedPtr!Class.make(3); 743 //assert(x.dynTo!Class.get.i == 3); 744 } 745 -------------------- 746 */ 747 748 749 public static auto make(AllocatorType = DefaultAllocator, bool supportGC = platformSupportGC, Args...)(auto ref Args args) 750 if(!isDynamicArray!ElementType){ 751 check_dtor(); 752 753 ()@trusted{ 754 (ref Args a)@system{ 755 if(false){ 756 auto tmp = a; 757 } 758 }(args); 759 }(); 760 761 alias ReturnType = SharedPtr!( 762 ElementType, 763 .DestructorType!( 764 ElementDestructorType!(), 765 DestructorType, 766 DestructorAllocatorType!AllocatorType 767 ), 768 ControlType 769 ); 770 771 auto m = ReturnType.MakeEmplace!(AllocatorType, supportGC).make(AllocatorType.init, forward!(args)); 772 773 return (m is null) 774 ? ReturnType(null) 775 : ReturnType(m.base, m.get); 776 } 777 778 /** 779 Constructs a `SharedPtr` with `element` as the pointer to the managed object. 780 781 Uses the specified `deleter` as the deleter. The expression `deleter(element)` must be well formed, have well-defined behavior and not throw any exceptions. 782 The construction of `deleter` and of the stored deleter from d must not throw exceptions. 783 784 Examples: 785 -------------------- 786 long deleted = -1; 787 auto x = SharedPtr!long.make(new long(123), (long* data){ 788 deleted = *data; 789 }); 790 assert(deleted == -1); 791 assert(*x == 123); 792 793 x = null; 794 assert(deleted == 123); 795 -------------------- 796 */ 797 public static auto make(AllocatorType = DefaultAllocator, bool supportGC = platformSupportGC, DeleterType)(ElementReferenceType element, DeleterType deleter) 798 if(isCallable!DeleterType){ 799 check_dtor(); 800 801 alias ReturnType = SharedPtr!( 802 ElementType, 803 .DestructorType!( 804 ElementDestructorType!(), 805 DestructorType, 806 DestructorAllocatorType!AllocatorType, 807 DestructorDeleterType!(ElementType, DeleterType) 808 ), 809 ControlType 810 ); 811 812 auto m = ReturnType.MakeDeleter!(DeleterType, AllocatorType, supportGC).make(AllocatorType.init, forward!deleter, forward!element); 813 814 return (m is null) 815 ? ReturnType(null) 816 : ReturnType(m.base, m.get); 817 } 818 819 820 /** 821 Constructs an object of array type `ElementType` including its array elements and wraps it in a `SharedPtr`. 822 823 Parameters: 824 n = Array length 825 826 args = parameters for constructor for each array element. 827 828 The array elements are constructed as if by the expression `emplace!ElementType(_payload, args)`, where _payload is an internal pointer to storage suitable to hold an object of type `ElementType`. 829 The storage is typically larger than `ElementType.sizeof * n` in order to use one allocation for both the control block and the each array element. 830 831 Examples: 832 -------------------- 833 auto arr = SharedPtr!(long[]).make(6, -1); 834 assert(arr.length == 6); 835 assert(arr.get.length == 6); 836 837 import std.algorithm : all; 838 assert(arr.get.all!(x => x == -1)); 839 840 for(int i = 0; i < 6; ++i) 841 arr.get[i] = i; 842 843 assert(arr.get == [0, 1, 2, 3, 4, 5]); 844 -------------------- 845 */ 846 public static auto make(AllocatorType = DefaultAllocator, bool supportGC = platformSupportGC, Args...)(const size_t n, auto ref Args args) 847 if(isDynamicArray!ElementType){ 848 check_dtor(); 849 850 alias ReturnType = SharedPtr!( 851 ElementType, 852 .DestructorType!( 853 ElementDestructorType!(), 854 DestructorType, 855 DestructorAllocatorType!AllocatorType 856 ), 857 ControlType 858 ); 859 auto m = ReturnType.MakeDynamicArray!(AllocatorType, supportGC).make(AllocatorType.init, n, forward!(args)); 860 861 return (m is null) 862 ? ReturnType(null) 863 : ReturnType(m.base, m.get); 864 } 865 866 867 868 /** 869 Constructs an object of type `ElementType` and wraps it in a `SharedPtr` using args as the parameter list for the constructor of `ElementType`. 870 871 The object is constructed as if by the expression `emplace!ElementType(_payload, forward!args)`, where _payload is an internal pointer to storage suitable to hold an object of type `ElementType`. 872 The storage is typically larger than `ElementType.sizeof` in order to use one allocation for both the control block and the `ElementType` object. 873 874 Examples: 875 -------------------- 876 auto a = allocatorObject(Mallocator.instance); 877 { 878 auto a = SharedPtr!long.alloc(a); 879 assert(a.get == 0); 880 881 auto b = SharedPtr!(const long).alloc(a, 2); 882 assert(b.get == 2); 883 } 884 885 { 886 static struct Struct{ 887 int i = 7; 888 889 this(int i)pure nothrow @safe @nogc{ 890 this.i = i; 891 } 892 } 893 894 auto s1 = SharedPtr!Struct.alloc(a); 895 assert(s1.get.i == 7); 896 897 auto s2 = SharedPtr!Struct.alloc(a, 123); 898 assert(s2.get.i == 123); 899 } 900 901 { 902 static interface Interface{ 903 } 904 static class Class : Interface{ 905 int i; 906 907 this(int i)pure nothrow @safe @nogc{ 908 this.i = i; 909 } 910 } 911 912 SharedPtr!Interface x = SharedPtr!Class.alloc(a, 3); 913 //assert(x.dynTo!Class.get.i == 3); 914 } 915 -------------------- 916 */ 917 public static auto alloc(bool supportGC = platformSupportGC, AllocatorType, Args...)(AllocatorType a, auto ref Args args) 918 if(!isDynamicArray!ElementType){ 919 check_dtor(); 920 921 alias ReturnType = SharedPtr!( 922 ElementType, 923 .DestructorType!( 924 ElementDestructorType!(), 925 DestructorType, 926 DestructorAllocatorType!AllocatorType 927 ), 928 ControlType 929 ); 930 auto m = ReturnType.MakeEmplace!(AllocatorType, supportGC).make(forward!(a, args)); 931 932 return (m is null) 933 ? ReturnType(null) 934 : ReturnType(m.base, m.get); 935 } 936 937 938 /** 939 Constructs a `SharedPtr` with `element` as the pointer to the managed object using `allocator` with state. 940 941 Uses the specified `deleter` as the deleter. The expression `deleter(element)` must be well formed, have well-defined behavior and not throw any exceptions. 942 The construction of `deleter` and of the stored deleter from d must not throw exceptions. 943 944 Examples: 945 -------------------- 946 auto a = allocatorObject(Mallocator.instance); 947 948 long deleted = -1; 949 auto x = SharedPtr!long.make(new long(123), (long* data){ 950 deleted = *data; 951 }, a); 952 assert(deleted == -1); 953 assert(*x == 123); 954 955 x = null; 956 assert(deleted == 123); 957 -------------------- 958 */ 959 public static auto alloc(bool supportGC = platformSupportGC, AllocatorType, DeleterType)(AllocatorType allocator, ElementReferenceType element, DeleterType deleter) 960 if(isCallable!DeleterType){ 961 check_dtor(); 962 963 alias ReturnType = SharedPtr!( 964 ElementType, 965 .DestructorType!( 966 ElementDestructorType!(), 967 DestructorType, 968 DestructorAllocatorType!AllocatorType, 969 DestructorDeleterType!(ElementType, DeleterType) 970 ), 971 ControlType 972 ); 973 auto m = ReturnType.MakeDeleter!(DeleterType, AllocatorType, supportGC).make(forward!allocator, forward!deleter, forward!element); 974 975 return (m is null) 976 ? ReturnType(null) 977 : ReturnType(m.base, m.get); 978 } 979 980 981 982 /** 983 Constructs an object of array type `ElementType` including its array elements and wraps it in a `SharedPtr`. 984 985 Parameters: 986 n = Array length 987 988 args = parameters for constructor for each array element. 989 990 The array elements are constructed as if by the expression `emplace!ElementType(_payload, args)`, where _payload is an internal pointer to storage suitable to hold an object of type `ElementType`. 991 The storage is typically larger than `ElementType.sizeof * n` in order to use one allocation for both the control block and the each array element. 992 993 Examples: 994 -------------------- 995 auto a = allocatorObject(Mallocator.instance); 996 auto arr = SharedPtr!(long[], DestructorType!(typeof(a))).alloc(a, 6, -1); 997 assert(arr.length == 6); 998 assert(arr.get.length == 6); 999 1000 import std.algorithm : all; 1001 assert(arr.get.all!(x => x == -1)); 1002 1003 for(int i = 0; i < 6; ++i) 1004 arr.get[i] = i; 1005 1006 assert(arr.get == [0, 1, 2, 3, 4, 5]); 1007 -------------------- 1008 */ 1009 public static auto alloc(bool supportGC = platformSupportGC, AllocatorType, Args...)(AllocatorType a, const size_t n, auto ref Args args) 1010 if(isDynamicArray!ElementType){ 1011 check_dtor(); 1012 1013 alias ReturnType = SharedPtr!( 1014 ElementType, 1015 .DestructorType!( 1016 ElementDestructorType!(), 1017 DestructorType, 1018 DestructorAllocatorType!AllocatorType 1019 ), 1020 ControlType 1021 ); 1022 auto m = ReturnType.MakeDynamicArray!(AllocatorType, supportGC).make(forward!(a, n, args)); 1023 1024 return (m is null) 1025 ? ReturnType(null) 1026 : ReturnType(m.base, m.get); 1027 } 1028 1029 1030 1031 /** 1032 Returns the number of different `SharedPtr` instances 1033 1034 Returns the number of different `SharedPtr` instances (`this` included) managing the current object or `0` if there is no managed object. 1035 1036 Examples: 1037 -------------------- 1038 SharedPtr!long x = null; 1039 1040 assert(x.useCount == 0); 1041 1042 x = SharedPtr!long.make(123); 1043 assert(x.useCount == 1); 1044 1045 auto y = x; 1046 assert(x.useCount == 2); 1047 1048 auto w1 = x.weak; //weak ptr 1049 assert(x.useCount == 2); 1050 1051 SharedPtr!long.WeakType w2 = x; //weak ptr 1052 assert(x.useCount == 2); 1053 1054 y = null; 1055 assert(x.useCount == 1); 1056 1057 x = null; 1058 assert(x.useCount == 0); 1059 assert(w1.useCount == 0); 1060 -------------------- 1061 */ 1062 public @property ControlType.Shared useCount(this This)()const scope nothrow @safe @nogc{ 1063 static if(is(This == shared)){ 1064 return this.lockSmartPtr!( 1065 (ref scope self) => self.useCount() 1066 )(); 1067 } 1068 else{ 1069 const control = this._control; 1070 1071 return (control is null) 1072 ? 0 1073 : control.count!false + 1; 1074 } 1075 1076 } 1077 1078 1079 /** 1080 Returns the number of different `SharedPtr.WeakType` instances 1081 1082 Returns the number of different `SharedPtr.WeakType` instances (`this` included) managing the current object or `0` if there is no managed object. 1083 1084 Examples: 1085 -------------------- 1086 SharedPtr!long x = null; 1087 assert(x.useCount == 0); 1088 assert(x.weakCount == 0); 1089 1090 x = SharedPtr!long.make(123); 1091 assert(x.useCount == 1); 1092 assert(x.weakCount == 0); 1093 1094 auto w = x.weak(); 1095 assert(x.useCount == 1); 1096 assert(x.weakCount == 1); 1097 -------------------- 1098 */ 1099 public @property ControlType.Weak weakCount(this This)()const scope nothrow @safe @nogc{ 1100 1101 static if(is(This == shared)){ 1102 return this.lockSmartPtr!( 1103 (ref scope self) => self.weakCount() 1104 )(); 1105 } 1106 else{ 1107 const control = this._control; 1108 1109 return (control is null) 1110 ? 0 1111 : control.count!true; 1112 } 1113 1114 } 1115 1116 1117 1118 /** 1119 Swap `this` with `rhs` 1120 1121 Examples: 1122 -------------------- 1123 { 1124 SharedPtr!long a = SharedPtr!long.make(1); 1125 SharedPtr!long b = SharedPtr!long.make(2); 1126 a.proxySwap(b); 1127 assert(*a == 2); 1128 assert(*b == 1); 1129 import std.algorithm : swap; 1130 swap(a, b); 1131 assert(*a == 1); 1132 assert(*b == 2); 1133 assert(a.useCount == 1); 1134 assert(b.useCount == 1); 1135 } 1136 -------------------- 1137 */ 1138 public void proxySwap(ref scope typeof(this) rhs)scope @trusted pure nothrow @nogc{ 1139 auto control = this._control; 1140 auto element = this._element; 1141 1142 this._control = rhs._control; 1143 this._set_element(rhs._element); 1144 1145 rhs._control = control; 1146 rhs._set_element(element); 1147 } 1148 1149 1150 1151 /** 1152 Returns the non `shared` `SharedPtr` pointer pointed-to by `shared` `this`. 1153 1154 Examples: 1155 -------------------- 1156 shared SharedPtr!(long) x = SharedPtr!(shared long).make(123); 1157 1158 { 1159 SharedPtr!(shared long) y = x.load(); 1160 assert(y.useCount == 2); 1161 1162 assert(y.get == 123); 1163 } 1164 -------------------- 1165 */ 1166 public UnqualSmartPtr!This load(MemoryOrder order = MemoryOrder.seq, this This)()scope{ 1167 1168 static if(is(This == shared)){ 1169 return this.lockSmartPtr!( 1170 (ref scope self) => self.load!order() 1171 )(); 1172 } 1173 else{ 1174 return typeof(return)(this); 1175 } 1176 } 1177 1178 1179 1180 /** 1181 Stores the non `shared` `SharedPtr` parameter `ptr` to `this`. 1182 1183 If `this` is shared then operation is atomic or guarded by mutex. 1184 1185 Template parameter `order` has type `core.atomic.MemoryOrder`. 1186 1187 Examples: 1188 -------------------- 1189 //null store: 1190 { 1191 shared x = SharedPtr!(shared long).make(123); 1192 assert(x.load.get == 123); 1193 1194 x.store(null); 1195 assert(x.useCount == 0); 1196 assert(x.load == null); 1197 } 1198 1199 //rvalue store: 1200 { 1201 shared x = SharedPtr!(shared long).make(123); 1202 assert(x.load.get == 123); 1203 1204 x.store(SharedPtr!(shared long).make(42)); 1205 assert(x.load.get == 42); 1206 } 1207 1208 //lvalue store: 1209 { 1210 shared x = SharedPtr!(shared long).make(123); 1211 auto y = SharedPtr!(shared long).make(42); 1212 1213 assert(x.load.get == 123); 1214 assert(y.load.get == 42); 1215 1216 x.store(y); 1217 assert(x.load.get == 42); 1218 assert(x.useCount == 2); 1219 } 1220 -------------------- 1221 */ 1222 public alias store = opAssign; 1223 1224 1225 1226 /** 1227 Stores the non `shared` `SharedPtr` pointer ptr in the `shared(SharedPtr)` pointed to by `this` and returns the value formerly pointed-to by this, atomically or with mutex. 1228 1229 Examples: 1230 -------------------- 1231 //lvalue exchange 1232 { 1233 shared x = SharedPtr!(shared long).make(123); 1234 auto y = SharedPtr!(shared long).make(42); 1235 1236 auto z = x.exchange(y); 1237 1238 assert(x.load.get == 42); 1239 assert(y.get == 42); 1240 assert(z.get == 123); 1241 } 1242 1243 //rvalue exchange 1244 { 1245 shared x = SharedPtr!(shared long).make(123); 1246 auto y = SharedPtr!(shared long).make(42); 1247 1248 import core.lifetime : move; 1249 auto z = x.exchange(move(y)); 1250 1251 assert(x.load.get == 42); 1252 assert(y == null); 1253 assert(z.get == 123); 1254 } 1255 1256 //null exchange (same as move) 1257 { 1258 shared x = SharedPtr!(shared long).make(123); 1259 1260 auto z = x.exchange(null); 1261 1262 assert(x.load == null); 1263 assert(z.get == 123); 1264 } 1265 1266 //swap: 1267 { 1268 shared x = SharedPtr!(shared long).make(123); 1269 auto y = SharedPtr!(shared long).make(42); 1270 1271 //opAssign is same as store 1272 import core.lifetime : move; 1273 y = x.exchange(move(y)); 1274 1275 assert(x.load.get == 42); 1276 assert(y.get == 123); 1277 } 1278 -------------------- 1279 */ 1280 public SharedPtr exchange(MemoryOrder order = MemoryOrder.seq, this This)(typeof(null))scope 1281 if(isMutable!This){ 1282 1283 static if(is(This == shared)) 1284 return this.lockSmartPtr!( 1285 (ref scope self) => self.exchange!order(null) 1286 )(); 1287 else{ 1288 return this._move; 1289 } 1290 } 1291 1292 /// ditto 1293 public SharedPtr exchange(MemoryOrder order = MemoryOrder.seq, Rhs, this This)(scope Rhs ptr)scope 1294 if( isSharedPtr!Rhs 1295 && isMoveAssignable!(Rhs, This) 1296 && !is(Rhs == shared) 1297 ){ 1298 static if(is(This == shared)) 1299 return this.lockSmartPtr!( 1300 (ref scope self, Rhs x) => self.exchange!order(x._move) 1301 )(ptr._move); 1302 else{ 1303 auto result = this._move; 1304 1305 return()@trusted{ 1306 this = ptr._move; 1307 return result._move; 1308 }(); 1309 } 1310 } 1311 1312 1313 1314 /** 1315 Same as `compareExchange`. 1316 1317 More info in c++ `std::atomic<std::shared_ptr>`. 1318 */ 1319 public alias compareExchangeStrong = compareExchange; 1320 1321 1322 1323 /** 1324 Same as `compareExchange`. 1325 1326 More info in c++ `std::atomic<std::shared_ptr>`. 1327 */ 1328 public alias compareExchangeWeak = compareExchange; 1329 1330 1331 1332 /** 1333 Compares the `SharedPtr` pointers pointed-to by `this` and `expected`. 1334 1335 If they are equivalent (store the same pointer value, and either share ownership of the same object or are both empty), assigns `desired` into `this` using the memory ordering constraints specified by `success` and returns `true`. 1336 If they are not equivalent, assigns `this` into `expected` using the memory ordering constraints specified by `failure` and returns `false`. 1337 1338 More info in c++ std::atomic<std::shared_ptr>. 1339 1340 Examples: 1341 -------------------- 1342 //fail 1343 { 1344 SharedPtr!long a = SharedPtr!long.make(123); 1345 SharedPtr!long b = SharedPtr!long.make(42); 1346 SharedPtr!long c = SharedPtr!long.make(666); 1347 1348 a.compareExchange(b, c); 1349 1350 assert(*a == 123); 1351 assert(*b == 123); 1352 assert(*c == 666); 1353 1354 } 1355 1356 //success 1357 { 1358 SharedPtr!long a = SharedPtr!long.make(123); 1359 SharedPtr!long b = a; 1360 SharedPtr!long c = SharedPtr!long.make(666); 1361 1362 a.compareExchange(b, c); 1363 1364 assert(*a == 666); 1365 assert(*b == 123); 1366 assert(*c == 666); 1367 } 1368 1369 //shared fail 1370 { 1371 shared SharedPtr!(shared long) a = SharedPtr!(shared long).make(123); 1372 SharedPtr!(shared long) b = SharedPtr!(shared long).make(42); 1373 SharedPtr!(shared long) c = SharedPtr!(shared long).make(666); 1374 1375 a.compareExchange(b, c); 1376 1377 auto tmp = a.exchange(null); 1378 assert(*tmp == 123); 1379 assert(*b == 123); 1380 assert(*c == 666); 1381 } 1382 1383 //shared success 1384 { 1385 SharedPtr!(shared long) b = SharedPtr!(shared long).make(123); 1386 shared SharedPtr!(shared long) a = b; 1387 SharedPtr!(shared long) c = SharedPtr!(shared long).make(666); 1388 1389 a.compareExchange(b, c); 1390 1391 auto tmp = a.exchange(null); 1392 assert(*tmp == 666); 1393 assert(*b == 123); 1394 assert(*c == 666); 1395 } 1396 1397 -------------------- 1398 */ 1399 public bool compareExchange 1400 (MemoryOrder success = MemoryOrder.seq, MemoryOrder failure = success, E, D, this This) 1401 (ref scope E expected, scope D desired)scope //@trusted 1402 if( isSharedPtr!E && !is(E == shared) 1403 && isSharedPtr!D && !is(D == shared) 1404 && isMoveAssignable!(D, This) 1405 && isCopyAssignable!(This, E) 1406 ){ 1407 static if(is(This == shared)){ 1408 import btl.internal.mutex : getMutex; 1409 1410 shared mutex = getMutex(this); 1411 1412 mutex.lock(); 1413 1414 alias Self = UnqualSmartPtr!This; 1415 1416 static assert(!is(Self == shared)); 1417 1418 Self* self = cast(Self*)&this; 1419 1420 if(*self == expected){ 1421 auto tmp = self._move; //destructor is called after mutex.unlock(); 1422 *self = desired._move; 1423 1424 mutex.unlock(); 1425 return true; 1426 } 1427 1428 auto tmp = expected._move; //destructor is called after mutex.unlock(); 1429 expected = *self; 1430 1431 mutex.unlock(); 1432 return false; 1433 } 1434 else{ 1435 if(this == expected){ 1436 this = desired._move; 1437 1438 return true; 1439 } 1440 expected = this; 1441 1442 return false; 1443 } 1444 } 1445 1446 1447 1448 /** 1449 Creates a new non weak `SharedPtr` that shares ownership of the managed object (must be `SharedPtr.WeakType`). 1450 1451 If there is no managed object, i.e. this is empty or this is `expired`, then the returned `SharedPtr` is empty. 1452 Method exists only if `SharedPtr` is `isWeak` 1453 1454 Examples: 1455 -------------------- 1456 { 1457 SharedPtr!long x = SharedPtr!long.make(123); 1458 1459 auto w = x.weak; //weak ptr 1460 1461 SharedPtr!long y = w.lock; 1462 1463 assert(x == y); 1464 assert(x.useCount == 2); 1465 assert(y.get == 123); 1466 } 1467 1468 { 1469 SharedPtr!long x = SharedPtr!long.make(123); 1470 1471 auto w = x.weak; //weak ptr 1472 1473 assert(w.expired == false); 1474 1475 x = SharedPtr!long.make(321); 1476 1477 assert(w.expired == true); 1478 1479 SharedPtr!long y = w.lock; 1480 1481 assert(y == null); 1482 } 1483 -------------------- 1484 */ 1485 public SharedType lock()()scope 1486 if(isCopyConstructable!(typeof(this), SharedType)){ 1487 return typeof(return)(this); 1488 } 1489 1490 1491 1492 /** 1493 Equivalent to `useCount() == 0` (must be `SharedPtr.WeakType`). 1494 1495 Method exists only if `SharedPtr` is `isWeak` 1496 1497 Examples: 1498 -------------------- 1499 { 1500 SharedPtr!long x = SharedPtr!long.make(123); 1501 1502 auto wx = x.weak; //weak pointer 1503 1504 assert(wx.expired == false); 1505 1506 x = null; 1507 1508 assert(wx.expired == true); 1509 } 1510 -------------------- 1511 */ 1512 public @property bool expired(this This)()scope const nothrow @safe @nogc{ 1513 return (this.useCount == 0); 1514 } 1515 1516 1517 1518 static if(!isWeak){ 1519 /** 1520 Operator *, same as method 'get'. 1521 1522 Examples: 1523 -------------------- 1524 SharedPtr!long x = SharedPtr!long.make(123); 1525 assert(*x == 123); 1526 (*x = 321); 1527 assert(*x == 321); 1528 const y = x; 1529 assert(*y == 321); 1530 assert(*x == 321); 1531 static assert(is(typeof(*y) == const long)); 1532 -------------------- 1533 */ 1534 public template opUnary(string op : "*") 1535 if(op == "*"){ //doc 1536 alias opUnary = get; 1537 } 1538 1539 1540 1541 /** 1542 Get reference to managed object of `ElementType` or value if `ElementType` is reference type (class or interface) or dynamic array. 1543 1544 Doesn't increment useCount, is inherently unsafe. 1545 1546 Examples: 1547 -------------------- 1548 SharedPtr!long x = SharedPtr!long.make(123); 1549 assert(x.get == 123); 1550 x.get = 321; 1551 assert(x.get == 321); 1552 const y = x; 1553 assert(y.get == 321); 1554 assert(x.get == 321); 1555 static assert(is(typeof(y.get) == const long)); 1556 -------------------- 1557 */ 1558 static if(referenceElementType){ 1559 public @property inout(ElementType) get()inout return pure nothrow @system @nogc{ 1560 assert((this._element is null) <= (this._control is null)); 1561 return this._element; 1562 } 1563 } 1564 else static if(is(Unqual!ElementType == void)){ 1565 /// ditto 1566 public @property inout(ElementType) get()inout scope pure nothrow @safe @nogc{ 1567 } 1568 } 1569 else{ 1570 /// ditto 1571 public @property ref inout(ElementType) get()inout return pure nothrow @system @nogc{ 1572 assert((this._element is null) <= (this._control is null)); 1573 return *cast(inout(ElementType)*)this._element; 1574 } 1575 } 1576 } 1577 1578 1579 1580 /** 1581 Get pointer to managed object of `ElementType` or reference if `ElementType` is reference type (class or interface) or dynamic array. 1582 1583 If `this` is weak expired pointer then return null. 1584 1585 Doesn't increment useCount, is inherently unsafe. 1586 1587 Examples: 1588 -------------------- 1589 { 1590 SharedPtr!long x = SharedPtr!long.make(123); 1591 assert(*x.element == 123); 1592 1593 x.get = 321; 1594 assert(*x.element == 321); 1595 1596 const y = x; 1597 assert(*y.element == 321); 1598 assert(*x.element == 321); 1599 1600 static assert(is(typeof(y.element) == const(long)*)); 1601 } 1602 1603 { 1604 auto x = SharedPtr!(long[]).make(6, 42); 1605 assert(x.element[0] == 42); 1606 static assert(is(typeof(x.element) == long[])); 1607 } 1608 1609 { 1610 auto s = SharedPtr!long.make(42); 1611 const w = s.weak; 1612 1613 assert(*w.element == 42); 1614 1615 s = null; 1616 assert(w.element is null); 1617 } 1618 1619 { 1620 auto s = SharedPtr!long.make(42); 1621 auto w = s.weak; 1622 1623 scope const p = w.element; 1624 1625 s = null; 1626 assert(w.element is null); 1627 1628 assert(p !is null); //p is dangling pointer! 1629 } 1630 -------------------- 1631 */ 1632 public @property ElementReferenceTypeImpl!(GetElementType!This) element(this This)()return pure nothrow @system @nogc 1633 if(!is(This == shared)){ 1634 assert((this._element is null) <= (this._control is null)); 1635 static if(isWeak) 1636 return (cast(const)this).expired 1637 ? null 1638 : this._element; 1639 else 1640 return this._element; 1641 } 1642 1643 1644 1645 /** 1646 Get pointer to managed object of `ElementType` or reference if `ElementType` is reference type (class or interface) or pointer to first dynamic array element. 1647 1648 If `this` is weak expired pointer then return null. 1649 1650 Doesn't increment useCount, is inherently unsafe. 1651 1652 Examples: 1653 -------------------- 1654 { 1655 SharedPtr!long x = SharedPtr!long.make(123); 1656 assert(*x.ptr == 123); 1657 1658 x.get = 321; 1659 assert(*x.ptr == 321); 1660 1661 const y = x; 1662 assert(*y.ptr == 321); 1663 assert(*x.ptr == 321); 1664 1665 static assert(is(typeof(y.ptr) == const(long)*)); 1666 } 1667 1668 { 1669 auto x = SharedPtr!(long[]).make(6, 42); 1670 assert(*x.ptr == 42); 1671 static assert(is(typeof(x.ptr) == long*)); 1672 } 1673 1674 { 1675 auto s = SharedPtr!long.make(42); 1676 const w = s.weak; 1677 1678 assert(*w.ptr == 42); 1679 1680 s = null; 1681 assert(w.ptr is null); 1682 } 1683 1684 { 1685 auto s = SharedPtr!long.make(42); 1686 auto w = s.weak; 1687 1688 scope const p = w.ptr; 1689 1690 s = null; 1691 assert(w.ptr is null); 1692 1693 assert(p !is null); //p is dangling pointer! 1694 } 1695 -------------------- 1696 */ 1697 public @property ElementPointerTypeImpl!(GetElementType!This) ptr(this This)()return pure nothrow @system @nogc 1698 if(!is(This == shared)){ 1699 assert((this._element is null) <= (this._control is null)); 1700 1701 auto get_ptr(){ 1702 static if(isDynamicArray!ElementType) 1703 return this._element.ptr; 1704 else 1705 return this._element; 1706 } 1707 1708 1709 1710 static if(isWeak) 1711 return (cast(const)this).expired 1712 ? null 1713 : get_ptr(); 1714 else 1715 return get_ptr(); 1716 } 1717 1718 1719 1720 /** 1721 Returns length of dynamic array (isDynamicArray!ElementType == true). 1722 1723 Examples: 1724 -------------------- 1725 auto x = SharedPtr!(int[]).make(10, -1); 1726 assert(x.length == 10); 1727 assert(x.get.length == 10); 1728 1729 import std.algorithm : all; 1730 assert(x.get.all!(i => i == -1)); 1731 -------------------- 1732 */ 1733 static if(isDynamicArray!ElementType) 1734 public @property size_t length()const scope pure nothrow @safe @nogc{ 1735 return this._element.length; 1736 } 1737 1738 1739 /** 1740 Returns weak pointer (must have weak counter). 1741 1742 Examples: 1743 -------------------- 1744 SharedPtr!long x = SharedPtr!long.make(123); 1745 assert(x.useCount == 1); 1746 1747 auto wx = x.weak; //weak pointer 1748 assert(wx.expired == false); 1749 assert(wx.lock.get == 123); 1750 assert(wx.useCount == 1); 1751 1752 x = null; 1753 assert(wx.expired == true); 1754 assert(wx.useCount == 0); 1755 -------------------- 1756 */ 1757 public WeakType weak()()scope 1758 if(isCopyConstructable!(typeof(this), WeakType)){ 1759 static if(hasWeakCounter){ 1760 return typeof(return)(this); 1761 } 1762 } 1763 1764 1765 1766 /** 1767 Checks if `this` stores a non-null pointer, i.e. whether `this != null`. 1768 1769 Examples: 1770 -------------------- 1771 SharedPtr!long x = SharedPtr!long.make(123); 1772 assert(cast(bool)x); //explicit cast 1773 assert(x); //implicit cast 1774 x = null; 1775 assert(!cast(bool)x); //explicit cast 1776 assert(!x); //implicit cast 1777 -------------------- 1778 */ 1779 public bool opCast(To : bool)()const scope pure nothrow @safe @nogc 1780 if(is(To : bool)){ //docs 1781 return (this != null); 1782 } 1783 1784 1785 /** 1786 Support for quelifier cast. 1787 */ 1788 public ref To opCast(To, this This)()return scope pure nothrow @nogc 1789 if(is(immutable To : immutable typeof(this))){ 1790 static if(is(This : To)){ 1791 return *(()@trusted => cast(To*)&this )(); 1792 } 1793 else{ 1794 return *(()@system => cast(To*)&this )(); 1795 } 1796 } 1797 1798 1799 /** 1800 Cast `this` to different type `To` when `isSharedPtr!To`. 1801 1802 BUG: qualfied variable of struct with dtor cannot be inside other struct (generated dtor will use opCast to mutable before dtor call ). opCast is renamed to opCastImpl 1803 1804 Examples: 1805 -------------------- 1806 SharedPtr!long x = SharedPtr!long.make(123); 1807 auto y = cast(SharedPtr!(const long))x; 1808 auto z = cast(const SharedPtr!long)x; 1809 auto u = cast(const SharedPtr!(const long))x; 1810 assert(x.useCount == 4); 1811 -------------------- 1812 */ 1813 public To opCastImpl(To, this This)()scope 1814 if(isSharedPtr!To && !is(This == shared)){ 1815 1816 return To(this); 1817 } 1818 1819 1820 /** 1821 Operator == and != . 1822 Compare pointers. 1823 1824 Examples: 1825 -------------------- 1826 { 1827 SharedPtr!long x = SharedPtr!long.make(0); 1828 assert(x != null); 1829 x = null; 1830 assert(x == null); 1831 } 1832 1833 { 1834 SharedPtr!long x = SharedPtr!long.make(123); 1835 SharedPtr!long y = SharedPtr!long.make(123); 1836 assert(x == x); 1837 assert(y == y); 1838 assert(x != y); 1839 } 1840 1841 { 1842 SharedPtr!long x; 1843 SharedPtr!(const long) y; 1844 assert(x == x); 1845 assert(y == y); 1846 assert(x == y); 1847 } 1848 1849 { 1850 SharedPtr!long x = SharedPtr!long.make(123); 1851 SharedPtr!long y = SharedPtr!long.make(123); 1852 assert(x == x.element); 1853 assert(y.element == y); 1854 assert(x != y.element); 1855 } 1856 -------------------- 1857 */ 1858 public bool opEquals(typeof(null) nil)const @safe scope pure nothrow @nogc{ 1859 static if(isDynamicArray!ElementType) 1860 return (this._element.length == 0); 1861 else 1862 return (this._element is null); 1863 } 1864 1865 /// ditto 1866 public bool opEquals(Rhs)(scope auto ref const Rhs rhs)const @safe scope pure nothrow @nogc 1867 if(isSharedPtr!Rhs && !is(Rhs == shared)){ 1868 return this.opEquals(rhs._element); 1869 } 1870 1871 /// ditto 1872 public bool opEquals(Elm)(scope const Elm elm)const @safe scope pure nothrow @nogc 1873 if(is(Elm : GetElementReferenceType!(typeof(this)))){ 1874 static if(isDynamicArray!ElementType){ 1875 static assert(isDynamicArray!Elm); 1876 1877 if(this._element.length != elm.length) 1878 return false; 1879 1880 if(this._element.ptr is elm.ptr) 1881 return true; 1882 1883 return (this._element.length == 0); 1884 } 1885 else{ 1886 return (this._element is elm); 1887 } 1888 } 1889 1890 1891 1892 /** 1893 Operators <, <=, >, >= for `SharedPtr`. 1894 1895 Compare address of payload. 1896 1897 Examples: 1898 -------------------- 1899 { 1900 const a = SharedPtr!long.make(42); 1901 const b = SharedPtr!long.make(123); 1902 const n = SharedPtr!long.init; 1903 1904 assert(a <= a); 1905 assert(a >= a); 1906 1907 assert((a < b) == !(a >= b)); 1908 assert((a > b) == !(a <= b)); 1909 1910 assert(a > n); 1911 assert(a > null); 1912 1913 assert(n < a); 1914 assert(null < a); 1915 } 1916 1917 { 1918 const a = SharedPtr!long.make(42); 1919 const b = SharedPtr!long.make(123); 1920 1921 assert(a <= a.element); 1922 assert(a.element >= a); 1923 1924 assert((a < b.element) == !(a.element >= b)); 1925 assert((a > b.element) == !(a.element <= b)); 1926 } 1927 -------------------- 1928 */ 1929 public sizediff_t opCmp(typeof(null) nil)const @trusted scope pure nothrow @nogc{ 1930 static if(isDynamicArray!ElementType){ 1931 return this._element.length; 1932 } 1933 else{ 1934 return (cast(const void*)this._element) - (cast(const void*)null); 1935 } 1936 1937 } 1938 1939 /// ditto 1940 public sizediff_t opCmp(Elm)(scope const Elm elm)const @trusted scope pure nothrow @nogc 1941 if(is(Elm : GetElementReferenceType!(typeof(this)))){ 1942 static if(isDynamicArray!ElementType){ 1943 const void* lhs = cast(const void*)(this._element.ptr + this._element.length); 1944 const void* rhs = cast(const void*)(elm.ptr + elm.length); 1945 1946 return lhs - rhs; 1947 } 1948 else{ 1949 return (cast(const void*)this._element) - (cast(const void*)elm); 1950 } 1951 } 1952 1953 /// ditto 1954 public sizediff_t opCmp(Rhs)(scope auto ref const Rhs rhs)const @trusted scope pure nothrow @nogc 1955 if(isSharedPtr!Rhs && !is(Rhs == shared)){ 1956 return this.opCmp(rhs._element); 1957 } 1958 1959 1960 1961 /** 1962 Generate hash 1963 1964 Return: 1965 Address of payload as `size_t` 1966 1967 Examples: 1968 -------------------- 1969 { 1970 SharedPtr!long x = SharedPtr!long.make(123); 1971 SharedPtr!long y = SharedPtr!long.make(123); 1972 assert(x.toHash == x.toHash); 1973 assert(y.toHash == y.toHash); 1974 assert(x.toHash != y.toHash); 1975 SharedPtr!(const long) z = x; 1976 assert(x.toHash == z.toHash); 1977 } 1978 { 1979 SharedPtr!long x; 1980 SharedPtr!(const long) y; 1981 assert(x.toHash == x.toHash); 1982 assert(y.toHash == y.toHash); 1983 assert(x.toHash == y.toHash); 1984 } 1985 -------------------- 1986 */ 1987 public @property size_t toHash()@trusted scope const pure nothrow @nogc { 1988 static if(isDynamicArray!ElementType) 1989 return cast(size_t)cast(void*)(this._element.ptr + this._element.length); 1990 else 1991 return cast(size_t)cast(void*)this._element; 1992 } 1993 1994 1995 1996 private ControlType* _control; 1997 private ElementReferenceType _element; 1998 1999 private void _set_element(ElementReferenceType e)pure nothrow @system @nogc{ 2000 static if(isMutable!ElementReferenceType) 2001 this._element = e; 2002 else 2003 (*cast(Unqual!ElementReferenceType*)&this._element) = cast(Unqual!ElementReferenceType)e; 2004 } 2005 2006 private void _const_set_element(ElementReferenceType e)const pure nothrow @system @nogc{ 2007 auto self = cast(Unqual!(typeof(this))*)&this; 2008 2009 static if(isMutable!ElementReferenceType) 2010 self._element = e; 2011 else 2012 (*cast(Unqual!ElementReferenceType*)&self._element) = cast(Unqual!ElementReferenceType)e; 2013 } 2014 2015 private void _const_set_counter(ControlType* c)const pure nothrow @system @nogc{ 2016 auto self = cast(Unqual!(typeof(this))*)&this; 2017 2018 self._control = c; 2019 } 2020 2021 private void _release()scope{ 2022 if(false){ 2023 DestructorType dt; 2024 dt(null); 2025 } 2026 2027 import std.traits : hasIndirections; 2028 import core.memory : GC; 2029 2030 if(this._control !is null) 2031 this._control.release!isWeak; 2032 } 2033 2034 private void _reset()scope pure nothrow @system @nogc{ 2035 this._control = null; 2036 this._set_element(null); 2037 } 2038 2039 private void _const_reset()scope const pure nothrow @system @nogc{ 2040 this._const_set_counter(null); 2041 this._const_set_element(null); 2042 } 2043 2044 package auto _move()@trusted{ 2045 auto e = this._element; 2046 auto c = this._control; 2047 this._const_reset(); 2048 2049 return typeof(this)(c, e); 2050 } 2051 2052 private alias MakeEmplace(AllocatorType, bool supportGC) = .MakeEmplace!( 2053 _Type, 2054 _DestructorType, 2055 _ControlType, 2056 AllocatorType, 2057 supportGC 2058 ); 2059 2060 private alias MakeDynamicArray(AllocatorType, bool supportGC) = .MakeDynamicArray!( 2061 _Type, 2062 _DestructorType, 2063 _ControlType, 2064 AllocatorType, 2065 supportGC 2066 ); 2067 2068 private alias MakeDeleter(DeleterType, AllocatorType, bool supportGC) = .MakeDeleter!( 2069 _Type, 2070 _DestructorType, 2071 _ControlType, 2072 DeleterType, 2073 AllocatorType, 2074 supportGC 2075 ); 2076 2077 package alias SmartPtr = .SmartPtr; 2078 2079 /**/ 2080 package alias ChangeElementType(T) = SharedPtr!( 2081 CopyTypeQualifiers!(ElementType, T), 2082 DestructorType, 2083 ControlType, 2084 isWeak 2085 ); 2086 } 2087 2088 } 2089 2090 2091 2092 /// Alias to `SharedPtr` with different order of template parameters 2093 public template SharedPtr( 2094 _Type, 2095 _ControlType, 2096 _DestructorType = DestructorType!_Type, 2097 bool _weakPtr = false 2098 ) 2099 if(isControlBlock!_ControlType && isDestructorType!_DestructorType){ 2100 alias SharedPtr = .SharedPtr!(_Type, _DestructorType, _ControlType, _weakPtr); 2101 } 2102 2103 /// 2104 unittest{ 2105 static class Foo{ 2106 int i; 2107 2108 this(int i)pure nothrow @safe @nogc{ 2109 this.i = i; 2110 } 2111 } 2112 2113 static class Bar : Foo{ 2114 double d; 2115 2116 this(int i, double d)pure nothrow @safe @nogc{ 2117 super(i); 2118 this.d = d; 2119 } 2120 } 2121 2122 static class Zee : Bar{ 2123 bool b; 2124 2125 this(int i, double d, bool b)pure nothrow @safe @nogc{ 2126 super(i, d); 2127 this.b = b; 2128 } 2129 2130 ~this()nothrow @system{ 2131 } 2132 } 2133 2134 ///simple: 2135 { 2136 SharedPtr!long a = SharedPtr!long.make(42); 2137 assert(a.useCount == 1); 2138 2139 SharedPtr!(const long) b = a; 2140 assert(a.useCount == 2); 2141 2142 SharedPtr!long.WeakType w = a.weak; //or WeakPtr!long 2143 assert(a.useCount == 2); 2144 assert(a.weakCount == 1); 2145 2146 SharedPtr!long c = w.lock; 2147 assert(a.useCount == 3); 2148 assert(a.weakCount == 1); 2149 2150 assert(*c == 42); 2151 assert(c.get == 42); 2152 } 2153 2154 ///polymorphism and aliasing: 2155 { 2156 ///create SharedPtr 2157 SharedPtr!Foo foo = SharedPtr!Bar.make(42, 3.14); 2158 SharedPtr!Zee zee = SharedPtr!Zee.make(42, 3.14, false); 2159 2160 ///dynamic cast: 2161 SharedPtr!Bar bar = dynCast!Bar(foo); 2162 assert(bar != null); 2163 assert(foo.useCount == 2); 2164 2165 ///this doesnt work because Foo destructor attributes are more restrictive then Zee's: 2166 //SharedPtr!Foo x = zee; 2167 2168 ///this does work: 2169 SharedPtr!(Foo, DestructorType!(Foo, Zee)) x = zee; 2170 assert(zee.useCount == 2); 2171 2172 ///aliasing (shared ptr `d` share ref counting with `bar`): 2173 SharedPtr!double d = SharedPtr!double(bar, &bar.get.d); 2174 assert(d != null); 2175 assert(*d == 3.14); 2176 assert(foo.useCount == 3); 2177 } 2178 2179 2180 ///multi threading: 2181 { 2182 ///create SharedPtr with atomic ref counting 2183 SharedPtr!(shared Foo) foo = SharedPtr!(shared Bar).make(42, 3.14); 2184 2185 ///this doesnt work: 2186 //foo.get.i += 1; 2187 2188 import core.atomic : atomicFetchAdd; 2189 atomicFetchAdd(foo.get.i, 1); 2190 assert(foo.get.i == 43); 2191 2192 2193 ///creating `shared(SharedPtr)`: 2194 shared SharedPtr!(shared Bar) bar = share(dynCast!Bar(foo)); 2195 2196 ///`shared(SharedPtr)` is not lock free but `RcPtr` is lock free. 2197 static assert(typeof(bar).isLockFree == false); 2198 2199 ///multi thread operations (`load`, `store`, `exchange` and `compareExchange`): 2200 SharedPtr!(shared Bar) bar2 = bar.load(); 2201 assert(bar2 != null); 2202 assert(bar2.useCount == 3); 2203 2204 SharedPtr!(shared Bar) bar3 = bar.exchange(null); 2205 assert(bar3 != null); 2206 assert(bar3.useCount == 3); 2207 } 2208 2209 ///dynamic array: 2210 { 2211 import std.algorithm : all, equal; 2212 2213 SharedPtr!(long[]) a = SharedPtr!(long[]).make(10, -1); 2214 assert(a.length == 10); 2215 assert(a.get.length == 10); 2216 assert(a.get.all!(x => x == -1)); 2217 2218 for(int i = 0; i < a.length; ++i){ 2219 a.get[i] = i; 2220 } 2221 assert(a.get[] == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); 2222 2223 ///aliasing: 2224 SharedPtr!long a6 = SharedPtr!long(a, &a.get[6]); 2225 assert(*a6 == a.get[6]); 2226 } 2227 } 2228 2229 2230 pure nothrow @nogc unittest{ 2231 2232 static class Foo{ 2233 int i; 2234 2235 this(int i)pure nothrow @safe @nogc{ 2236 this.i = i; 2237 } 2238 } 2239 2240 static class Bar : Foo{ 2241 double d; 2242 2243 this(int i, double d)pure nothrow @safe @nogc{ 2244 super(i); 2245 this.d = d; 2246 } 2247 } 2248 2249 //implicit qualifier cast 2250 { 2251 SharedPtr!(const Foo) foo = SharedPtr!Foo.make(42); 2252 assert(foo.get.i == 42); 2253 assert(foo.useCount == 1); 2254 2255 const SharedPtr!Foo foo2 = foo; 2256 assert(foo2.get.i == 42); 2257 assert(foo.useCount == 2); 2258 2259 } 2260 2261 //polymorphic classes: 2262 { 2263 SharedPtr!Foo foo = SharedPtr!Bar.make(42, 3.14); 2264 assert(foo != null); 2265 assert(foo.useCount == 1); 2266 assert(foo.get.i == 42); 2267 2268 //dynamic cast: 2269 { 2270 SharedPtr!Bar bar = dynCast!Bar(foo); 2271 assert(foo.useCount == 2); 2272 2273 assert(bar.get.i == 42); 2274 assert(bar.get.d == 3.14); 2275 } 2276 2277 } 2278 2279 //aliasing: 2280 { 2281 SharedPtr!Foo foo = SharedPtr!Bar.make(42, 3.14); 2282 assert(foo.useCount == 1); 2283 2284 auto x = SharedPtr!int(foo, &foo.get.i); 2285 assert(foo.useCount == 2); 2286 assert(x.useCount == 2); 2287 2288 assert(*x == 42); 2289 } 2290 2291 //weak references: 2292 { 2293 auto x = SharedPtr!double.make(3.14); 2294 assert(x.useCount == 1); 2295 assert(x.weakCount == 0); 2296 2297 auto w = x.weak(); //weak pointer 2298 assert(x.useCount == 1); 2299 assert(x.weakCount == 1); 2300 assert(*w.lock == 3.14); 2301 2302 SharedPtr!double.WeakType w2 = x; 2303 assert(x.useCount == 1); 2304 assert(x.weakCount == 2); 2305 2306 assert(w2.expired == false); 2307 x = null; 2308 assert(w2.expired == true); 2309 } 2310 2311 //dynamic array 2312 { 2313 import std.algorithm : all; 2314 2315 { 2316 auto arr = SharedPtr!(long[]).make(10, -1); 2317 2318 assert(arr.length == 10); 2319 assert(arr.get.all!(x => x == -1)); 2320 } 2321 2322 { 2323 auto arr = SharedPtr!(long[]).make(8); 2324 assert(arr.length == 8); 2325 assert(arr.get.all!(x => x == long.init)); 2326 } 2327 } 2328 2329 //static array 2330 { 2331 import std.algorithm : all; 2332 2333 { 2334 auto arr = SharedPtr!(long[4]).make(-1); 2335 assert(arr.get[].all!(x => x == -1)); 2336 2337 } 2338 2339 { 2340 long[4] tmp = [0, 1, 2, 3]; 2341 auto arr = SharedPtr!(long[4]).make(tmp); 2342 assert(arr.get[] == tmp[]); 2343 } 2344 } 2345 2346 } 2347 2348 /// 2349 pure nothrow @safe @nogc unittest{ 2350 //make SharedPtr object 2351 static struct Foo{ 2352 int i; 2353 2354 this(int i)pure nothrow @safe @nogc{ 2355 this.i = i; 2356 } 2357 } 2358 2359 { 2360 auto foo = SharedPtr!Foo.make(42); 2361 auto foo2 = SharedPtr!Foo.make!Mallocator(42); //explicit stateless allocator 2362 } 2363 2364 { 2365 import std.experimental.allocator : make, dispose; 2366 2367 static void deleter(long* x)pure nothrow @trusted @nogc{ 2368 Mallocator.instance.dispose(x); 2369 } 2370 long* element = Mallocator.instance.make!long; 2371 2372 auto x = SharedPtr!long.make(element, &deleter); 2373 } 2374 2375 { 2376 auto arr = SharedPtr!(long[]).make(10); //dynamic array with length 10 2377 assert(arr.length == 10); 2378 } 2379 } 2380 2381 /// 2382 nothrow unittest{ 2383 //alloc SharedPtr object 2384 import std.experimental.allocator : make, dispose, allocatorObject; 2385 2386 auto allocator = allocatorObject(Mallocator.instance); 2387 2388 { 2389 auto x = SharedPtr!long.alloc(allocator, 42); 2390 } 2391 2392 { 2393 static void deleter(long* x)pure nothrow @trusted @nogc{ 2394 Mallocator.instance.dispose(x); 2395 } 2396 long* element = Mallocator.instance.make!long; 2397 auto x = SharedPtr!long.alloc(allocator, element, &deleter); 2398 } 2399 2400 { 2401 auto arr = SharedPtr!(long[]).alloc(allocator, 10); //dynamic array with length 10 2402 assert(arr.length == 10); 2403 } 2404 2405 } 2406 2407 2408 2409 /** 2410 Weak pointer. 2411 2412 Alias to `SharedPtr.WeakType`. 2413 */ 2414 public template WeakPtr( 2415 _Type, 2416 _DestructorType = DestructorType!_Type, 2417 _ControlType = ControlBlockDeduction!(_Type, SharedControlBlock), 2418 ) 2419 if(isControlBlock!_ControlType && isDestructorType!_DestructorType){ 2420 alias WeakPtr = .SharedPtr!(_Type, _DestructorType, _ControlType, true); 2421 } 2422 2423 /// ditto 2424 public template WeakPtr( 2425 _Type, 2426 _ControlType, 2427 _DestructorType = DestructorType!_Type, 2428 ) 2429 if(isControlBlock!_ControlType && isDestructorType!_DestructorType){ 2430 alias WeakPtr = .SharedPtr!(_Type, _DestructorType, _ControlType, true); 2431 } 2432 2433 2434 2435 //make: 2436 pure nothrow @nogc unittest{ 2437 enum bool supportGC = true; 2438 2439 // 2440 { 2441 auto s = SharedPtr!long.make(42); 2442 } 2443 2444 { 2445 auto s = SharedPtr!long.make!(DefaultAllocator, supportGC)(42); 2446 } 2447 2448 { 2449 auto s = SharedPtr!(long, shared(SharedControlBlock)).make!(DefaultAllocator, supportGC)(42); 2450 } 2451 2452 // dynamic array: 2453 { 2454 auto s = SharedPtr!(long[]).make(10, 42); 2455 assert(s.length == 10); 2456 } 2457 { 2458 auto s = SharedPtr!(long[]).make!(DefaultAllocator, supportGC)(10, 42); 2459 assert(s.length == 10); 2460 } 2461 { 2462 auto s = SharedPtr!(long[], shared(SharedControlBlock)).make!(DefaultAllocator, supportGC)(10, 42); 2463 assert(s.length == 10); 2464 } 2465 2466 // deleter: 2467 long x = 42; 2468 2469 static void deleter(long* var)nothrow{ 2470 (*var) += 1; 2471 } 2472 2473 { 2474 auto s = SharedPtr!long.make(&x, &deleter); 2475 } 2476 assert(x == 43); 2477 2478 { 2479 auto s = SharedPtr!(long).make!(DefaultAllocator, supportGC)(&x, &deleter); 2480 } 2481 assert(x == 44); 2482 2483 { 2484 auto s = SharedPtr!(long, shared(SharedControlBlock)).make!(DefaultAllocator, supportGC)(&x, &deleter); 2485 } 2486 assert(x == 45); 2487 } 2488 2489 //alloc: 2490 nothrow unittest{ 2491 import std.experimental.allocator : allocatorObject; 2492 2493 auto a = allocatorObject(Mallocator.instance); 2494 enum bool supportGC = true; 2495 2496 // 2497 { 2498 auto s = SharedPtr!long.alloc(a, 42); 2499 } 2500 2501 { 2502 auto s = SharedPtr!long.alloc!supportGC(a, 42); 2503 } 2504 2505 { 2506 auto s = SharedPtr!(long, shared(SharedControlBlock)).alloc!supportGC(a, 42); 2507 } 2508 2509 // dynamic array: 2510 { 2511 auto s = SharedPtr!(long[]).alloc(a, 10, 42); 2512 assert(s.length == 10); 2513 } 2514 { 2515 auto s = SharedPtr!(long[]).alloc!supportGC(a, 10, 42); 2516 assert(s.length == 10); 2517 } 2518 { 2519 auto s = SharedPtr!(long[], shared(SharedControlBlock)).alloc!supportGC(a, 10, 42); 2520 assert(s.length == 10); 2521 } 2522 2523 // deleter: 2524 long x = 42; 2525 2526 static void deleter(long* var)nothrow{ 2527 (*var) += 1; 2528 } 2529 2530 { 2531 auto s = SharedPtr!long.alloc(a, &x, &deleter); 2532 } 2533 assert(x == 43); 2534 2535 { 2536 auto s = SharedPtr!(long).alloc!supportGC(a, &x, &deleter); 2537 } 2538 assert(x == 44); 2539 2540 { 2541 auto s = SharedPtr!(long, shared(SharedControlBlock)).alloc!supportGC(a, &x, &deleter); 2542 } 2543 assert(x == 45); 2544 } 2545 2546 2547 2548 /** 2549 Dynamic cast for shared pointers if `ElementType` is class with D linkage. 2550 2551 Move instance of `SharedPtr` whose stored pointer is obtained from `ptr`'s stored pointer using a dynaic cast expression. 2552 2553 If `ptr` is null or dynamic cast fail then result `SharedPtr` is null. 2554 Otherwise, the new `SharedPtr` will share ownership with the initial value of `ptr`. 2555 */ 2556 public UnqualSmartPtr!Ptr.ChangeElementType!T dynCastMove(T, Ptr)(scope auto ref Ptr ptr) 2557 if( isSharedPtr!Ptr && !is(Ptr == shared) && !Ptr.isWeak 2558 && isClassOrInterface!T && __traits(getLinkage, T) == "D" 2559 && isClassOrInterface!(Ptr.ElementType) && __traits(getLinkage, Ptr.ElementType) == "D" 2560 ){ 2561 import std.traits : CopyTypeQualifiers; 2562 import core.lifetime : forward, move; 2563 2564 alias Return = typeof(return); 2565 2566 if(auto element = dynCastElement!T(ptr._element)){ 2567 return (()@trusted => Return(move(ptr), element) )(); 2568 } 2569 2570 return typeof(return).init; 2571 } 2572 2573 2574 /// 2575 unittest{ 2576 static class Foo{ 2577 int i; 2578 2579 this(int i)pure nothrow @safe @nogc{ 2580 this.i = i; 2581 } 2582 } 2583 2584 static class Bar : Foo{ 2585 double d; 2586 2587 this(int i, double d)pure nothrow @safe @nogc{ 2588 super(i); 2589 this.d = d; 2590 } 2591 } 2592 2593 static class Zee{ 2594 } 2595 2596 { 2597 SharedPtr!(const Foo) foo = SharedPtr!Bar.make(42, 3.14); 2598 assert(foo.get.i == 42); 2599 2600 auto bar = dynCastMove!Bar(foo); 2601 assert(foo == null); 2602 assert(bar != null); 2603 assert(bar.get.d == 3.14); 2604 static assert(is(typeof(bar) == SharedPtr!(const Bar))); 2605 2606 auto zee = dynCastMove!Zee(bar); 2607 assert(zee == null); 2608 assert(bar != null); 2609 static assert(is(typeof(zee) == SharedPtr!(const Zee))); 2610 } 2611 } 2612 2613 2614 2615 /** 2616 Dynamic cast for shared pointers if `ElementType` is class with D linkage. 2617 2618 Creates a new instance of `SharedPtr` whose stored pointer is obtained from `ptr`'s stored pointer using a dynaic cast expression. 2619 2620 If `ptr` is null or dynamic cast fail then result `SharedPtr` is null. 2621 Otherwise, the new `SharedPtr` will share ownership with the initial value of `ptr`. 2622 */ 2623 public UnqualSmartPtr!Ptr.ChangeElementType!T dynCast(T, Ptr)(scope auto ref Ptr ptr) 2624 if( isSharedPtr!Ptr && !is(Ptr == shared) && !Ptr.isWeak 2625 && isClassOrInterface!T && __traits(getLinkage, T) == "D" 2626 && isClassOrInterface!(Ptr.ElementType) && __traits(getLinkage, Ptr.ElementType) == "D" 2627 ){ 2628 import std.traits : CopyTypeQualifiers; 2629 import core.lifetime : forward; 2630 2631 alias Return = typeof(return); 2632 2633 if(auto element = dynCastElement!T(ptr._element)){ 2634 //return typeof(return)(forward!ptr, element); 2635 return (()@trusted => Return(forward!ptr, element) )(); 2636 2637 } 2638 2639 return typeof(return).init; 2640 } 2641 2642 2643 /// 2644 unittest{ 2645 static class Foo{ 2646 int i; 2647 2648 this(int i)pure nothrow @safe @nogc{ 2649 this.i = i; 2650 } 2651 } 2652 2653 static class Bar : Foo{ 2654 double d; 2655 2656 this(int i, double d)pure nothrow @safe @nogc{ 2657 super(i); 2658 this.d = d; 2659 } 2660 } 2661 2662 static class Zee{ 2663 } 2664 2665 { 2666 SharedPtr!(const Foo) foo = SharedPtr!Bar.make(42, 3.14); 2667 assert(foo.get.i == 42); 2668 2669 auto bar = dynCast!Bar(foo); 2670 assert(bar != null); 2671 assert(bar.get.d == 3.14); 2672 static assert(is(typeof(bar) == SharedPtr!(const Bar))); 2673 2674 auto zee = dynCast!Zee(foo); 2675 assert(zee == null); 2676 static assert(is(typeof(zee) == SharedPtr!(const Zee))); 2677 } 2678 } 2679 2680 2681 /** 2682 Create `SharedPtr` from parameter `ptr` of type `SharedPtr`, `RcPtr` or `IntrusivePtr`. 2683 */ 2684 auto sharedPtr(Ptr)(scope auto ref Ptr ptr)@trusted 2685 if(!is(Ptr == shared) 2686 && isSmartPtr!Ptr //(isSharedPtr!Ptr || isRcPtr!Ptr || isIntrusivePtr!Ptr) 2687 ){ 2688 import core.lifetime : forward; 2689 import std.traits : CopyTypeQualifiers; 2690 2691 return SharedPtr!( 2692 GetElementType!Ptr, 2693 Ptr.DestructorType, 2694 GetControlType!Ptr, 2695 Ptr.isWeak 2696 )(forward!ptr, Forward.init); 2697 } 2698 2699 /// 2700 pure nothrow @nogc unittest{ 2701 import btl.autoptr.rc_ptr; 2702 import btl.autoptr.intrusive_ptr; 2703 2704 import core.lifetime : move; 2705 //RcPtr -> SharedPtr: 2706 { 2707 auto x = RcPtr!long.make(42); 2708 assert(*x == 42); 2709 assert(x.useCount == 1); 2710 2711 auto s = sharedPtr(x); 2712 assert(x.useCount == 2); 2713 2714 import btl.autoptr.shared_ptr : isSharedPtr; 2715 static assert(isSharedPtr!(typeof(s))); 2716 2717 auto s2 = sharedPtr(x.move); 2718 assert(s.useCount == 2); 2719 2720 auto y = sharedPtr(RcPtr!long.init); 2721 assert(y == null); 2722 } 2723 2724 //IntrusivePtr -> SharedPtr: 2725 { 2726 static class Foo{ 2727 ControlBlock!(int, int) c; 2728 int i; 2729 2730 this(int i)pure nothrow @safe @nogc{ 2731 this.i = i; 2732 } 2733 } 2734 2735 auto x = IntrusivePtr!Foo.make(42); 2736 //assert(x.get.i == 42); 2737 assert(x.useCount == 1); 2738 2739 auto s = sharedPtr(x); 2740 assert(x.useCount == 2); 2741 2742 import btl.autoptr.shared_ptr : isSharedPtr; 2743 static assert(isSharedPtr!(typeof(s))); 2744 2745 auto s2 = sharedPtr(x.move); 2746 assert(s.useCount == 2); 2747 2748 auto y = sharedPtr(IntrusivePtr!Foo.init); 2749 assert(y == null); 2750 } 2751 2752 //SharedPtr -> SharedPtr: 2753 { 2754 auto a = SharedPtr!long.make(1); 2755 assert(a.useCount == 1); 2756 2757 auto b = sharedPtr(a); 2758 assert(a.useCount == 2); 2759 2760 auto c = sharedPtr(SharedPtr!long.make(2)); 2761 assert(c.useCount == 1); 2762 } 2763 } 2764 2765 2766 2767 /** 2768 Return `shared SharedPtr` pointing to same managed object like parameter `ptr`. 2769 2770 Type of parameter `ptr` must be `SharedPtr` with `shared(ControlType)` and `shared`/`immutable` `ElementType` . 2771 */ 2772 public shared(Ptr) share(Ptr)(scope auto ref Ptr ptr) 2773 if(isSharedPtr!Ptr){ 2774 import core.lifetime : forward; 2775 2776 static if(is(Ptr == shared)){ 2777 return forward!ptr; 2778 } 2779 else{ 2780 static assert(is(GetControlType!Ptr == shared) || is(GetControlType!Ptr == immutable), 2781 "`SharedPtr` has not shared/immutable ref counter `ControlType`." 2782 ); 2783 2784 static assert(is(GetElementType!Ptr == shared) || is(GetElementType!Ptr == immutable), 2785 "`SharedPtr` has not shared/immutable `ElementType`." 2786 ); 2787 2788 alias Result = shared(Ptr); 2789 2790 return Result(forward!ptr, Forward.init); 2791 } 2792 } 2793 2794 /// 2795 nothrow @nogc unittest{ 2796 { 2797 auto x = SharedPtr!(shared long).make(123); 2798 assert(x.useCount == 1); 2799 2800 shared s1 = share(x); 2801 assert(x.useCount == 2); 2802 2803 2804 import core.lifetime : move; 2805 shared s2 = share(x.move); 2806 assert(x == null); 2807 assert(s2.useCount == 2); 2808 assert(s2.load.get == 123); 2809 2810 } 2811 2812 { 2813 auto x = SharedPtr!(long).make(123); 2814 assert(x.useCount == 1); 2815 2816 ///error `shared SharedPtr` need shared `ControlType` and shared `ElementType`. 2817 //shared s1 = share(x); 2818 2819 } 2820 2821 } 2822 2823 2824 2825 /** 2826 Return `SharedPtr` pointing to first element of array managed by shared pointer `ptr`. 2827 */ 2828 public auto first(Ptr)(scope auto ref Ptr ptr)@trusted 2829 if( isSharedPtr!Ptr 2830 && !is(Ptr == shared) 2831 && is(Ptr.ElementType : T[], T) 2832 ){ 2833 import std.traits : isDynamicArray, isStaticArray; 2834 import std.range : ElementEncodingType; 2835 import core.lifetime : forward; 2836 2837 alias Result = UnqualSmartPtr!Ptr.ChangeElementType!( 2838 ElementEncodingType!(Ptr.ElementType) 2839 ); 2840 2841 if(ptr == null) 2842 return Result.init; 2843 2844 static if(isDynamicArray!(Ptr.ElementType) || isStaticArray!(Ptr.ElementType)){ 2845 auto ptr_element = ptr._element.ptr; 2846 return Result(forward!ptr, (()@trusted => ptr_element )()); 2847 //assert(0); 2848 } 2849 else static assert(0, "no impl"); 2850 } 2851 2852 /// 2853 pure nothrow @nogc unittest{ 2854 //copy 2855 { 2856 auto x = SharedPtr!(long[]).make(10, -1); 2857 assert(x.length == 10); 2858 2859 auto y = first(x); 2860 static assert(is(typeof(y) == SharedPtr!long)); 2861 assert(*y == -1); 2862 assert(x.useCount == 2); 2863 } 2864 2865 { 2866 auto x = SharedPtr!(long[10]).make(-1); 2867 assert(x.get.length == 10); 2868 2869 auto y = first(x); 2870 static assert(is(typeof(y) == SharedPtr!long)); 2871 assert(*y == -1); 2872 assert(x.useCount == 2); 2873 } 2874 2875 //move 2876 import core.lifetime : move; 2877 { 2878 auto x = SharedPtr!(long[]).make(10, -1); 2879 assert(x.length == 10); 2880 2881 auto y = first(x.move); 2882 static assert(is(typeof(y) == SharedPtr!long)); 2883 assert(*y == -1); 2884 } 2885 2886 { 2887 auto x = SharedPtr!(long[10]).make(-1); 2888 assert(x.get.length == 10); 2889 2890 auto y = first(x.move); 2891 static assert(is(typeof(y) == SharedPtr!long)); 2892 assert(*y == -1); 2893 } 2894 } 2895 2896 2897 //local traits: 2898 private{ 2899 2900 template isAliasable(From, To){ 2901 enum bool isAliasable = true 2902 && is(From.DestructorType : To.DestructorType) 2903 && is(GetControlType!From* : GetControlType!To*); 2904 } 2905 2906 //Constructable: 2907 template isMoveConstructable(From, To){ 2908 enum bool isMoveConstructable = true 2909 && isAliasable!(From, To) 2910 && is(GetElementReferenceType!From : GetElementReferenceType!To); 2911 } 2912 template isCopyConstructable(From, To){ 2913 import std.traits : isMutable; 2914 2915 enum bool isCopyConstructable = true 2916 && isMoveConstructable!(From, To) 2917 && isMutable!From 2918 && isMutable!(From.ControlType); 2919 } 2920 template isConstructable(alias from, To){ 2921 enum bool isConstructable = isRef!from 2922 ? isCopyConstructable!(typeof(from), To) 2923 : isMoveConstructable!(typeof(from), To); 2924 } 2925 2926 //Assignable: 2927 template isMoveAssignable(From, To){ 2928 import std.traits : isMutable; 2929 2930 enum bool isMoveAssignable = true 2931 && isMoveConstructable!(From, To) 2932 && !weakLock!(From, To) 2933 && isMutable!To; 2934 } 2935 template isCopyAssignable(From, To){ 2936 import std.traits : isMutable; 2937 2938 enum bool isCopyAssignable = true 2939 && isCopyConstructable!(From, To) 2940 && !weakLock!(From, To) 2941 && isMutable!To; 2942 } 2943 template isAssignable(alias from, To){ 2944 enum bool isAssignable = isRef!from 2945 ? isCopyAssignable!(typeof(from), To) 2946 : isMoveAssignable!(typeof(from), To); 2947 2948 } 2949 } 2950 2951 version(unittest){ 2952 import btl.internal.test_allocator; 2953 //this(SharedPtr, Element) 2954 pure nothrow @nogc unittest{ 2955 { 2956 static struct Foo{ 2957 int i; 2958 double d; 2959 } 2960 SharedPtr!Foo foo = SharedPtr!Foo.make(42, 3.14); 2961 2962 auto x = SharedPtr!double(foo, &foo.get.d); 2963 assert(foo.useCount == 2); 2964 assert(x.get == 3.14); 2965 } 2966 2967 { 2968 auto x1 = SharedPtr!long.make(1); 2969 2970 const float f; 2971 auto x2 = const SharedPtr!float(x1, &f); 2972 2973 assert(x1.useCount == 2); 2974 2975 const double d; 2976 auto x3 = SharedPtr!(const double)((() => x1)(), &d); 2977 assert(x1.useCount == 3); 2978 2979 /+shared bool b; 2980 auto x4 = SharedPtr!(shared bool)(x3, &b); 2981 assert(x1.useCount == 4);+/ 2982 } 2983 2984 } 2985 2986 //copy ctor 2987 pure nothrow @nogc unittest{ 2988 2989 2990 static struct Test{} 2991 2992 import std.meta : AliasSeq; 2993 //alias Test = long; 2994 static foreach(alias ControlType; AliasSeq!(SharedControlBlock, shared SharedControlBlock)){{ 2995 alias SPtr(T) = SharedPtr!(T, DestructorType!T, ControlType); 2996 2997 //mutable: 2998 { 2999 alias Ptr = SPtr!(Test); 3000 Ptr ptr; 3001 static assert(__traits(compiles, Ptr(ptr))); 3002 static assert(__traits(compiles, const(Ptr)(ptr))); 3003 static assert(!__traits(compiles, immutable(Ptr)(ptr))); 3004 static assert(!__traits(compiles, shared(Ptr)(ptr))); 3005 static assert(!__traits(compiles, const(shared(Ptr))(ptr))); 3006 } 3007 3008 //const: 3009 { 3010 alias Ptr = SPtr!(const Test); 3011 Ptr ptr; 3012 static assert(__traits(compiles, Ptr(ptr))); 3013 static assert(__traits(compiles, const(Ptr)(ptr))); 3014 static assert(!__traits(compiles, immutable(Ptr)(ptr))); 3015 static assert(!__traits(compiles, shared(Ptr)(ptr))); 3016 static assert(!__traits(compiles, const(shared(Ptr))(ptr))); 3017 } 3018 3019 //immutable: 3020 { 3021 alias Ptr = SPtr!(immutable Test); 3022 Ptr ptr; 3023 static assert(__traits(compiles, Ptr(ptr))); 3024 static assert(__traits(compiles, const(Ptr)(ptr))); 3025 static assert(!__traits(compiles, immutable(Ptr)(ptr))); 3026 static assert(__traits(compiles, shared(Ptr)(ptr)) == is(ControlType == shared)); 3027 static assert(__traits(compiles, const(shared(Ptr))(ptr)) == is(ControlType == shared)); 3028 } 3029 3030 3031 //shared: 3032 { 3033 alias Ptr = SPtr!(shared Test); 3034 Ptr ptr; 3035 static assert(__traits(compiles, Ptr(ptr))); 3036 static assert(__traits(compiles, const(Ptr)(ptr))); 3037 static assert(!__traits(compiles, immutable(Ptr)(ptr))); 3038 static assert(__traits(compiles, shared(Ptr)(ptr)) == is(ControlType == shared)); 3039 static assert(__traits(compiles, const(shared(Ptr))(ptr)) == is(ControlType == shared)); 3040 } 3041 3042 3043 //const shared: 3044 { 3045 alias Ptr = SPtr!(const shared Test); 3046 Ptr ptr; 3047 static assert(__traits(compiles, Ptr(ptr))); 3048 static assert(__traits(compiles, const(Ptr)(ptr))); 3049 static assert(!__traits(compiles, immutable(Ptr)(ptr))); 3050 static assert(__traits(compiles, shared(Ptr)(ptr)) == is(ControlType == shared)); 3051 static assert(__traits(compiles, const(shared(Ptr))(ptr)) == is(ControlType == shared)); 3052 } 3053 3054 static foreach(alias T; AliasSeq!( 3055 Test, 3056 const Test, 3057 shared Test, 3058 const shared Test, 3059 immutable Test, 3060 )){{ 3061 alias Ptr = SPtr!T; 3062 3063 const(Ptr) cptr; 3064 static assert(!__traits(compiles, Ptr(cptr))); 3065 static assert(!__traits(compiles, const(Ptr)(cptr))); 3066 static assert(!__traits(compiles, immutable(Ptr)(cptr))); 3067 static assert(!__traits(compiles, shared(Ptr)(cptr))); 3068 static assert(!__traits(compiles, const(shared(Ptr))(cptr))); 3069 3070 immutable(Ptr) iptr; 3071 static assert(!__traits(compiles, Ptr(iptr))); 3072 static assert(!__traits(compiles, const(Ptr)(iptr))); 3073 static assert(!__traits(compiles, immutable(Ptr)(iptr))); 3074 static assert(!__traits(compiles, shared(Ptr)(iptr))); 3075 static assert(!__traits(compiles, const(shared(Ptr))(iptr))); 3076 3077 shared(Ptr) sptr; 3078 static assert(!__traits(compiles, Ptr(sptr))); 3079 static assert(!__traits(compiles, const(Ptr)(sptr))); 3080 static assert(!__traits(compiles, immutable(Ptr)(sptr))); 3081 static assert(!__traits(compiles, shared(Ptr)(sptr))); //need load 3082 static assert(!__traits(compiles, const shared Ptr(sptr))); //need load 3083 3084 shared(const(Ptr)) scptr; 3085 static assert(!__traits(compiles, Ptr(scptr))); 3086 static assert(!__traits(compiles, const(Ptr)(scptr))); 3087 static assert(!__traits(compiles, immutable(Ptr)(scptr))); 3088 static assert(!__traits(compiles, shared(Ptr)(scptr))); //need load 3089 static assert(!__traits(compiles, const(shared(Ptr))(scptr))); //need load 3090 3091 }} 3092 3093 }} 3094 } 3095 3096 //this(typeof(null)) 3097 pure nothrow @safe @nogc unittest{ 3098 SharedPtr!long x = null; 3099 3100 assert(x == null); 3101 assert(x == SharedPtr!long.init); 3102 3103 } 3104 3105 3106 //opAssign(SmartPtr) 3107 pure nothrow @nogc unittest{ 3108 3109 { 3110 SharedPtr!long px1 = SharedPtr!long.make(1); 3111 SharedPtr!long px2 = SharedPtr!long.make(2); 3112 3113 assert(px2.useCount == 1); 3114 px1 = px2; 3115 assert(px1.get == 2); 3116 assert(px2.useCount == 2); 3117 } 3118 3119 3120 3121 { 3122 SharedPtr!long px = SharedPtr!long.make(1); 3123 SharedPtr!(const long) pcx = SharedPtr!long.make(2); 3124 3125 assert(px.useCount == 1); 3126 pcx = px; 3127 assert(pcx.get == 1); 3128 assert(pcx.useCount == 2); 3129 3130 } 3131 3132 3133 { 3134 SharedPtr!(const long) pcx = SharedPtr!long.make(2); 3135 const SharedPtr!long pcx2 = pcx; 3136 3137 assert(pcx.useCount == 2); 3138 3139 } 3140 3141 { 3142 SharedPtr!(immutable long) pix = SharedPtr!(immutable long).make(123); 3143 SharedPtr!(const long) pcx = SharedPtr!long.make(2); 3144 3145 assert(pix.useCount == 1); 3146 pcx = pix; 3147 assert(pcx.get == 123); 3148 assert(pcx.useCount == 2); 3149 3150 } 3151 3152 import btl.autoptr.rc_ptr; 3153 { 3154 SharedPtr!long sp = SharedPtr!long.make(10); 3155 RcPtr!long rc = RcPtr!long.make(20); 3156 3157 assert(sp.useCount == 1); 3158 assert(rc.useCount == 1); 3159 sp = rc; 3160 3161 assert(sp.get == 20); 3162 assert(rc.useCount == 2); 3163 } 3164 3165 import btl.autoptr.intrusive_ptr; 3166 { 3167 static struct Foo{ 3168 private SharedControlBlock control; 3169 int i; 3170 3171 this(int i)pure nothrow @safe @nogc{ 3172 this.i = i; 3173 } 3174 } 3175 3176 SharedPtr!Foo sp = SharedPtr!Foo.make(10); 3177 IntrusivePtr!Foo ip = IntrusivePtr!Foo.make(20); 3178 3179 assert(sp.useCount == 1); 3180 assert(ip.useCount == 1); 3181 sp = ip; 3182 3183 assert(sp.get.i== 20); 3184 assert(ip.useCount == 2); 3185 } 3186 } 3187 3188 3189 3190 //opAssign(null) 3191 nothrow @safe @nogc unittest{ 3192 { 3193 SharedPtr!long x = SharedPtr!long.make(1); 3194 3195 assert(x.useCount == 1); 3196 x = null; 3197 assert(x.useCount == 0); 3198 assert(x == null); 3199 } 3200 3201 { 3202 SharedPtr!(shared long) x = SharedPtr!(shared long).make(1); 3203 3204 assert(x.useCount == 1); 3205 x = null; 3206 assert(x.useCount == 0); 3207 assert(x == null); 3208 } 3209 3210 import btl.internal.mutex : supportMutex; 3211 static if(supportMutex){ 3212 shared SharedPtr!(long) x = SharedPtr!(shared long).make(1); 3213 3214 assert(x.useCount == 1); 3215 x = null; 3216 assert(x.useCount == 0); 3217 assert(x.load == null); 3218 } 3219 } 3220 3221 //useCount 3222 pure nothrow @safe @nogc unittest{ 3223 SharedPtr!long x = null; 3224 3225 assert(x.useCount == 0); 3226 3227 x = SharedPtr!long.make(123); 3228 assert(x.useCount == 1); 3229 3230 auto y = x; 3231 assert(x.useCount == 2); 3232 3233 auto w1 = x.weak; //weak ptr 3234 assert(x.useCount == 2); 3235 3236 SharedPtr!long.WeakType w2 = x; //weak ptr 3237 assert(x.useCount == 2); 3238 3239 y = null; 3240 assert(x.useCount == 1); 3241 3242 x = null; 3243 assert(x.useCount == 0); 3244 assert(w1.useCount == 0); 3245 } 3246 3247 //weakCount 3248 pure nothrow @safe @nogc unittest{ 3249 3250 SharedPtr!long x = null; 3251 assert(x.useCount == 0); 3252 assert(x.weakCount == 0); 3253 3254 x = SharedPtr!long.make(123); 3255 assert(x.useCount == 1); 3256 assert(x.weakCount == 0); 3257 3258 auto w = x.weak(); 3259 assert(x.useCount == 1); 3260 assert(x.weakCount == 1); 3261 } 3262 3263 // store: 3264 nothrow @nogc unittest{ 3265 3266 //null store: 3267 { 3268 shared x = SharedPtr!(shared long).make(123); 3269 assert(x.load.get == 123); 3270 3271 x.store(null); 3272 assert(x.useCount == 0); 3273 assert(x.load == null); 3274 } 3275 3276 //rvalue store: 3277 { 3278 shared x = SharedPtr!(shared long).make(123); 3279 assert(x.load.get == 123); 3280 3281 x.store(SharedPtr!(shared long).make(42)); 3282 assert(x.load.get == 42); 3283 } 3284 3285 //lvalue store: 3286 { 3287 shared x = SharedPtr!(shared long).make(123); 3288 auto y = SharedPtr!(shared long).make(42); 3289 3290 assert(x.load.get == 123); 3291 assert(y.load.get == 42); 3292 3293 x.store(y); 3294 assert(x.load.get == 42); 3295 assert(x.useCount == 2); 3296 } 3297 } 3298 3299 //load: 3300 nothrow @nogc unittest{ 3301 3302 shared SharedPtr!(long) x = SharedPtr!(shared long).make(123); 3303 3304 import btl.internal.mutex : supportMutex; 3305 static if(supportMutex){ 3306 SharedPtr!(shared long) y = x.load(); 3307 assert(y.useCount == 2); 3308 3309 assert(y.get == 123); 3310 } 3311 3312 } 3313 3314 //exchange 3315 nothrow @nogc unittest{ 3316 3317 //lvalue exchange 3318 { 3319 shared x = SharedPtr!(shared long).make(123); 3320 auto y = SharedPtr!(shared long).make(42); 3321 3322 auto z = x.exchange(y); 3323 3324 assert(x.load.get == 42); 3325 assert(y.get == 42); 3326 assert(z.get == 123); 3327 } 3328 3329 //rvalue exchange 3330 { 3331 shared x = SharedPtr!(shared long).make(123); 3332 auto y = SharedPtr!(shared long).make(42); 3333 3334 import core.lifetime : move; 3335 auto z = x.exchange(y.move); 3336 3337 assert(x.load.get == 42); 3338 assert(y == null); 3339 assert(z.get == 123); 3340 } 3341 3342 //null exchange (same as move) 3343 { 3344 shared x = SharedPtr!(shared long).make(123); 3345 3346 auto z = x.exchange(null); 3347 3348 assert(x.load == null); 3349 assert(z.get == 123); 3350 } 3351 3352 //swap: 3353 { 3354 shared x = SharedPtr!(shared long).make(123); 3355 auto y = SharedPtr!(shared long).make(42); 3356 3357 //opAssign is same as store 3358 import core.lifetime : move; 3359 y = x.exchange(y.move); 3360 3361 assert(x.load.get == 42); 3362 assert(y.get == 123); 3363 } 3364 3365 } 3366 3367 //compareExchange 3368 nothrow @nogc unittest{ 3369 alias Type = const long; 3370 static foreach(enum bool weak; [true, false]){ 3371 //fail 3372 { 3373 SharedPtr!Type a = SharedPtr!Type.make(123); 3374 SharedPtr!Type b = SharedPtr!Type.make(42); 3375 SharedPtr!Type c = SharedPtr!Type.make(666); 3376 3377 static if(weak)a.compareExchangeWeak(b, c); 3378 else a.compareExchangeStrong(b, c); 3379 3380 assert(*a == 123); 3381 assert(*b == 123); 3382 assert(*c == 666); 3383 3384 } 3385 3386 //success 3387 { 3388 SharedPtr!Type a = SharedPtr!Type.make(123); 3389 SharedPtr!Type b = a; 3390 SharedPtr!Type c = SharedPtr!Type.make(666); 3391 3392 static if(weak)a.compareExchangeWeak(b, c); 3393 else a.compareExchangeStrong(b, c); 3394 3395 assert(*a == 666); 3396 assert(*b == 123); 3397 assert(*c == 666); 3398 } 3399 3400 //shared fail 3401 { 3402 shared SharedPtr!(shared Type) a = SharedPtr!(shared Type).make(123); 3403 SharedPtr!(shared Type) b = SharedPtr!(shared Type).make(42); 3404 SharedPtr!(shared Type) c = SharedPtr!(shared Type).make(666); 3405 3406 static if(weak)a.compareExchangeWeak(b, c); 3407 else a.compareExchangeStrong(b, c); 3408 3409 auto tmp = a.exchange(null); 3410 assert(*tmp == 123); 3411 assert(*b == 123); 3412 assert(*c == 666); 3413 } 3414 3415 //shared success 3416 { 3417 SharedPtr!(shared Type) b = SharedPtr!(shared Type).make(123); 3418 shared SharedPtr!(shared Type) a = b; 3419 SharedPtr!(shared Type) c = SharedPtr!(shared Type).make(666); 3420 3421 static if(weak)a.compareExchangeWeak(b, c); 3422 else a.compareExchangeStrong(b, c); 3423 3424 auto tmp = a.exchange(null); 3425 assert(*tmp == 666); 3426 assert(*b == 123); 3427 assert(*c == 666); 3428 } 3429 } 3430 } 3431 3432 //lock 3433 nothrow @nogc unittest{ 3434 { 3435 SharedPtr!long x = SharedPtr!long.make(123); 3436 3437 auto w = x.weak; //weak ptr 3438 3439 SharedPtr!long y = w.lock; 3440 3441 assert(x == y); 3442 assert(x.useCount == 2); 3443 assert(y.get == 123); 3444 } 3445 3446 { 3447 SharedPtr!long x = SharedPtr!long.make(123); 3448 3449 auto w = x.weak; //weak ptr 3450 3451 assert(w.expired == false); 3452 3453 x = SharedPtr!long.make(321); 3454 3455 assert(w.expired == true); 3456 3457 SharedPtr!long y = w.lock; 3458 3459 assert(y == null); 3460 } 3461 { 3462 shared SharedPtr!(shared long) x = SharedPtr!(shared long).make(123); 3463 3464 shared SharedPtr!(shared long).WeakType w = x.load.weak; //weak ptr 3465 3466 assert(w.expired == false); 3467 3468 x = SharedPtr!(shared long).make(321); 3469 3470 assert(w.expired == true); 3471 3472 SharedPtr!(shared long) y = w.load.lock; 3473 3474 assert(y == null); 3475 } 3476 } 3477 3478 //expired 3479 pure nothrow @nogc @safe unittest{ 3480 { 3481 SharedPtr!long x = SharedPtr!long.make(123); 3482 3483 auto wx = x.weak; //weak pointer 3484 3485 assert(wx.expired == false); 3486 3487 x = null; 3488 3489 assert(wx.expired == true); 3490 } 3491 } 3492 3493 //make 3494 pure nothrow @nogc unittest{ 3495 { 3496 SharedPtr!long a = SharedPtr!long.make(); 3497 assert(a.get == 0); 3498 3499 SharedPtr!(const long) b = SharedPtr!long.make(2); 3500 assert(b.get == 2); 3501 } 3502 3503 { 3504 static struct Struct{ 3505 int i = 7; 3506 3507 this(int i)pure nothrow @safe @nogc{ 3508 this.i = i; 3509 } 3510 } 3511 3512 SharedPtr!Struct s1 = SharedPtr!Struct.make(); 3513 assert(s1.get.i == 7); 3514 3515 SharedPtr!Struct s2 = SharedPtr!Struct.make(123); 3516 assert(s2.get.i == 123); 3517 } 3518 3519 static interface Interface{ 3520 } 3521 static class Class : Interface{ 3522 int i; 3523 3524 this(int i)pure nothrow @safe @nogc{ 3525 this.i = i; 3526 } 3527 } 3528 3529 { 3530 3531 SharedPtr!Interface x = SharedPtr!Class.make(3); 3532 //assert(x.dynTo!Class.get.i == 3); 3533 } 3534 3535 3536 } 3537 3538 //make dynamic array 3539 pure nothrow @nogc unittest{ 3540 { 3541 auto arr = SharedPtr!(long[]).make(6, -1); 3542 assert(arr.length == 6); 3543 assert(arr.get.length == 6); 3544 3545 import std.algorithm : all; 3546 assert(arr.get.all!(x => x == -1)); 3547 3548 for(int i = 0; i < 6; ++i) 3549 arr.get[i] = i; 3550 3551 assert(arr.get == [0, 1, 2, 3, 4, 5]); 3552 } 3553 3554 { 3555 static struct Struct{ 3556 int i; 3557 double d; 3558 } 3559 3560 { 3561 auto a = SharedPtr!(Struct[]).make(6, 42, 3.14); 3562 assert(a.length == 6); 3563 assert(a.get.length == 6); 3564 3565 import std.algorithm : all; 3566 assert(a.get[].all!(x => (x.i == 42 && x.d == 3.14))); 3567 } 3568 3569 { 3570 auto a = SharedPtr!(Struct[]).make(6); 3571 assert(a.length == 6); 3572 3573 import std.algorithm : all; 3574 assert(a.get[].all!(x => (x.i == int.init))); 3575 } 3576 } 3577 3578 { 3579 static class Class{ 3580 int i; 3581 double d; 3582 3583 this(int i, double d){ 3584 this.i = i; 3585 this.d = d; 3586 } 3587 } 3588 3589 { 3590 auto a = SharedPtr!(Class[]).make(6, null); 3591 assert(a.length == 6); 3592 3593 import std.algorithm : all; 3594 assert(a.get[].all!(x => x is null)); 3595 } 3596 3597 { 3598 auto a = SharedPtr!(Class[]).make(6); 3599 assert(a.length == 6); 3600 3601 import std.algorithm : all; 3602 assert(a.get[].all!(x => x is null)); 3603 } 3604 3605 3606 } 3607 } 3608 3609 //make static array 3610 pure nothrow @nogc unittest{ 3611 import std.algorithm : all; 3612 { 3613 SharedPtr!(long[6]) a = SharedPtr!(long[6]).make(); 3614 assert(a.get.length == 6); 3615 assert(a.get[].all!(x => x == long.init)); 3616 } 3617 { 3618 SharedPtr!(long[6]) a = SharedPtr!(long[6]).make(-1); 3619 assert(a.get.length == 6); 3620 assert(a.get[].all!(x => x == -1)); 3621 } 3622 { 3623 long[6] tmp = [1, 2, 3, 4, 5, 6]; 3624 3625 SharedPtr!(const(long)[6]) a = SharedPtr!(long[6]).make(tmp); 3626 assert(a.get.length == 6); 3627 assert(a.get[]== tmp); 3628 } 3629 { 3630 static struct Struct{ 3631 int i; 3632 double d; 3633 } 3634 3635 auto a = SharedPtr!(Struct[6]).make(42, 3.14); 3636 assert(a.get.length == 6); 3637 3638 import std.algorithm : all; 3639 assert(a.get[].all!(x => (x.i == 42 && x.d == 3.14))); 3640 3641 3642 } 3643 } 3644 3645 //make deleter 3646 pure nothrow unittest{ 3647 { 3648 long deleted = -1; 3649 long tmp = 123; 3650 auto x = SharedPtr!long.make(&tmp, (long* data){ 3651 deleted = *data; 3652 }); 3653 assert(deleted == -1); 3654 assert(*x == 123); 3655 3656 x = null; 3657 assert(deleted == 123); 3658 } 3659 } 3660 3661 //alloc 3662 pure nothrow @nogc unittest{ 3663 { 3664 TestAllocator allocator; 3665 3666 { 3667 SharedPtr!long a = SharedPtr!long.alloc(&allocator); 3668 assert(a.get == 0); 3669 3670 SharedPtr!(const long) b = SharedPtr!long.alloc(&allocator, 2); 3671 assert(b.get == 2); 3672 } 3673 3674 { 3675 static struct Struct{ 3676 int i = 7; 3677 3678 this(int i)pure nothrow @safe @nogc{ 3679 this.i = i; 3680 } 3681 } 3682 3683 SharedPtr!Struct s1 = SharedPtr!Struct.alloc(allocator); 3684 assert(s1.get.i == 7); 3685 3686 SharedPtr!Struct s2 = SharedPtr!Struct.alloc(allocator, 123); 3687 assert(s2.get.i == 123); 3688 } 3689 3690 static interface Interface{ 3691 } 3692 static class Class : Interface{ 3693 int i; 3694 3695 this(int i)pure nothrow @safe @nogc{ 3696 this.i = i; 3697 } 3698 } 3699 3700 { 3701 3702 3703 SharedPtr!Interface x = SharedPtr!Class.alloc(&allocator, 3); 3704 assert(x.useCount == 1); 3705 //assert(x.dynTo!Class.get.i == 3); 3706 } 3707 3708 } 3709 } 3710 3711 //alloc 3712 unittest{ 3713 3714 { 3715 import std.experimental.allocator : allocatorObject; 3716 3717 auto a = allocatorObject(Mallocator.instance); 3718 { 3719 auto x = SharedPtr!long.alloc(a); 3720 assert(x.get == 0); 3721 3722 auto y = SharedPtr!(const long).alloc(a, 2); 3723 assert(y.get == 2); 3724 } 3725 3726 { 3727 static struct Struct{ 3728 int i = 7; 3729 3730 this(int i)pure nothrow @safe @nogc{ 3731 this.i = i; 3732 } 3733 } 3734 3735 auto s1 = SharedPtr!Struct.alloc(a); 3736 assert(s1.get.i == 7); 3737 3738 auto s2 = SharedPtr!Struct.alloc(a, 123); 3739 assert(s2.get.i == 123); 3740 } 3741 3742 { 3743 static interface Interface{ 3744 } 3745 static class Class : Interface{ 3746 int i; 3747 3748 this(int i)pure nothrow @safe @nogc{ 3749 this.i = i; 3750 } 3751 } 3752 3753 SharedPtr!(Interface, DestructorAllocatorType!(typeof(a))) x = SharedPtr!Class.alloc(a, 3); 3754 //assert(x.dynTo!Class.get.i == 3); 3755 } 3756 3757 } 3758 } 3759 3760 //alloc array 3761 nothrow unittest{ 3762 { 3763 import std.experimental.allocator : allocatorObject; 3764 3765 auto a = allocatorObject(Mallocator.instance); 3766 auto arr = SharedPtr!(long[], DestructorAllocatorType!(typeof(a))).alloc(a, 6, -1); 3767 assert(arr.length == 6); 3768 assert(arr.get.length == 6); 3769 3770 import std.algorithm : all; 3771 assert(arr.get.all!(x => x == -1)); 3772 3773 for(int i = 0; i < 6; ++i) 3774 arr.get[i] = i; 3775 3776 assert(arr.get == [0, 1, 2, 3, 4, 5]); 3777 } 3778 } 3779 3780 //alloc deleter 3781 nothrow unittest{ 3782 import std.experimental.allocator : make, dispose, allocatorObject; 3783 3784 auto a = allocatorObject(Mallocator.instance); 3785 3786 long deleted = -1; 3787 3788 void del(long* data){ 3789 deleted = *data; 3790 a.dispose(data); 3791 } 3792 3793 auto x = SharedPtr!long.alloc(a, a.make!long(123), &del); 3794 assert(deleted == -1); 3795 assert(*x == 123); 3796 3797 x = null; 3798 assert(deleted == 123); 3799 } 3800 3801 //ctor 3802 pure nothrow @nogc @safe unittest{ 3803 3804 { 3805 SharedPtr!long x = SharedPtr!long.make(123); 3806 assert(x.useCount == 1); 3807 3808 SharedPtr!long a = x; //lvalue copy ctor 3809 assert(a == x); 3810 3811 const SharedPtr!long b = x; //lvalue copy ctor 3812 assert(b == x); 3813 3814 SharedPtr!(const long) c = x; //lvalue ctor 3815 assert(c == x); 3816 3817 //const SharedPtr!long d = b; //lvalue ctor 3818 //assert(d == x); 3819 3820 assert(x.useCount == 4); 3821 } 3822 3823 { 3824 import core.lifetime : move; 3825 SharedPtr!long x = SharedPtr!long.make(123); 3826 assert(x.useCount == 1); 3827 3828 SharedPtr!long a = move(x); //rvalue copy ctor 3829 assert(a.useCount == 1); 3830 3831 const SharedPtr!long b = move(a); //rvalue copy ctor 3832 assert(b.useCount == 1); 3833 3834 /+SharedPtr!(const long) c = b.load; //rvalue ctor 3835 assert(c.useCount == 2);+/ 3836 3837 /+const SharedPtr!long d = move(c); //rvalue ctor 3838 assert(d.useCount == 2);+/ 3839 } 3840 3841 /+{ 3842 import core.lifetime : move; 3843 auto u = UniquePtr!(long, SharedControlBlock).make(123); 3844 3845 SharedPtr!long s = move(u); //rvalue copy ctor 3846 assert(s != null); 3847 assert(s.useCount == 1); 3848 3849 SharedPtr!long s2 = UniquePtr!(long, SharedControlBlock).init; 3850 assert(s2 == null); 3851 3852 }+/ 3853 3854 { 3855 import btl.autoptr.rc_ptr; 3856 3857 import core.lifetime : move; 3858 auto rc = RcPtr!(long).make(123); 3859 assert(rc.useCount == 1); 3860 3861 SharedPtr!long s = rc; 3862 assert(s != null); 3863 assert(s.useCount == 2); 3864 assert(rc.useCount == 2); 3865 3866 SharedPtr!long s2 = RcPtr!(long).init; 3867 assert(s2 == null); 3868 } 3869 3870 } 3871 3872 //weak 3873 pure nothrow @nogc unittest{ 3874 SharedPtr!long x = SharedPtr!long.make(123); 3875 assert(x.useCount == 1); 3876 auto wx = x.weak; //weak pointer 3877 assert(wx.expired == false); 3878 assert(wx.lock.get == 123); 3879 assert(wx.useCount == 1); 3880 x = null; 3881 assert(wx.expired == true); 3882 assert(wx.useCount == 0); 3883 3884 } 3885 3886 //operator * 3887 pure nothrow @nogc unittest{ 3888 3889 SharedPtr!long x = SharedPtr!long.make(123); 3890 assert(*x == 123); 3891 (*x = 321); 3892 assert(*x == 321); 3893 const y = x; 3894 assert(*y == 321); 3895 assert(*x == 321); 3896 static assert(is(typeof(*y) == const long)); 3897 } 3898 3899 //get 3900 pure nothrow @nogc unittest{ 3901 SharedPtr!long x = SharedPtr!long.make(123); 3902 assert(x.get == 123); 3903 x.get = 321; 3904 assert(x.get == 321); 3905 const y = x; 3906 assert(y.get == 321); 3907 assert(x.get == 321); 3908 static assert(is(typeof(y.get) == const long)); 3909 } 3910 3911 //element 3912 pure nothrow @nogc unittest{ 3913 { 3914 SharedPtr!long x = SharedPtr!long.make(123); 3915 assert(*x.element == 123); 3916 3917 x.get = 321; 3918 assert(*x.element == 321); 3919 3920 const y = x; 3921 assert(*y.element == 321); 3922 assert(*x.element == 321); 3923 3924 static assert(is(typeof(y.element) == const(long)*)); 3925 } 3926 3927 { 3928 auto x = SharedPtr!(long[]).make(6, 42); 3929 assert(x.element[0] == 42); 3930 static assert(is(typeof(x.element) == long[])); 3931 } 3932 3933 { 3934 auto s = SharedPtr!long.make(42); 3935 const w = s.weak; 3936 3937 assert(*w.element == 42); 3938 3939 s = null; 3940 assert(w.element is null); 3941 } 3942 3943 { 3944 auto s = SharedPtr!long.make(42); 3945 auto w = s.weak; 3946 3947 scope const p = w.element; 3948 3949 s = null; 3950 assert(w.element is null); 3951 3952 assert(p !is null); //p is dangling pointer! 3953 } 3954 } 3955 3956 //ptr 3957 pure nothrow @nogc unittest{ 3958 { 3959 SharedPtr!long x = SharedPtr!long.make(123); 3960 assert(*x.ptr == 123); 3961 3962 x.get = 321; 3963 assert(*x.ptr == 321); 3964 3965 const y = x; 3966 assert(*y.ptr == 321); 3967 assert(*x.ptr == 321); 3968 3969 static assert(is(typeof(y.ptr) == const(long)*)); 3970 } 3971 3972 { 3973 auto x = SharedPtr!(long[]).make(6, 42); 3974 assert(*x.ptr == 42); 3975 static assert(is(typeof(x.ptr) == long*)); 3976 } 3977 3978 { 3979 auto s = SharedPtr!long.make(42); 3980 const w = s.weak; 3981 3982 assert(*w.ptr == 42); 3983 3984 s = null; 3985 assert(w.ptr is null); 3986 } 3987 3988 { 3989 auto s = SharedPtr!long.make(42); 3990 auto w = s.weak; 3991 3992 scope const p = w.ptr; 3993 3994 s = null; 3995 assert(w.ptr is null); 3996 3997 assert(p !is null); //p is dangling pointer! 3998 } 3999 } 4000 4001 //opCast bool 4002 @safe pure nothrow @nogc unittest{ 4003 SharedPtr!long x = SharedPtr!long.make(123); 4004 assert(cast(bool)x); //explicit cast 4005 assert(x); //implicit cast 4006 x = null; 4007 assert(!cast(bool)x); //explicit cast 4008 assert(!x); //implicit cast 4009 } 4010 4011 4012 //opCast SharedPtr 4013 /+TODO 4014 @safe pure nothrow @nogc unittest{ 4015 SharedPtr!long x = SharedPtr!long.make(123); 4016 auto y = cast(SharedPtr!(const long))x; 4017 auto z = cast(const SharedPtr!long)x; 4018 auto u = cast(const SharedPtr!(const long))x; 4019 assert(x.useCount == 4); 4020 } 4021 +/ 4022 4023 //opEquals SharedPtr 4024 pure @safe nothrow @nogc unittest{ 4025 { 4026 SharedPtr!long x = SharedPtr!long.make(0); 4027 assert(x != null); 4028 x = null; 4029 assert(x == null); 4030 } 4031 4032 { 4033 SharedPtr!long x = SharedPtr!long.make(123); 4034 SharedPtr!long y = SharedPtr!long.make(123); 4035 assert(x == x); 4036 assert(y == y); 4037 assert(x != y); 4038 } 4039 4040 { 4041 SharedPtr!long x; 4042 SharedPtr!(const long) y; 4043 assert(x == x); 4044 assert(y == y); 4045 assert(x == y); 4046 } 4047 } 4048 4049 //opEquals SharedPtr 4050 pure nothrow @nogc unittest{ 4051 { 4052 SharedPtr!long x = SharedPtr!long.make(123); 4053 SharedPtr!long y = SharedPtr!long.make(123); 4054 assert(x == x.element); 4055 assert(y.element == y); 4056 assert(x != y.element); 4057 } 4058 } 4059 4060 //opCmp 4061 pure nothrow @safe @nogc unittest{ 4062 { 4063 const a = SharedPtr!long.make(42); 4064 const b = SharedPtr!long.make(123); 4065 const n = SharedPtr!long.init; 4066 4067 assert(a <= a); 4068 assert(a >= a); 4069 4070 assert((a < b) == !(a >= b)); 4071 assert((a > b) == !(a <= b)); 4072 4073 assert(a > n); 4074 assert(a > null); 4075 4076 assert(n < a); 4077 assert(null < a); 4078 } 4079 } 4080 4081 //opCmp 4082 pure nothrow @nogc unittest{ 4083 { 4084 const a = SharedPtr!long.make(42); 4085 const b = SharedPtr!long.make(123); 4086 4087 assert(a <= a.element); 4088 assert(a.element >= a); 4089 4090 assert((a < b.element) == !(a.element >= b)); 4091 assert((a > b.element) == !(a.element <= b)); 4092 } 4093 } 4094 4095 //toHash 4096 pure nothrow @safe @nogc unittest{ 4097 { 4098 SharedPtr!long x = SharedPtr!long.make(123); 4099 SharedPtr!long y = SharedPtr!long.make(123); 4100 assert(x.toHash == x.toHash); 4101 assert(y.toHash == y.toHash); 4102 assert(x.toHash != y.toHash); 4103 SharedPtr!(const long) z = x; 4104 assert(x.toHash == z.toHash); 4105 } 4106 { 4107 SharedPtr!long x; 4108 SharedPtr!(const long) y; 4109 assert(x.toHash == x.toHash); 4110 assert(y.toHash == y.toHash); 4111 assert(x.toHash == y.toHash); 4112 } 4113 } 4114 4115 //proxySwap 4116 pure nothrow @nogc unittest{ 4117 { 4118 SharedPtr!long a = SharedPtr!long.make(1); 4119 SharedPtr!long b = SharedPtr!long.make(2); 4120 a.proxySwap(b); 4121 assert(*a == 2); 4122 assert(*b == 1); 4123 import std.algorithm : swap; 4124 swap(a, b); 4125 assert(*a == 1); 4126 assert(*b == 2); 4127 assert(a.useCount == 1); 4128 assert(b.useCount == 1); 4129 } 4130 } 4131 4132 //length 4133 pure nothrow @nogc unittest{ 4134 auto x = SharedPtr!(int[]).make(10, -1); 4135 assert(x.length == 10); 4136 assert(x.get.length == 10); 4137 4138 import std.algorithm : all; 4139 assert(x.get.all!(i => i == -1)); 4140 } 4141 4142 } 4143 4144 pure nothrow @safe @nogc unittest{ 4145 SharedPtr!void u = SharedPtr!void.make(); 4146 } 4147 4148 4149 //test strong ptr -> weak ptr move ctor 4150 unittest{ 4151 { 4152 import core.lifetime : move; 4153 4154 auto a = SharedPtr!int.make(1); 4155 auto b = a; 4156 assert(a.useCount == 2); 4157 assert(a.weakCount == 0); 4158 4159 SharedPtr!int.WeakType x = move(a); 4160 assert(b.useCount == 1); 4161 assert(b.weakCount == 1); 4162 } 4163 } 4164 4165 //test strong ptr -> weak ptr assign 4166 unittest{ 4167 { 4168 import core.lifetime : move; 4169 4170 auto a = SharedPtr!int.make(1); 4171 auto b = SharedPtr!int.make(2); 4172 4173 { 4174 SharedPtr!int.WeakType x = SharedPtr!int(a); 4175 assert(a.useCount == 1); 4176 assert(a.weakCount == 1); 4177 4178 x = SharedPtr!int(b); 4179 assert(a.useCount == 1); 4180 assert(a.weakCount == 0); 4181 4182 assert(b.useCount == 1); 4183 assert(b.weakCount == 1); 4184 } 4185 { 4186 SharedPtr!int.WeakType x = a; 4187 assert(a.useCount == 1); 4188 assert(a.weakCount == 1); 4189 4190 SharedPtr!int.WeakType y = b; 4191 assert(b.useCount == 1); 4192 assert(b.weakCount == 1); 4193 4194 x = y; 4195 assert(a.useCount == 1); 4196 assert(a.weakCount == 0); 4197 4198 assert(b.useCount == 1); 4199 assert(b.weakCount == 2); 4200 4201 4202 } 4203 } 4204 } 4205 4206 //compare strong and weak ptr 4207 unittest{ 4208 auto a = SharedPtr!int.make(1); 4209 auto b = a.weak; 4210 assert(a == b); 4211 } 4212 4213 //self opAssign 4214 unittest{ 4215 auto a = SharedPtr!long.make(1); 4216 a = a; 4217 } 4218 4219 4220 4221 //@safe const get: 4222 @safe pure nothrow @nogc unittest{ 4223 /+{ 4224 const x = SharedPtr!long.make(42); 4225 assert(x.get == 42); 4226 }+/ 4227 4228 { 4229 const x = SharedPtr!void.make(); 4230 x.get; 4231 } 4232 } 4233 4234 4235 4236 4237 @safe pure nothrow @nogc unittest{ 4238 { 4239 auto p = SharedPtr!long.make(42); 4240 4241 apply!((scope long* a, scope long* b){ 4242 assert(p.useCount == 3); 4243 assert(a !is null); 4244 assert(b !is null); 4245 4246 assert(a is b); 4247 assert(*a == 42); 4248 })(p, p); 4249 4250 } 4251 4252 { 4253 auto p = SharedPtr!long.make(42); 4254 4255 apply!((scope long* a, scope long* b){ 4256 assert(p.useCount == 3); 4257 assert(a !is null); 4258 assert(b !is null); 4259 4260 assert(a is b); 4261 assert(*a == 42); 4262 })(p, p.weak()); 4263 } 4264 4265 { 4266 auto p = SharedPtr!long.make(42); 4267 auto w = SharedPtr!long.make(123).weak(); 4268 assert(w.expired); 4269 4270 apply!((scope long* a, scope long* b){ 4271 assert(p.useCount == 2); 4272 assert(a !is null); 4273 assert(b is null); 4274 4275 assert(*a == 42); 4276 })(p, w); 4277 } 4278 4279 } 4280 4281 4282 version(unittest){ 4283 //debug{ 4284 private struct Cycle{ 4285 4286 SharedPtr!(Cycle, DestructorType!void, SharedControlBlock) cycle; 4287 4288 ~this()pure nothrow @safe @nogc{} 4289 } 4290 }