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 ptr = RefCounted!(shared SharedStruct)(5); 181 SharedStruct.numStructs.shouldEqual(1); 182 } 183 SharedStruct.numStructs.shouldEqual(0); 184 } 185 186 187 @("default.class.shared") 188 @system unittest { 189 { 190 auto ptr = RefCounted!(shared SharedClass)(5); 191 SharedClass.numClasss.shouldEqual(1); 192 } 193 SharedClass.numClasss.shouldEqual(0); 194 } 195 196 197 @("deref") 198 @system unittest { 199 auto allocator = TestAllocator(); 200 auto rc1 = RefCounted!(int, TestAllocator*)(&allocator, 5); 201 202 (*rc1).shouldEqual(5); 203 auto rc2 = rc1; 204 *rc2 = 42; 205 (*rc1).shouldEqual(42); 206 } 207 208 @("swap") 209 @system unittest { 210 import std.algorithm: swap; 211 RefCounted!(int, TestAllocator*) rc1, rc2; 212 swap(rc1, rc2); 213 } 214 215 @("phobos bug 6606") 216 @system unittest { 217 218 union U { 219 size_t i; 220 void* p; 221 } 222 223 struct S { 224 U u; 225 } 226 227 alias SRC = RefCounted!(S, TestAllocator*); 228 } 229 230 @("phobos bug 6436") 231 @system unittest 232 { 233 static struct S { 234 this(ref int val, string file = __FILE__, size_t line = __LINE__) { 235 val.shouldEqual(3, file, line); 236 ++val; 237 } 238 } 239 240 auto allocator = TestAllocator(); 241 int val = 3; 242 auto s = RefCounted!(S, TestAllocator*)(&allocator, val); 243 val.shouldEqual(4); 244 } 245 246 @("assign from T") 247 @safe unittest { 248 import std.experimental.allocator.mallocator: Mallocator; 249 250 { 251 auto a = RefCounted!(Struct, Mallocator)(3); 252 Struct.numStructs.shouldEqual(1); 253 254 *a = Struct(5); 255 Struct.numStructs.shouldEqual(1); 256 (*a).shouldEqual(Struct(5)); 257 258 RefCounted!(Struct, Mallocator) b; 259 b = a; 260 (*b).shouldEqual(Struct(5)); 261 Struct.numStructs.shouldEqual(1); 262 } 263 264 Struct.numStructs.shouldEqual(0); 265 } 266 267 @("assign self") 268 @system unittest { 269 auto allocator = TestAllocator(); 270 { 271 auto a = RefCounted!(Struct, TestAllocator*)(&allocator, 1); 272 a = a; 273 Struct.numStructs.shouldEqual(1); 274 } 275 Struct.numStructs.shouldEqual(0); 276 } 277 278 @("SharedStruct") 279 @system unittest { 280 auto allocator = TestAllocator(); 281 { 282 auto ptr = RefCounted!(shared SharedStruct, TestAllocator*)(&allocator, 5); 283 SharedStruct.numStructs.shouldEqual(1); 284 } 285 SharedStruct.numStructs.shouldEqual(0); 286 } 287 288 @("@nogc @safe") 289 @safe @nogc unittest { 290 291 auto allocator = SafeAllocator(); 292 293 { 294 const ptr = RefCounted!(NoGcStruct, SafeAllocator)(SafeAllocator(), 6); 295 assert(ptr.i == 6); 296 assert(NoGcStruct.numStructs == 1); 297 } 298 299 assert(NoGcStruct.numStructs == 0); 300 } 301 302 303 @("const object") 304 @system unittest { 305 auto allocator = TestAllocator(); 306 auto ptr1 = RefCounted!(const Struct, TestAllocator*)(&allocator, 5); 307 } 308 309 310 @("theAllocator") 311 @system unittest { 312 313 with(theTestAllocator) { 314 auto ptr = RefCounted!Struct(42); 315 (*ptr).shouldEqual(Struct(42)); 316 Struct.numStructs.shouldEqual(1); 317 } 318 319 Struct.numStructs.shouldEqual(0); 320 } 321 322 323 @("threads Mallocator") 324 @system unittest { 325 import std.experimental.allocator.mallocator: Mallocator; 326 static assert(__traits(compiles, sendRefCounted!Mallocator(7))); 327 } 328 329 @("threads SafeAllocator by value") 330 @system unittest { 331 // can't even use TestAllocator because it has indirections 332 // can't pass by pointer since it's an indirection 333 auto allocator = SafeAllocator(); 334 static assert(__traits(compiles, sendRefCounted!(SafeAllocator)(allocator, 7))); 335 } 336 337 @("threads SafeAllocator by shared pointer") 338 @system unittest { 339 // can't even use TestAllocator because it has indirections 340 // can't only pass by pointer if shared 341 auto allocator = shared SafeAllocator(); 342 static assert(__traits(compiles, sendRefCounted!(shared SafeAllocator*)(&allocator, 7))); 343 } 344 345 @("Construct RefCounted from Unique") 346 @system unittest { 347 import automem.unique: Unique; 348 auto allocator = TestAllocator(); 349 auto ptr = refCounted(Unique!(int, TestAllocator*)(&allocator, 42)); 350 (*ptr).shouldEqual(42); 351 } 352 353 @("RefCounted with class") 354 @system unittest { 355 auto allocator = TestAllocator(); 356 { 357 writelnUt("Creating ptr"); 358 auto ptr = RefCounted!(Class, TestAllocator*)(&allocator, 33); 359 (*ptr).i.shouldEqual(33); 360 Class.numClasses.shouldEqual(1); 361 } 362 Class.numClasses.shouldEqual(0); 363 } 364 365 @("@nogc class destructor") 366 @nogc unittest { 367 368 import automem: Unique; 369 370 auto allocator = SafeAllocator(); 371 372 { 373 const ptr = Unique!(NoGcClass, SafeAllocator)(SafeAllocator(), 6); 374 // shouldEqual isn't @nogc 375 assert(ptr.i == 6); 376 assert(NoGcClass.numClasses == 1); 377 } 378 379 assert(NoGcClass.numClasses == 0); 380 } 381 382 @("RefCounted opSlice and opIndex") 383 @system unittest { 384 import std.mmfile: MmFile; 385 auto file = RefCounted!MmFile(null, MmFile.Mode.readWriteNew, 120, null); 386 // The type of file[0] should be ubyte, not Impl. 387 static assert(is(typeof(file[0]) == typeof(MmFile.init[0]))); 388 // opSlice should result in void[] not Impl[]. 389 static assert(is(typeof(file[0 .. size_t.max]) == typeof(MmFile.init[0 .. size_t.max]))); 390 ubyte[] data = cast(ubyte[]) file[0 .. cast(size_t) file.length]; 391 immutable ubyte b = file[1]; 392 file[1] = cast(ubyte) (b + 1); 393 assert(data[1] == cast(ubyte) (b + 1)); 394 } 395 396 @("Construct RefCounted using global allocator for struct with zero-args ctor") 397 @system unittest { 398 struct S { 399 private ulong zeroArgsCtorTest = 3; 400 } 401 auto s = RefCounted!S.construct(); 402 static assert(is(typeof(s) == RefCounted!S)); 403 assert(s._impl !is null); 404 assert(s.zeroArgsCtorTest == 3); 405 } 406 407 408 409 void sendRefCounted(Allocator, Args...)(Args args) { 410 import std.concurrency: spawn, send; 411 412 auto tid = spawn(&threadFunc); 413 auto ptr = RefCounted!(shared SharedStruct, Allocator)(args); 414 415 tid.send(ptr); 416 } 417 418 void threadFunc() { 419 420 } 421 422 @("shared struct with indirection") 423 @system unittest { 424 auto s = RefCounted!(shared SharedStructWithIndirection)("foobar"); 425 } 426 427 428 @("copy from T.init") 429 unittest { 430 static struct X { 431 int i; 432 } 433 static struct Y { 434 RefCounted!X x; 435 } 436 Y y1; 437 Y y2; 438 y2 = y1; 439 } 440 441 442 @("number of allocations") 443 @safe unittest { 444 static TestAllocator allocator; 445 allocator.numAllocations.should == 0; 446 447 auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 77); 448 allocator.numAllocations.should == 1; 449 { 450 auto ptr2 = ptr1; 451 allocator.numAllocations.should == 1; 452 453 { 454 auto ptr = ptr2; 455 allocator.numAllocations.should == 1; 456 } 457 458 auto produce(int i) { 459 return typeof(ptr1)(&allocator, i); 460 } 461 462 ptr1 = produce(99); 463 allocator.numAllocations.should == 2; 464 } 465 466 allocator.numAllocations.should == 2; 467 }