1 module ut.ref_counted; 2 3 import ut; 4 import automem.ref_counted; 5 6 mixin TestUtils; 7 8 /// 9 @("struct test allocator no copies") 10 @system unittest { 11 auto allocator = TestAllocator(); 12 Struct.numStructs.should == 0; 13 { 14 auto ptr = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 15 Struct.numStructs.shouldEqual(1); 16 } 17 Struct.numStructs.shouldEqual(0); 18 } 19 20 @("struct test allocator one lvalue assignment") 21 @system unittest { 22 auto allocator = TestAllocator(); 23 { 24 auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 25 Struct.numStructs.shouldEqual(1); 26 27 RefCounted!(Struct, TestAllocator*) ptr2; 28 ptr2 = ptr1; 29 Struct.numStructs.shouldEqual(1); 30 } 31 Struct.numStructs.shouldEqual(0); 32 } 33 34 @("struct test allocator one lvalue assignment from T.init") 35 @system unittest { 36 37 auto allocator = TestAllocator(); 38 39 { 40 RefCounted!(Struct, TestAllocator*) ptr1; 41 Struct.numStructs.shouldEqual(0); 42 43 auto ptr2 = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 44 Struct.numStructs.shouldEqual(1); 45 46 ptr2 = ptr1; 47 Struct.numStructs.shouldEqual(0); 48 } 49 50 Struct.numStructs.shouldEqual(0); 51 } 52 53 @("struct test allocator one lvalue assignment both non-null") 54 @system unittest { 55 56 auto allocator = TestAllocator(); 57 58 { 59 auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 60 Struct.numStructs.shouldEqual(1); 61 62 auto ptr2 = RefCounted!(Struct, TestAllocator*)(&allocator, 7); 63 Struct.numStructs.shouldEqual(2); 64 65 ptr2 = ptr1; 66 Struct.numStructs.shouldEqual(1); 67 } 68 69 Struct.numStructs.shouldEqual(0); 70 } 71 72 73 74 @("struct test allocator one rvalue assignment test allocator") 75 @system unittest { 76 auto allocator = TestAllocator(); 77 { 78 RefCounted!(Struct, TestAllocator*) ptr; 79 ptr = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 80 Struct.numStructs.shouldEqual(1); 81 } 82 Struct.numStructs.shouldEqual(0); 83 } 84 85 @("struct test allocator one rvalue assignment mallocator") 86 @safe unittest { 87 import std.experimental.allocator.mallocator: Mallocator; 88 { 89 RefCounted!(Struct, Mallocator) ptr; 90 ptr = RefCounted!(Struct, Mallocator)(5); 91 Struct.numStructs.shouldEqual(1); 92 } 93 Struct.numStructs.shouldEqual(0); 94 } 95 96 97 @("struct test allocator one lvalue copy constructor") 98 @system unittest { 99 auto allocator = TestAllocator(); 100 { 101 auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 102 Struct.numStructs.shouldEqual(1); 103 auto ptr2 = ptr1; 104 Struct.numStructs.shouldEqual(1); 105 106 ptr1.i.shouldEqual(5); 107 ptr2.i.shouldEqual(5); 108 } 109 Struct.numStructs.shouldEqual(0); 110 } 111 112 @("struct test allocator one rvalue copy constructor") 113 @system unittest { 114 auto allocator = TestAllocator(); 115 { 116 auto ptr = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 117 Struct.numStructs.shouldEqual(1); 118 } 119 Struct.numStructs.shouldEqual(0); 120 } 121 122 @("many copies made") 123 @system unittest { 124 auto allocator = TestAllocator(); 125 126 // helper function for intrusive testing, in case the implementation 127 // ever changes 128 size_t refCount(T)(ref T ptr) { 129 return ptr._impl._count; 130 } 131 132 { 133 auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 134 Struct.numStructs.shouldEqual(1); 135 136 auto ptr2 = ptr1; 137 Struct.numStructs.shouldEqual(1); 138 139 { 140 auto ptr3 = ptr2; 141 Struct.numStructs.shouldEqual(1); 142 143 refCount(ptr1).shouldEqual(3); 144 refCount(ptr2).shouldEqual(3); 145 refCount(ptr3).shouldEqual(3); 146 } 147 148 Struct.numStructs.shouldEqual(1); 149 refCount(ptr1).shouldEqual(2); 150 refCount(ptr2).shouldEqual(2); 151 152 auto produce() { 153 return RefCounted!(Struct, TestAllocator*)(&allocator, 3); 154 } 155 156 ptr1 = produce; 157 Struct.numStructs.shouldEqual(2); 158 refCount(ptr1).shouldEqual(1); 159 refCount(ptr2).shouldEqual(1); 160 161 ptr1.twice.shouldEqual(6); 162 ptr2.twice.shouldEqual(10); 163 } 164 165 Struct.numStructs.shouldEqual(0); 166 } 167 168 @("default allocator") 169 @system unittest { 170 { 171 auto ptr = RefCounted!Struct(5); 172 Struct.numStructs.shouldEqual(1); 173 } 174 Struct.numStructs.shouldEqual(0); 175 } 176 177 @("default.struct.shared") 178 @system unittest { 179 { 180 auto ptr0 = RefCounted!(shared SharedStruct)(5); 181 SharedStruct.numStructs.shouldEqual(1); 182 auto ptr2 = ptr0; // copying the pointer ups the ref count but not # of structs 183 SharedStruct.numStructs.should == 1; 184 } 185 SharedStruct.numStructs.shouldEqual(0); 186 } 187 188 189 @("default.class.shared") 190 @system unittest { 191 { 192 auto ptr = RefCounted!(shared SharedClass)(5); 193 SharedClass.numClasss.shouldEqual(1); 194 } 195 SharedClass.numClasss.shouldEqual(0); 196 } 197 198 199 @("deref") 200 @system unittest { 201 auto allocator = TestAllocator(); 202 auto rc1 = RefCounted!(int, TestAllocator*)(&allocator, 5); 203 204 (*rc1).shouldEqual(5); 205 auto rc2 = rc1; 206 *rc2 = 42; 207 (*rc1).shouldEqual(42); 208 } 209 210 @("swap") 211 @system unittest { 212 import std.algorithm: swap; 213 RefCounted!(int, TestAllocator*) rc1, rc2; 214 swap(rc1, rc2); 215 } 216 217 @("phobos bug 6606") 218 @system unittest { 219 220 union U { 221 size_t i; 222 void* p; 223 } 224 225 struct S { 226 U u; 227 } 228 229 alias SRC = RefCounted!(S, TestAllocator*); 230 } 231 232 @("phobos bug 6436") 233 @system unittest 234 { 235 static struct S { 236 this(ref int val, string file = __FILE__, size_t line = __LINE__) { 237 val.shouldEqual(3, file, line); 238 ++val; 239 } 240 } 241 242 auto allocator = TestAllocator(); 243 int val = 3; 244 auto s = RefCounted!(S, TestAllocator*)(&allocator, val); 245 val.shouldEqual(4); 246 } 247 248 @("assign from T") 249 @safe unittest { 250 import std.experimental.allocator.mallocator: Mallocator; 251 252 { 253 auto a = RefCounted!(Struct, Mallocator)(3); 254 Struct.numStructs.shouldEqual(1); 255 256 *a = Struct(5); 257 Struct.numStructs.shouldEqual(1); 258 (*a).shouldEqual(Struct(5)); 259 260 RefCounted!(Struct, Mallocator) b; 261 b = a; 262 (*b).shouldEqual(Struct(5)); 263 Struct.numStructs.shouldEqual(1); 264 } 265 266 Struct.numStructs.shouldEqual(0); 267 } 268 269 @("assign self") 270 @system unittest { 271 auto allocator = TestAllocator(); 272 { 273 auto a = RefCounted!(Struct, TestAllocator*)(&allocator, 1); 274 a = a; 275 Struct.numStructs.shouldEqual(1); 276 } 277 Struct.numStructs.shouldEqual(0); 278 } 279 280 @("SharedStruct") 281 @system unittest { 282 auto allocator = TestAllocator(); 283 { 284 auto ptr = RefCounted!(shared SharedStruct, TestAllocator*)(&allocator, 5); 285 SharedStruct.numStructs.shouldEqual(1); 286 } 287 SharedStruct.numStructs.shouldEqual(0); 288 } 289 290 @("@nogc @safe") 291 @safe @nogc unittest { 292 293 auto allocator = SafeAllocator(); 294 295 { 296 const ptr = RefCounted!(NoGcStruct, SafeAllocator)(SafeAllocator(), 6); 297 assert(ptr.i == 6); 298 assert(NoGcStruct.numStructs == 1); 299 } 300 301 assert(NoGcStruct.numStructs == 0); 302 } 303 304 305 @("const object") 306 @system unittest { 307 auto allocator = TestAllocator(); 308 auto ptr1 = RefCounted!(const Struct, TestAllocator*)(&allocator, 5); 309 } 310 311 312 @("theAllocator") 313 @system unittest { 314 315 with(theTestAllocator) { 316 auto ptr = RefCounted!Struct(42); 317 (*ptr).shouldEqual(Struct(42)); 318 Struct.numStructs.shouldEqual(1); 319 } 320 321 Struct.numStructs.shouldEqual(0); 322 } 323 324 325 @("threads Mallocator") 326 @system unittest { 327 import std.experimental.allocator.mallocator: Mallocator; 328 static assert(__traits(compiles, sendRefCounted!Mallocator(7))); 329 } 330 331 @("threads SafeAllocator by value") 332 @system unittest { 333 // can't even use TestAllocator because it has indirections 334 // can't pass by pointer since it's an indirection 335 auto allocator = SafeAllocator(); 336 static assert(__traits(compiles, sendRefCounted!(SafeAllocator)(allocator, 7))); 337 } 338 339 @("threads SafeAllocator by shared pointer") 340 @system unittest { 341 // can't even use TestAllocator because it has indirections 342 // can't only pass by pointer if shared 343 auto allocator = shared SafeAllocator(); 344 static assert(__traits(compiles, sendRefCounted!(shared SafeAllocator*)(&allocator, 7))); 345 } 346 347 @("Construct RefCounted from Unique") 348 @system unittest { 349 import automem.unique: Unique; 350 auto allocator = TestAllocator(); 351 auto ptr = refCounted(Unique!(int, TestAllocator*)(&allocator, 42)); 352 (*ptr).shouldEqual(42); 353 } 354 355 @("RefCounted with class") 356 @system unittest { 357 auto allocator = TestAllocator(); 358 { 359 writelnUt("Creating ptr"); 360 auto ptr = RefCounted!(Class, TestAllocator*)(&allocator, 33); 361 (*ptr).i.shouldEqual(33); 362 Class.numClasses.shouldEqual(1); 363 } 364 Class.numClasses.shouldEqual(0); 365 } 366 367 @("@nogc class destructor") 368 @nogc unittest { 369 370 import automem: Unique; 371 372 auto allocator = SafeAllocator(); 373 374 { 375 const ptr = Unique!(NoGcClass, SafeAllocator)(SafeAllocator(), 6); 376 // shouldEqual isn't @nogc 377 assert(ptr.i == 6); 378 assert(NoGcClass.numClasses == 1); 379 } 380 381 assert(NoGcClass.numClasses == 0); 382 } 383 384 @("RefCounted opSlice and opIndex") 385 @system unittest { 386 import std.mmfile: MmFile; 387 auto file = RefCounted!MmFile(null, MmFile.Mode.readWriteNew, 120, null); 388 // The type of file[0] should be ubyte, not Impl. 389 static assert(is(typeof(file[0]) == typeof(MmFile.init[0]))); 390 // opSlice should result in void[] not Impl[]. 391 static assert(is(typeof(file[0 .. size_t.max]) == typeof(MmFile.init[0 .. size_t.max]))); 392 ubyte[] data = cast(ubyte[]) file[0 .. cast(size_t) file.length]; 393 immutable ubyte b = file[1]; 394 file[1] = cast(ubyte) (b + 1); 395 assert(data[1] == cast(ubyte) (b + 1)); 396 } 397 398 @("Construct RefCounted using global allocator for struct with zero-args ctor") 399 @system unittest { 400 struct S { 401 private ulong zeroArgsCtorTest = 3; 402 } 403 auto s = RefCounted!S.construct(); 404 static assert(is(typeof(s) == RefCounted!S)); 405 assert(s._impl !is null); 406 assert(s.zeroArgsCtorTest == 3); 407 } 408 409 410 411 void sendRefCounted(Allocator, Args...)(Args args) { 412 import std.concurrency: spawn, send; 413 414 auto tid = spawn(&threadFunc); 415 auto ptr = RefCounted!(shared SharedStruct, Allocator)(args); 416 417 tid.send(ptr); 418 } 419 420 void threadFunc() { 421 422 } 423 424 @("shared struct with indirection") 425 @system unittest { 426 auto s = RefCounted!(shared SharedStructWithIndirection)("foobar"); 427 } 428 429 430 @("copy from T.init") 431 unittest { 432 static struct X { 433 int i; 434 } 435 static struct Y { 436 RefCounted!X x; 437 } 438 Y y1; 439 Y y2; 440 y2 = y1; 441 } 442 443 444 @("number of allocations") 445 @safe unittest { 446 static TestAllocator allocator; 447 allocator.numAllocations.should == 0; 448 449 auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 77); 450 allocator.numAllocations.should == 1; 451 { 452 auto ptr2 = ptr1; 453 allocator.numAllocations.should == 1; 454 455 { 456 auto ptr = ptr2; 457 allocator.numAllocations.should == 1; 458 } 459 460 auto produce(int i) { 461 return typeof(ptr1)(&allocator, i); 462 } 463 464 ptr1 = produce(99); 465 allocator.numAllocations.should == 2; 466 } 467 468 allocator.numAllocations.should == 2; 469 }