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 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 inout(Pointer) get() @safe pure nothrow inout { 73 return _object; 74 } 75 76 Unique unique() { 77 import std.algorithm: move; 78 Unique u; 79 move(this, u); 80 assert(_object is null); 81 return u; 82 } 83 84 Pointer release() @safe pure nothrow { 85 auto ret = _object; 86 _object = null; 87 return ret; 88 } 89 90 void reset(Pointer newObject) { 91 deleteObject; 92 _object = newObject; 93 } 94 95 bool opCast(T)() @safe pure nothrow const if(is(T == bool)) { 96 return _object !is null; 97 } 98 99 void opAssign(T)(Unique!(T, Allocator) other) if(is(T: Type)) { 100 deleteObject; 101 moveFrom(other); 102 } 103 104 mixin Proxy!_object; 105 106 private: 107 108 Pointer _object; 109 110 static if(hasInstance) 111 alias _allocator = Allocator.instance; 112 else 113 Allocator _allocator; 114 115 void makeObject(Args...)(auto ref Args args) { 116 import std.experimental.allocator: make; 117 _object = _allocator.make!Type(args); 118 } 119 120 void deleteObject() { 121 import std.experimental.allocator: dispose; 122 if(_object !is null) _allocator.dispose(_object); 123 } 124 125 void moveFrom(T)(ref Unique!(T, Allocator) other) if(is(T: Type)) { 126 _object = other._object; 127 other._object = null; 128 129 static if(!hasInstance) { 130 import std.algorithm: move; 131 move(other._allocator, _allocator); 132 } 133 } 134 } 135 136 137 @("Unique with struct and test allocator") 138 @system unittest { 139 140 auto allocator = TestAllocator(); 141 { 142 const foo = Unique!(Struct, TestAllocator*)(&allocator, 5); 143 foo.twice.shouldEqual(10); 144 allocator.numAllocations.shouldEqual(1); 145 Struct.numStructs.shouldEqual(1); 146 } 147 148 Struct.numStructs.shouldEqual(0); 149 } 150 151 @("Unique with class and test allocator") 152 @system unittest { 153 154 auto allocator = TestAllocator(); 155 { 156 const foo = Unique!(Class, TestAllocator*)(&allocator, 5); 157 foo.twice.shouldEqual(10); 158 allocator.numAllocations.shouldEqual(1); 159 Class.numClasses.shouldEqual(1); 160 } 161 162 Class.numClasses.shouldEqual(0); 163 } 164 165 166 @("Unique with struct and mallocator") 167 @system unittest { 168 169 import std.experimental.allocator.mallocator: Mallocator; 170 { 171 const foo = Unique!(Struct, Mallocator)(5); 172 foo.twice.shouldEqual(10); 173 Struct.numStructs.shouldEqual(1); 174 } 175 176 Struct.numStructs.shouldEqual(0); 177 } 178 179 180 @("Unique default constructor") 181 @system unittest { 182 auto allocator = TestAllocator(); 183 184 auto ptr = Unique!(Struct, TestAllocator*)(); 185 (cast(bool)ptr).shouldBeFalse; 186 ptr.get.shouldBeNull; 187 188 ptr = Unique!(Struct, TestAllocator*)(&allocator, 5); 189 ptr.get.shouldNotBeNull; 190 ptr.get.twice.shouldEqual(10); 191 (cast(bool)ptr).shouldBeTrue; 192 } 193 194 195 @("Unique release") 196 @system unittest { 197 import std.experimental.allocator: dispose; 198 199 auto allocator = TestAllocator(); 200 201 auto ptr = Unique!(Struct, TestAllocator*)(&allocator, 5); 202 auto obj = ptr.release; 203 obj.twice.shouldEqual(10); 204 allocator.dispose(obj); 205 } 206 207 @("Unique reset") 208 @system unittest { 209 import std.experimental.allocator: make; 210 211 auto allocator = TestAllocator(); 212 213 auto ptr = Unique!(Struct, TestAllocator*)(&allocator, 5); 214 ptr.reset(allocator.make!Struct(2)); 215 ptr.twice.shouldEqual(4); 216 } 217 218 @("Unique move") 219 @system unittest { 220 import std.algorithm: move; 221 222 auto allocator = TestAllocator(); 223 auto oldPtr = Unique!(Struct, TestAllocator*)(&allocator, 5); 224 Unique!(Struct, TestAllocator*) newPtr; 225 move(oldPtr, newPtr); 226 oldPtr.shouldBeNull; 227 newPtr.twice.shouldEqual(10); 228 } 229 230 @("Unique copy") 231 @system unittest { 232 import std.algorithm: move; 233 234 auto allocator = TestAllocator(); 235 auto oldPtr = Unique!(Struct, TestAllocator*)(&allocator, 5); 236 Unique!(Struct, TestAllocator*) newPtr; 237 // non-copyable 238 static assert(!__traits(compiles, newPtr = oldPtr)); 239 } 240 241 @("Unique construct base class") 242 @system unittest { 243 auto allocator = TestAllocator(); 244 { 245 Unique!(Object, TestAllocator*) bar = Unique!(Class, TestAllocator*)(&allocator, 5); 246 Class.numClasses.shouldEqual(1); 247 } 248 249 Class.numClasses.shouldEqual(0); 250 } 251 252 @("Unique assign base class") 253 @system unittest { 254 import std.algorithm: move; 255 auto allocator = TestAllocator(); 256 { 257 Unique!(Object, TestAllocator*) bar; 258 bar = Unique!(Class, TestAllocator*)(&allocator, 5); 259 Class.numClasses.shouldEqual(1); 260 } 261 262 Class.numClasses.shouldEqual(0); 263 } 264 265 @("Return Unique from function") 266 @system unittest { 267 auto allocator = TestAllocator(); 268 269 auto produce(int i) { 270 return Unique!(Struct, TestAllocator*)(&allocator, i); 271 } 272 273 auto ptr = produce(4); 274 ptr.twice.shouldEqual(8); 275 } 276 277 @("Unique unique") 278 @system unittest { 279 auto allocator = TestAllocator(); 280 auto oldPtr = Unique!(Struct, TestAllocator*)(&allocator, 5); 281 auto newPtr = oldPtr.unique; 282 newPtr.twice.shouldEqual(10); 283 oldPtr.shouldBeNull; 284 } 285 286 287 struct RefCounted(Type, Allocator) if(isAllocator!Allocator) { 288 import std.traits: hasMember; 289 import std.typecons: Proxy; 290 291 enum hasInstance = hasMember!(Allocator, "instance"); 292 293 static if(is(Type == class)) 294 alias Pointer = Type; 295 else 296 alias Pointer = Type*; 297 298 static if(hasInstance) 299 /** 300 The allocator is a singleton, so no need to pass it in to the 301 constructor 302 */ 303 this(Args...)(auto ref Args args) { 304 makeObject(args); 305 } 306 else 307 /** 308 Non-singleton allocator, must be passed in 309 */ 310 this(Args...)(Allocator allocator, auto ref Args args) { 311 _allocator = allocator; 312 makeObject(args); 313 } 314 315 this(this) @safe pure nothrow @nogc { 316 assert(_impl !is null); 317 ++_impl._count; 318 } 319 320 ~this() { 321 release; 322 } 323 324 void opAssign(ref RefCounted other) { 325 326 if(_impl !is null) { 327 release; 328 } 329 static if(!hasInstance) 330 _allocator = other._allocator; 331 332 _impl = other._impl; 333 ++_impl._count; 334 } 335 336 void opAssign(RefCounted other) { 337 import std.algorithm: swap; 338 swap(_impl, other._impl); 339 static if(!hasInstance) 340 swap(_allocator, other._allocator); 341 } 342 343 /** 344 If the allocator isn't a singleton, assigning to the raw type is unsafe. 345 If RefCounted was default-contructed then there is no allocator 346 */ 347 static if(hasInstance) { 348 void opAssign(Type object) { 349 import std.algorithm: move; 350 351 if(_impl is null) { 352 allocateImpl; 353 } 354 355 move(object, _impl._object); 356 } 357 } 358 359 ref inout(Type) get() inout { 360 return _impl._object; 361 } 362 363 alias _impl this; 364 365 private: 366 367 static struct Impl { 368 Type _object; 369 size_t _count; 370 alias _object this; 371 } 372 373 static if(hasInstance) 374 alias _allocator = Allocator.instance; 375 else 376 Allocator _allocator; 377 378 Impl* _impl; 379 380 void makeObject(Args...)(auto ref Args args) { 381 import std.conv: emplace; 382 383 allocateImpl; 384 emplace(&_impl._object, args); 385 } 386 387 void allocateImpl() { 388 import std.experimental.allocator: make; 389 import std.traits: hasIndirections; 390 import core.memory : GC; 391 392 _impl = cast(Impl*)_allocator.allocate(Impl.sizeof); 393 _impl._count= 1; 394 395 static if (hasIndirections!Type) 396 GC.addRange(&_impl._object, Type.sizeof); 397 } 398 399 void release() { 400 if(_impl is null) return; 401 assert(_impl._count > 0); 402 403 --_impl._count; 404 405 if(_impl._count == 0) { 406 destroy(_impl._object); 407 auto mem = cast(void*)_impl; 408 _allocator.deallocate(mem[0 .. Impl.sizeof]); 409 } 410 } 411 412 } 413 414 @("RefCounted struct test allocator no copies") 415 @system unittest { 416 auto allocator = TestAllocator(); 417 { 418 auto ptr = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 419 Struct.numStructs.shouldEqual(1); 420 } 421 Struct.numStructs.shouldEqual(0); 422 } 423 424 @("RefCounted struct test allocator one lvalue assignment") 425 @system unittest { 426 auto allocator = TestAllocator(); 427 { 428 auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 429 Struct.numStructs.shouldEqual(1); 430 RefCounted!(Struct, TestAllocator*) ptr2; 431 ptr2 = ptr1; 432 Struct.numStructs.shouldEqual(1); 433 } 434 Struct.numStructs.shouldEqual(0); 435 } 436 437 @("RefCounted struct test allocator one rvalue assignment test allocator") 438 @system unittest { 439 auto allocator = TestAllocator(); 440 { 441 RefCounted!(Struct, TestAllocator*) ptr; 442 ptr = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 443 Struct.numStructs.shouldEqual(1); 444 } 445 Struct.numStructs.shouldEqual(0); 446 } 447 448 @("RefCounted struct test allocator one rvalue assignment mallocator") 449 @system unittest { 450 import std.experimental.allocator.mallocator: Mallocator; 451 { 452 RefCounted!(Struct, Mallocator) ptr; 453 ptr = RefCounted!(Struct, Mallocator)(5); 454 Struct.numStructs.shouldEqual(1); 455 } 456 Struct.numStructs.shouldEqual(0); 457 } 458 459 460 @("RefCounted struct test allocator one lvalue copy constructor") 461 @system unittest { 462 auto allocator = TestAllocator(); 463 { 464 auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 465 Struct.numStructs.shouldEqual(1); 466 auto ptr2 = ptr1; 467 Struct.numStructs.shouldEqual(1); 468 } 469 Struct.numStructs.shouldEqual(0); 470 } 471 472 @("RefCounted struct test allocator one rvalue copy constructor") 473 @system unittest { 474 auto allocator = TestAllocator(); 475 { 476 auto ptr = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 477 Struct.numStructs.shouldEqual(1); 478 } 479 Struct.numStructs.shouldEqual(0); 480 } 481 482 @("RefCounted many copies made") 483 @system unittest { 484 auto allocator = TestAllocator(); 485 486 // helper function for intrusive testing, in case the implementation 487 // ever changes 488 size_t refCount(T)(ref T ptr) { 489 return ptr._impl._count; 490 } 491 492 { 493 auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 494 Struct.numStructs.shouldEqual(1); 495 496 auto ptr2 = ptr1; 497 Struct.numStructs.shouldEqual(1); 498 499 { 500 auto ptr3 = ptr2; 501 Struct.numStructs.shouldEqual(1); 502 503 refCount(ptr1).shouldEqual(3); 504 refCount(ptr2).shouldEqual(3); 505 refCount(ptr3).shouldEqual(3); 506 } 507 508 Struct.numStructs.shouldEqual(1); 509 refCount(ptr1).shouldEqual(2); 510 refCount(ptr2).shouldEqual(2); 511 512 auto produce() { 513 return RefCounted!(Struct, TestAllocator*)(&allocator, 3); 514 } 515 516 ptr1 = produce; 517 Struct.numStructs.shouldEqual(2); 518 refCount(ptr1).shouldEqual(1); 519 refCount(ptr2).shouldEqual(1); 520 521 ptr1.twice.shouldEqual(6); 522 ptr2.twice.shouldEqual(10); 523 } 524 525 Struct.numStructs.shouldEqual(0); 526 } 527 528 529 // TODO: get this to compile 530 // @("RefCounted reference semantics") 531 // @system unittest { 532 // auto allocator = TestAllocator(); 533 // auto rc1 = RefCounted!(int, TestAllocator*)(&allocator, 5); 534 535 // rc1.shouldEqual(5); 536 // auto rc2 = rc1; 537 // rc2 = 42; 538 // rc1.shouldEqual(42); 539 // } 540 541 @("RefCounted swap") 542 @system unittest { 543 import std.algorithm: swap; 544 RefCounted!(int, TestAllocator*) rc1, rc2; 545 swap(rc1, rc2); 546 } 547 548 @("phobos bug 6606") 549 @system unittest { 550 551 union U { 552 size_t i; 553 void* p; 554 } 555 556 struct S { 557 U u; 558 } 559 560 alias SRC = RefCounted!(S, TestAllocator*); 561 } 562 563 @("phobos bug 6436") 564 @system unittest 565 { 566 static struct S { 567 this(ref int val, string file = __FILE__, size_t line = __LINE__) { 568 val.shouldEqual(3, file, line); 569 ++val; 570 } 571 } 572 573 auto allocator = TestAllocator(); 574 int val = 3; 575 auto s = RefCounted!(S, TestAllocator*)(&allocator, val); 576 val.shouldEqual(4); 577 } 578 579 @("RefCounted assign from T") 580 @system unittest { 581 import std.experimental.allocator.mallocator: Mallocator; 582 583 { 584 auto a = RefCounted!(Struct, Mallocator)(3); 585 Struct.numStructs.shouldEqual(1); 586 587 a = Struct(5); 588 ++Struct.numStructs; // compensate for move not calling the constructor 589 Struct.numStructs.shouldEqual(1); 590 // TODO - change this to not use get 591 a.get.shouldEqual(Struct(5)); 592 593 RefCounted!(Struct, Mallocator) b; 594 b = a; 595 // TODO - change this to not use get 596 b.get.shouldEqual(Struct(5)); 597 Struct.numStructs.shouldEqual(1); 598 } 599 600 Struct.numStructs.shouldEqual(0); 601 } 602 603 version(unittest) { 604 605 private struct Struct { 606 int i; 607 static int numStructs = 0; 608 609 this(int i) @safe nothrow { 610 this.i = i; 611 612 ++numStructs; 613 try () @trusted { 614 writelnUt("Struct normal ctor ", &this, ", i=", i, ", N=", numStructs); 615 }(); 616 catch(Exception ex) {} 617 } 618 619 this(this) @safe nothrow { 620 ++numStructs; 621 try () @trusted { 622 writelnUt("Struct postBlit ctor ", &this, ", i=", i, ", N=", numStructs); 623 }(); 624 catch(Exception ex) {} 625 } 626 627 ~this() @safe nothrow { 628 --numStructs; 629 try () @trusted { writelnUt("Struct dtor ", &this, ", i=", i, ", N=", numStructs); }(); 630 catch(Exception ex) {} 631 } 632 633 int twice() @safe pure const nothrow { 634 return i * 2; 635 } 636 } 637 638 private class Class { 639 int i; 640 static int numClasses = 0; 641 642 this(int i) @safe nothrow { 643 this.i = i; 644 ++numClasses; 645 } 646 647 ~this() @safe nothrow { 648 --numClasses; 649 } 650 651 int twice() @safe pure const nothrow { 652 return i * 2; 653 } 654 } 655 }