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 }