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