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 }