1 module automem; 2 3 4 version(unittest) { 5 import unit_threaded; 6 import test_allocator; 7 } 8 9 10 private void checkAllocator(T)() { 11 import std.experimental.allocator: make, dispose; 12 import std.traits: hasMember; 13 14 static if(hasMember!(T, "instance")) 15 alias allocator = T.instance; 16 else 17 T allocator; 18 19 int* i = allocator.make!int; 20 allocator.dispose(&i); 21 void[] bytes = allocator.allocate(size_t.init); 22 allocator.deallocate(bytes); 23 } 24 enum isAllocator(T) = is(typeof(checkAllocator!T)); 25 26 @("isAllocator") 27 @safe @nogc pure unittest { 28 import std.experimental.allocator.mallocator: Mallocator; 29 static assert(isAllocator!Mallocator); 30 static assert(isAllocator!TestAllocator); 31 static assert(!isAllocator!Struct); 32 } 33 34 struct Unique(Type, Allocator) if(isAllocator!Allocator) { 35 import std.traits: hasMember; 36 import std.typecons: Proxy; 37 38 enum hasInstance = hasMember!(Allocator, "instance"); 39 40 static if(is(Type == class)) 41 alias Pointer = Type; 42 else 43 alias Pointer = Type*; 44 45 static if(hasInstance) 46 /** 47 The allocator is a singleton, so no need to pass it in to the 48 constructor 49 */ 50 this(Args...)(auto ref Args args) { 51 makeObject(args); 52 } 53 else 54 /** 55 Non-singleton allocator, must be passed in 56 */ 57 this(Args...)(Allocator allocator, auto ref Args args) { 58 _allocator = allocator; 59 makeObject(args); 60 } 61 62 this(T)(Unique!(T, Allocator) other) if(is(T: Type)) { 63 moveFrom(other); 64 } 65 66 @disable this(this); 67 68 ~this() { 69 deleteObject; 70 } 71 72 /** 73 Gets the owned pointer. Use with caution. 74 */ 75 inout(Pointer) get() inout @system { 76 return _object; 77 } 78 79 /** 80 Releases ownership and transfers it to the returned 81 Unique object. 82 */ 83 Unique unique() { 84 import std.algorithm: move; 85 Unique u; 86 move(this, u); 87 assert(_object is null); 88 return u; 89 } 90 91 /** 92 "Truthiness" cast 93 */ 94 bool opCast(T)() const if(is(T == bool)) { 95 return _object !is null; 96 } 97 98 void opAssign(T)(Unique!(T, Allocator) other) if(is(T: Type)) { 99 deleteObject; 100 moveFrom(other); 101 } 102 103 mixin Proxy!_object; 104 105 private: 106 107 Pointer _object; 108 109 static if(hasInstance) 110 alias _allocator = Allocator.instance; 111 else 112 Allocator _allocator; 113 114 void makeObject(Args...)(auto ref Args args) { 115 import std.experimental.allocator: make; 116 version(LDC) 117 _object = () @trusted { return _allocator.make!Type(args); }(); 118 else 119 _object = _allocator.make!Type(args); 120 } 121 122 void deleteObject() @safe { 123 import std.experimental.allocator: dispose; 124 if(_object !is null) () @trusted { _allocator.dispose(_object); }(); 125 } 126 127 void moveFrom(T)(ref Unique!(T, Allocator) other) if(is(T: Type)) { 128 _object = other._object; 129 other._object = null; 130 131 static if(!hasInstance) { 132 import std.algorithm: move; 133 move(other._allocator, _allocator); 134 } 135 } 136 } 137 138 139 @("Unique with struct and test allocator") 140 @system unittest { 141 142 auto allocator = TestAllocator(); 143 { 144 const foo = Unique!(Struct, TestAllocator*)(&allocator, 5); 145 foo.twice.shouldEqual(10); 146 allocator.numAllocations.shouldEqual(1); 147 Struct.numStructs.shouldEqual(1); 148 } 149 150 Struct.numStructs.shouldEqual(0); 151 } 152 153 @("Unique with class and test allocator") 154 @system unittest { 155 156 auto allocator = TestAllocator(); 157 { 158 const foo = Unique!(Class, TestAllocator*)(&allocator, 5); 159 foo.twice.shouldEqual(10); 160 allocator.numAllocations.shouldEqual(1); 161 Class.numClasses.shouldEqual(1); 162 } 163 164 Class.numClasses.shouldEqual(0); 165 } 166 167 168 @("Unique with struct and mallocator") 169 @system unittest { 170 171 import std.experimental.allocator.mallocator: Mallocator; 172 { 173 const foo = Unique!(Struct, Mallocator)(5); 174 foo.twice.shouldEqual(10); 175 Struct.numStructs.shouldEqual(1); 176 } 177 178 Struct.numStructs.shouldEqual(0); 179 } 180 181 182 @("Unique default constructor") 183 @system unittest { 184 auto allocator = TestAllocator(); 185 186 auto ptr = Unique!(Struct, TestAllocator*)(); 187 (cast(bool)ptr).shouldBeFalse; 188 ptr.get.shouldBeNull; 189 190 ptr = Unique!(Struct, TestAllocator*)(&allocator, 5); 191 ptr.get.shouldNotBeNull; 192 ptr.get.twice.shouldEqual(10); 193 (cast(bool)ptr).shouldBeTrue; 194 } 195 196 @("Unique .init") 197 @system unittest { 198 auto allocator = TestAllocator(); 199 200 Unique!(Struct, TestAllocator*) ptr; 201 (cast(bool)ptr).shouldBeFalse; 202 ptr.get.shouldBeNull; 203 204 ptr = Unique!(Struct, TestAllocator*)(&allocator, 5); 205 ptr.get.shouldNotBeNull; 206 ptr.get.twice.shouldEqual(10); 207 (cast(bool)ptr).shouldBeTrue; 208 } 209 210 @("Unique move") 211 @system unittest { 212 import std.algorithm: move; 213 214 auto allocator = TestAllocator(); 215 auto oldPtr = Unique!(Struct, TestAllocator*)(&allocator, 5); 216 Unique!(Struct, TestAllocator*) newPtr; 217 move(oldPtr, newPtr); 218 oldPtr.shouldBeNull; 219 newPtr.twice.shouldEqual(10); 220 Struct.numStructs.shouldEqual(1); 221 } 222 223 @("Unique copy") 224 @system unittest { 225 auto allocator = TestAllocator(); 226 auto oldPtr = Unique!(Struct, TestAllocator*)(&allocator, 5); 227 Unique!(Struct, TestAllocator*) newPtr; 228 // non-copyable 229 static assert(!__traits(compiles, newPtr = oldPtr)); 230 } 231 232 @("Unique construct base class") 233 @system unittest { 234 auto allocator = TestAllocator(); 235 { 236 Unique!(Object, TestAllocator*) bar = Unique!(Class, TestAllocator*)(&allocator, 5); 237 Class.numClasses.shouldEqual(1); 238 } 239 240 Class.numClasses.shouldEqual(0); 241 } 242 243 @("Unique assign base class") 244 @system unittest { 245 auto allocator = TestAllocator(); 246 { 247 Unique!(Object, TestAllocator*) bar; 248 bar = Unique!(Class, TestAllocator*)(&allocator, 5); 249 Class.numClasses.shouldEqual(1); 250 } 251 252 Class.numClasses.shouldEqual(0); 253 } 254 255 @("Return Unique from function") 256 @system unittest { 257 auto allocator = TestAllocator(); 258 259 auto produce(int i) { 260 return Unique!(Struct, TestAllocator*)(&allocator, i); 261 } 262 263 auto ptr = produce(4); 264 ptr.twice.shouldEqual(8); 265 } 266 267 @("Unique unique") 268 @system unittest { 269 auto allocator = TestAllocator(); 270 auto oldPtr = Unique!(Struct, TestAllocator*)(&allocator, 5); 271 auto newPtr = oldPtr.unique; 272 newPtr.twice.shouldEqual(10); 273 oldPtr.shouldBeNull; 274 } 275 276 @("Unique @nogc") 277 @system @nogc unittest { 278 279 import std.experimental.allocator.mallocator: Mallocator; 280 281 { 282 const ptr = Unique!(NoGcStruct, Mallocator)(5); 283 // shouldEqual isn't @nogc 284 assert(ptr.i == 5); 285 assert(NoGcStruct.numStructs == 1); 286 } 287 288 assert(NoGcStruct.numStructs == 0); 289 } 290 291 @("Unique @nogc @safe") 292 @safe @nogc unittest { 293 294 auto allocator = SafeAllocator(); 295 296 { 297 const ptr = Unique!(NoGcStruct, SafeAllocator)(SafeAllocator(), 6); 298 // shouldEqual isn't @nogc 299 assert(ptr.i == 6); 300 assert(NoGcStruct.numStructs == 1); 301 } 302 303 assert(NoGcStruct.numStructs == 0); 304 } 305 306 @("Unique deref") 307 @system unittest { 308 { 309 auto allocator = TestAllocator(); 310 auto ptr = Unique!(Struct, TestAllocator*)(&allocator, 5); 311 *ptr = Struct(13); 312 ptr.twice.shouldEqual(26); 313 Struct.numStructs.shouldEqual(1); 314 } 315 Struct.numStructs.shouldEqual(0); 316 } 317 318 @("Unique move from populated other unique") 319 @system unittest { 320 321 import std.algorithm: move; 322 323 { 324 auto allocator = TestAllocator(); 325 326 auto ptr1 = Unique!(Struct, TestAllocator*)(&allocator, 5); 327 Struct.numStructs.shouldEqual(1); 328 329 { 330 auto ptr2 = Unique!(Struct, TestAllocator*)(&allocator, 10); 331 Struct.numStructs.shouldEqual(2); 332 move(ptr2, ptr1); 333 Struct.numStructs.shouldEqual(1); 334 ptr2.shouldBeNull; 335 ptr1.twice.shouldEqual(20); 336 } 337 338 } 339 340 Struct.numStructs.shouldEqual(0); 341 } 342 343 @("Unique assign to rvalue") 344 @system unittest { 345 346 import std.algorithm: move; 347 348 { 349 auto allocator = TestAllocator(); 350 351 auto ptr = Unique!(Struct, TestAllocator*)(&allocator, 5); 352 ptr = Unique!(Struct, TestAllocator*)(&allocator, 7); 353 354 Struct.numStructs.shouldEqual(1); 355 ptr.twice.shouldEqual(14); 356 } 357 358 Struct.numStructs.shouldEqual(0); 359 } 360 361 362 363 struct RefCounted(Type, Allocator) if(isAllocator!Allocator) { 364 import std.traits: hasMember; 365 import std.typecons: Proxy; 366 367 enum hasInstance = hasMember!(Allocator, "instance"); 368 369 static if(is(Type == class)) 370 alias Pointer = Type; 371 else 372 alias Pointer = Type*; 373 374 static if(hasInstance) 375 /** 376 The allocator is a singleton, so no need to pass it in to the 377 constructor 378 */ 379 this(Args...)(auto ref Args args) { 380 makeObject(args); 381 } 382 else 383 /** 384 Non-singleton allocator, must be passed in 385 */ 386 this(Args...)(Allocator allocator, auto ref Args args) { 387 _allocator = allocator; 388 makeObject(args); 389 } 390 391 this(this) { 392 assert(_impl !is null); 393 inc; 394 } 395 396 ~this() { 397 release; 398 } 399 400 /** 401 Assign to an lvalue RefCounted 402 */ 403 void opAssign(ref RefCounted other) { 404 405 if(_impl !is null) { 406 release; 407 } 408 static if(!hasInstance) 409 _allocator = other._allocator; 410 411 _impl = other._impl; 412 inc; 413 } 414 415 /** 416 Assign to an rvalue RefCounted 417 */ 418 void opAssign(RefCounted other) { 419 import std.algorithm: swap; 420 swap(_impl, other._impl); 421 static if(!hasInstance) 422 swap(_allocator, other._allocator); 423 } 424 425 /** 426 Dereference the smart pointer and yield a reference 427 to the contained type. 428 */ 429 ref inout(Type) opUnary(string s)() inout if(s == "*") { 430 return _impl._object; 431 } 432 433 alias _impl this; 434 435 private: 436 437 static struct Impl { 438 Type _object; 439 440 static if(is(Type == shared)) 441 shared size_t _count; 442 else 443 size_t _count; 444 445 alias _object this; 446 } 447 448 static if(hasInstance) 449 alias _allocator = Allocator.instance; 450 else 451 Allocator _allocator; 452 453 Impl* _impl; 454 455 void makeObject(Args...)(auto ref Args args) { 456 import std.conv: emplace; 457 458 allocateImpl; 459 emplace(&_impl._object, args); 460 } 461 462 void allocateImpl() { 463 import std.experimental.allocator: make; 464 import std.traits: hasIndirections; 465 466 _impl = cast(Impl*)_allocator.allocate(Impl.sizeof); 467 _impl._count= 1; 468 469 static if (hasIndirections!Type) { 470 import core.memory: GC; 471 GC.addRange(&_impl._object, Type.sizeof); 472 } 473 } 474 475 void release() { 476 if(_impl is null) return; 477 assert(_impl._count > 0); 478 479 dec; 480 481 if(_impl._count == 0) { 482 destroy(_impl._object); 483 auto mem = cast(void*)_impl; 484 _allocator.deallocate(mem[0 .. Impl.sizeof]); 485 } 486 } 487 488 void inc() { 489 static if(is(Type == shared)) { 490 import core.atomic: atomicOp; 491 _impl._count.atomicOp!"+="(1); 492 } else 493 ++_impl._count; 494 495 } 496 497 void dec() { 498 static if(is(Type == shared)) { 499 import core.atomic: atomicOp; 500 _impl._count.atomicOp!"-="(1); 501 } else 502 --_impl._count; 503 } 504 505 } 506 507 @("RefCounted struct test allocator no copies") 508 @system unittest { 509 auto allocator = TestAllocator(); 510 { 511 auto ptr = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 512 Struct.numStructs.shouldEqual(1); 513 } 514 Struct.numStructs.shouldEqual(0); 515 } 516 517 @("RefCounted struct test allocator one lvalue assignment") 518 @system unittest { 519 auto allocator = TestAllocator(); 520 { 521 auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 522 Struct.numStructs.shouldEqual(1); 523 RefCounted!(Struct, TestAllocator*) ptr2; 524 ptr2 = ptr1; 525 Struct.numStructs.shouldEqual(1); 526 } 527 Struct.numStructs.shouldEqual(0); 528 } 529 530 @("RefCounted struct test allocator one rvalue assignment test allocator") 531 @system unittest { 532 auto allocator = TestAllocator(); 533 { 534 RefCounted!(Struct, TestAllocator*) ptr; 535 ptr = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 536 Struct.numStructs.shouldEqual(1); 537 } 538 Struct.numStructs.shouldEqual(0); 539 } 540 541 @("RefCounted struct test allocator one rvalue assignment mallocator") 542 @system unittest { 543 import std.experimental.allocator.mallocator: Mallocator; 544 { 545 RefCounted!(Struct, Mallocator) ptr; 546 ptr = RefCounted!(Struct, Mallocator)(5); 547 Struct.numStructs.shouldEqual(1); 548 } 549 Struct.numStructs.shouldEqual(0); 550 } 551 552 553 @("RefCounted struct test allocator one lvalue copy constructor") 554 @system unittest { 555 auto allocator = TestAllocator(); 556 { 557 auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 558 Struct.numStructs.shouldEqual(1); 559 auto ptr2 = ptr1; 560 Struct.numStructs.shouldEqual(1); 561 562 ptr1.i.shouldEqual(5); 563 ptr2.i.shouldEqual(5); 564 } 565 Struct.numStructs.shouldEqual(0); 566 } 567 568 @("RefCounted struct test allocator one rvalue copy constructor") 569 @system unittest { 570 auto allocator = TestAllocator(); 571 { 572 auto ptr = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 573 Struct.numStructs.shouldEqual(1); 574 } 575 Struct.numStructs.shouldEqual(0); 576 } 577 578 @("RefCounted many copies made") 579 @system unittest { 580 auto allocator = TestAllocator(); 581 582 // helper function for intrusive testing, in case the implementation 583 // ever changes 584 size_t refCount(T)(ref T ptr) { 585 return ptr._impl._count; 586 } 587 588 { 589 auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 590 Struct.numStructs.shouldEqual(1); 591 592 auto ptr2 = ptr1; 593 Struct.numStructs.shouldEqual(1); 594 595 { 596 auto ptr3 = ptr2; 597 Struct.numStructs.shouldEqual(1); 598 599 refCount(ptr1).shouldEqual(3); 600 refCount(ptr2).shouldEqual(3); 601 refCount(ptr3).shouldEqual(3); 602 } 603 604 Struct.numStructs.shouldEqual(1); 605 refCount(ptr1).shouldEqual(2); 606 refCount(ptr2).shouldEqual(2); 607 608 auto produce() { 609 return RefCounted!(Struct, TestAllocator*)(&allocator, 3); 610 } 611 612 ptr1 = produce; 613 Struct.numStructs.shouldEqual(2); 614 refCount(ptr1).shouldEqual(1); 615 refCount(ptr2).shouldEqual(1); 616 617 ptr1.twice.shouldEqual(6); 618 ptr2.twice.shouldEqual(10); 619 } 620 621 Struct.numStructs.shouldEqual(0); 622 } 623 624 625 @("RefCounted deref") 626 @system unittest { 627 auto allocator = TestAllocator(); 628 auto rc1 = RefCounted!(int, TestAllocator*)(&allocator, 5); 629 630 (*rc1).shouldEqual(5); 631 auto rc2 = rc1; 632 *rc2 = 42; 633 (*rc1).shouldEqual(42); 634 } 635 636 @("RefCounted swap") 637 @system unittest { 638 import std.algorithm: swap; 639 RefCounted!(int, TestAllocator*) rc1, rc2; 640 swap(rc1, rc2); 641 } 642 643 @("phobos bug 6606") 644 @system unittest { 645 646 union U { 647 size_t i; 648 void* p; 649 } 650 651 struct S { 652 U u; 653 } 654 655 alias SRC = RefCounted!(S, TestAllocator*); 656 } 657 658 @("phobos bug 6436") 659 @system unittest 660 { 661 static struct S { 662 this(ref int val, string file = __FILE__, size_t line = __LINE__) { 663 val.shouldEqual(3, file, line); 664 ++val; 665 } 666 } 667 668 auto allocator = TestAllocator(); 669 int val = 3; 670 auto s = RefCounted!(S, TestAllocator*)(&allocator, val); 671 val.shouldEqual(4); 672 } 673 674 @("RefCounted assign from T") 675 @system unittest { 676 import std.experimental.allocator.mallocator: Mallocator; 677 678 { 679 auto a = RefCounted!(Struct, Mallocator)(3); 680 Struct.numStructs.shouldEqual(1); 681 682 *a = Struct(5); 683 Struct.numStructs.shouldEqual(1); 684 (*a).shouldEqual(Struct(5)); 685 686 RefCounted!(Struct, Mallocator) b; 687 b = a; 688 (*b).shouldEqual(Struct(5)); 689 Struct.numStructs.shouldEqual(1); 690 } 691 692 Struct.numStructs.shouldEqual(0); 693 } 694 695 version(LDC) {} 696 else { 697 @("RefCounted SharedStruct") 698 @system unittest { 699 auto allocator = TestAllocator(); 700 { 701 auto ptr = RefCounted!(shared SharedStruct, TestAllocator*)(&allocator, 5); 702 SharedStruct.numStructs.shouldEqual(1); 703 } 704 SharedStruct.numStructs.shouldEqual(0); 705 } 706 } 707 708 709 version(unittest) { 710 711 void _writelnUt(T...)(T args) { 712 try { 713 () @trusted { writelnUt(args); }(); 714 } catch(Exception ex) { 715 assert(false); 716 } 717 } 718 719 private struct Struct { 720 int i; 721 static int numStructs = 0; 722 723 this(int i) @safe nothrow { 724 this.i = i; 725 726 ++numStructs; 727 _writelnUt("Struct normal ctor ", &this, ", i=", i, ", N=", numStructs); 728 } 729 730 this(this) @safe nothrow { 731 ++numStructs; 732 _writelnUt("Struct postBlit ctor ", &this, ", i=", i, ", N=", numStructs); 733 } 734 735 ~this() @safe nothrow { 736 --numStructs; 737 _writelnUt("Struct dtor ", &this, ", i=", i, ", N=", numStructs); 738 } 739 740 int twice() @safe pure const nothrow { 741 return i * 2; 742 } 743 } 744 745 private struct SharedStruct { 746 int i; 747 static int numStructs = 0; 748 749 this(int i) @safe nothrow shared { 750 this.i = i; 751 752 ++numStructs; 753 try () @trusted { 754 writelnUt("Struct normal ctor ", &this, ", i=", i, ", N=", numStructs); 755 }(); 756 catch(Exception ex) {} 757 } 758 759 this(this) @safe nothrow shared { 760 ++numStructs; 761 try () @trusted { 762 writelnUt("Struct postBlit ctor ", &this, ", i=", i, ", N=", numStructs); 763 }(); 764 catch(Exception ex) {} 765 } 766 767 ~this() @safe nothrow shared { 768 --numStructs; 769 try () @trusted { writelnUt("Struct dtor ", &this, ", i=", i, ", N=", numStructs); }(); 770 catch(Exception ex) {} 771 } 772 773 int twice() @safe pure const nothrow shared { 774 return i * 2; 775 } 776 } 777 778 private class Class { 779 int i; 780 static int numClasses = 0; 781 782 this(int i) @safe nothrow { 783 this.i = i; 784 ++numClasses; 785 } 786 787 ~this() @safe nothrow { 788 --numClasses; 789 } 790 791 int twice() @safe pure const nothrow { 792 return i * 2; 793 } 794 } 795 796 private struct SafeAllocator { 797 798 import std.experimental.allocator.mallocator: Mallocator; 799 800 void[] allocate(size_t i) @trusted nothrow @nogc { 801 return Mallocator.instance.allocate(i); 802 } 803 804 void deallocate(void[] bytes) @trusted nothrow @nogc { 805 Mallocator.instance.deallocate(bytes); 806 } 807 } 808 809 static struct NoGcStruct { 810 int i; 811 812 static int numStructs = 0; 813 814 this(int i) @safe @nogc nothrow { 815 this.i = i; 816 817 ++numStructs; 818 } 819 820 this(this) @safe @nogc nothrow { 821 ++numStructs; 822 } 823 824 ~this() @safe @nogc nothrow { 825 --numStructs; 826 } 827 828 } 829 }