The OpenD Programming Language

1 /++
2 $(H1 Thread-safe reference-counted shared pointers).
3 
4 This implementation supports class and struct (`alias this`) polymorphism.
5 +/
6 module mir.rc.ptr;
7 
8 import mir.rc.context;
9 import mir.type_info;
10 import std.traits;
11 
12 package static immutable allocationExcMsg = "mir_rcptr: out of memory error.";
13 package static immutable getExcMsg = "mir_rcptr: trying to use null value.";
14 
15 version (D_Exceptions)
16 {
17     import core.exception: OutOfMemoryError, InvalidMemoryOperationError;
18     package static immutable allocationError = new OutOfMemoryError(allocationExcMsg);
19 }
20 
21 /++
22 Thread safe reference counting array.
23 
24 This implementation supports class and struct (`alias this`) polymorphism.
25 
26 `__xdtor` if any is used to destruct objects.
27 
28 The implementation never adds roots into the GC.
29 +/
30 struct mir_rcptr(T)
31 {
32     static if (is(T == class) || is(T == interface) || is(T == struct) || is(T == union))
33         static assert(!__traits(isNested, T), "mir_rcptr does not support nested types.");
34 
35     ///
36     static if (is(T == class) || is(T == interface))
37         package Unqual!T _value;
38     else
39         package T* _value;
40     package mir_rc_context* _context;
41 
42     package ref mir_rc_context context() inout return scope @trusted @property
43     {
44         return *cast(mir_rc_context*)_context;
45     }
46 
47     package void _reset()
48     {
49         _value = null;
50         _context = null;
51     }
52 
53     inout(void)* _thisPtr() inout return scope @trusted @property
54     {
55         return cast(inout(void)*) _value;
56     }
57 
58     package alias ThisTemplate = .mir_rcptr;
59 
60     /// ditto
61     alias opUnary(string op : "*") = _get_value;
62     /// ditto
63     alias _get_value this;
64 
65     static if (is(T == class) || is(T == interface))
66         ///
67         pragma(inline, true)
68         inout(T) _get_value() return scope inout @property
69         {
70             assert(this, getExcMsg);
71             return _value;
72         }
73     else
74         ///
75         pragma(inline, true)
76         ref inout(T) _get_value() return scope inout @property
77         {
78             assert(this, getExcMsg);
79             return *_value;
80         }
81 
82     ///
83     void proxySwap(ref typeof(this) rhs) pure nothrow @nogc @safe
84     {
85         auto t0 = this._value;
86         auto t1 = this._context;
87         this._value = rhs._value;
88         this._context = rhs._context;
89         rhs._value = t0;
90         rhs._context = t1;
91     }
92 
93     ///
94     this(typeof(null))
95     {
96     }
97 
98     ///
99     mixin CommonRCImpl;
100 
101 
102     ///
103     pragma(inline, true)
104     bool opEquals(typeof(null)) @safe scope const pure nothrow @nogc
105     {
106         return !this;
107     }
108 
109     /// ditto
110     bool opEquals(Y)(auto ref scope const ThisTemplate!Y rhs) @safe scope const pure nothrow @nogc
111     {
112         if (_thisPtr is null)
113             return rhs._thisPtr is null;
114         if (rhs._thisPtr is null)
115             return false;
116         return _get_value == rhs._get_value;
117     }
118 
119     ///
120     auto opCmp(Y)(auto ref scope const ThisTemplate!Y rhs) @trusted scope const pure nothrow @nogc
121     {
122         if (_thisPtr is null)
123             return (rhs._thisPtr is null) - 1;
124         if (rhs._thisPtr is null)
125             return 1;
126         return _get_value.opCmp(rhs._get_value);
127     }
128 
129     ///
130     size_t toHash() @trusted scope const pure nothrow @nogc
131     {
132         if (_thisPtr is null)
133             return 0;
134         return hashOf(_get_value);
135     }
136 
137     ///
138     ~this() nothrow
139     {
140         static if (hasElaborateDestructor!T || hasDestructor!T)
141         {
142             if (false) // break @safe and pure attributes
143             {
144                 Unqual!T* object;
145                 (*object).__xdtor;
146             }
147         }
148         if (this)
149         {
150             (() @trusted { mir_rc_decrease_counter(context); })();
151             debug _reset;
152         }
153     }
154 
155     static if (is(T == const) || is(T == immutable))
156     this(return ref scope const typeof(this) rhs) @trusted pure nothrow @nogc
157     {
158         if (rhs)
159         {
160             this._value = cast(typeof(this._value))rhs._value;
161             this._context = cast(typeof(this._context))rhs._context;
162             mir_rc_increase_counter(context);
163         }
164     }
165 
166     static if (is(T == immutable))
167     this(return ref scope const typeof(this) rhs) immutable @trusted pure nothrow @nogc
168     {
169         if (rhs)
170         {
171             this._value = cast(typeof(this._value))rhs._value;
172             this._context = cast(typeof(this._context))rhs._context;
173             mir_rc_increase_counter(context);
174         }
175     }
176 
177     static if (is(T == immutable))
178     this(return ref scope const typeof(this) rhs) const @trusted pure nothrow @nogc
179     {
180         if (rhs)
181         {
182             this._value = cast(typeof(this._value))rhs._value;
183             this._context = cast(typeof(this._context))rhs._context;
184             mir_rc_increase_counter(context);
185         }
186     }
187 
188     this(return ref scope inout typeof(this) rhs) inout @trusted pure nothrow @nogc
189     {
190         if (rhs)
191         {
192             this._value = rhs._value;
193             this._context = rhs._context;
194             mir_rc_increase_counter(context);
195         }
196     }
197 
198     ///
199     ref opAssign(typeof(null)) scope return @trusted // pure nothrow @nogc
200     {
201         this = typeof(this).init;
202     }
203 
204     ///
205     ref opAssign(return typeof(this) rhs) scope return @trusted // pure nothrow @nogc
206     {
207         this.proxySwap(rhs);
208         return this;
209     }
210 
211     ///
212     ref opAssign(Q)(return ThisTemplate!Q rhs) scope return @trusted // pure nothrow @nogc
213         if (isImplicitlyConvertible!(Q*, T*))
214     {
215         this.proxySwap(*()@trusted{return cast(typeof(this)*)&rhs;}());
216         return this;
217     }
218 }
219 
220 ///
221 alias RCPtr = mir_rcptr;
222 
223 /++
224 Returns: shared pointer of the member and the context from the current pointer. 
225 +/
226 auto shareMember(string member, T, Args...)(return mir_rcptr!T context, auto ref Args args) @safe
227 {
228     import core.lifetime: move;
229     void foo(A)(auto ref A) {}
230     assert(context != null);
231     static if (args.length)
232     {
233         // breaks safaty
234         if (false) foo(__traits(getMember, T.init, member)(forward!args));
235         return (()@trusted => createRCWithContext(__traits(getMember, context._get_value, member)(forward!args), context.move))();
236     }
237     else
238     {
239         // breaks safaty
240         if (false) foo(__traits(getMember, T.init, member));
241         return (()@trusted => createRCWithContext(__traits(getMember, context._get_value, member), context.move))();
242     }
243 }
244 
245 /++
246 Returns: shared pointer constructed with current context. 
247 +/
248 @system .mir_rcptr!R createRCWithContext(R, F)(return R value, return const mir_rcptr!F context)
249     if (is(R == class) || is(R == interface))
250 {
251     typeof(return) ret;
252     ret._value = cast()value;
253     ret._context = cast(mir_rc_context*)context._context;
254     (*cast(mir_rcptr!F*)&context)._value = null;
255     (*cast(mir_rcptr!F*)&context)._context = null;
256     return ret;
257 }
258 
259 ///ditto
260 @system .mir_rcptr!R createRCWithContext(R, F)(return ref R value, return const mir_rcptr!F context)
261     if (!is(R == class) && !is(R == interface))
262 {
263     typeof(return) ret;
264     ret._value = &value;
265     ret._context = cast(mir_rc_context*)context._context;
266     (*cast(mir_rcptr!F*)&context)._value = null;
267     (*cast(mir_rcptr!F*)&context)._context = null;
268     return ret;
269 }
270 
271 /++
272 Construct a shared pointer of a required type with a current context.
273 Provides polymorphism abilities for classes and structures with `alias this` syntax.
274 +/
275 mir_rcptr!R castTo(R, T)(return mir_rcptr!T context) @trusted
276     if (isImplicitlyConvertible!(T, R))
277 {
278     import core.lifetime: move;
279     return createRCWithContext(cast(R)context._get_value, move(context));
280 }
281 
282 /// ditto
283 mir_rcptr!(const R) castTo(R, T)(return const mir_rcptr!T context) @trusted
284     if (isImplicitlyConvertible!(const T, const R))
285 {
286     import core.lifetime: move;
287     return createRCWithContext(cast(const R)context._get_value, move(*cast(mir_rcptr!T*)&context));
288 }
289 
290 /// ditto
291 mir_rcptr!(immutable R) castTo(R, T)(return immutable mir_rcptr!T context) @trusted
292     if (isImplicitlyConvertible!(immutable T, immutable R))
293 {
294     import core.lifetime: move;
295     return createRCWithContext(cast(immutable R)context._get_value, move(*cast(mir_rcptr!T*)&context));
296 }
297 
298 ///
299 template createRC(T)
300     if (!is(T == interface) && !__traits(isAbstractClass, T))
301 {
302     ///
303     mir_rcptr!T createRC(Args...)(auto ref Args args)
304     {
305         typeof(return) ret;
306         with (ret) () @trusted {
307             _context = mir_rc_create(mir_get_type_info!T, 1, mir_get_payload_ptr!T);
308             if (!_context)
309             {
310                 version(D_Exceptions)
311                     { import mir.exception : toMutable; throw allocationError.toMutable; }
312                 else
313                     assert(0, allocationExcMsg);
314             }
315             _value = cast(typeof(_value))(_context + 1);
316         } ();
317         import core.lifetime: forward;
318         import mir.conv: emplace;
319         cast(void) emplace!T(ret._value, forward!args);
320         return ret;
321     }
322 }
323 
324 ///
325 version(mir_test)
326 @safe pure @nogc nothrow
327 unittest
328 {
329     auto a = createRC!double(10);
330     auto b = a;
331     assert(*b == 10);
332     *b = 100;
333     assert(*a == 100);
334 }
335 
336 /// Classes with empty constructor
337 version(mir_test)
338 @safe pure @nogc nothrow
339 unittest
340 {
341     static class C
342     {
343         int index = 34;
344 
345         override size_t toHash() const scope @safe pure nothrow @nogc
346         {
347             return index;
348         }
349     }
350     assert(createRC!C.index == 34);
351 }
352 
353 ///
354 version(mir_test)
355 @safe pure @nogc nothrow
356 unittest
357 {
358     static interface I {
359         ref double bar() @safe pure nothrow @nogc;
360         size_t toHash() @trusted scope const pure nothrow @nogc;
361     }
362     static abstract class D
363     {
364         int index;
365 
366         override size_t toHash() const scope @safe pure nothrow @nogc
367         {
368             return index;
369         }
370     }
371     static class C : D, I
372     {
373         double value;
374         ref double bar() @safe pure nothrow @nogc { return value; }
375         this(double d){ value = d; }
376 
377         override size_t toHash() const scope @safe pure nothrow @nogc
378         {
379             return hashOf(value, super.toHash);
380         }
381     }
382     auto a = createRC!C(10);
383     assert(a._counter == 1);
384     auto b = a;
385     assert(a._counter == 2);
386     assert((*b).value == 10);
387     b.value = 100; // access via alias this syntax
388     assert(a.value == 100);
389     assert(a._counter == 2);
390 
391     auto d = a.castTo!D; //RCPtr!D
392     assert(d._counter == 3);
393     d.index = 234;
394     assert(a.index == 234);
395     auto i = a.castTo!I; //RCPtr!I
396     assert(i.bar == 100);
397     assert(i._counter == 4);
398 
399     auto v = a.shareMember!"value"; //RCPtr!double
400     auto w = a.shareMember!"bar"; //RCPtr!double
401     assert(i._counter == 6);
402     assert(*v == 100);
403     ()@trusted{assert(&*w is &*v);}();
404 }
405 
406 /// 'Alias This' support
407 version(mir_test)
408 @safe pure @nogc nothrow
409 unittest
410 {
411     struct S
412     {
413         double e;
414     }
415     struct C
416     {
417         int i;
418         S s;
419         // 'alias' should be accesable by reference
420         // or a class/interface
421         alias s this;
422     }
423 
424     auto a = createRC!C(10, S(3));
425     auto s = a.castTo!S; // RCPtr!S
426     assert(s._counter == 2);
427     assert(s.e == 3);
428 }
429 
430 version(unittest):
431 
432 package struct _test_unpure_system_dest_s__ {
433     static int numStructs;
434     int i;
435 
436     this(this This)(int i) {
437         this.i = i;
438         ++numStructs;
439     }
440 
441     ~this() @system nothrow {
442         auto d = new int[2];
443         --numStructs;
444     }
445 }
446 
447 version(mir_test)
448 @system nothrow
449 unittest
450 {
451     import mir.rc.array;
452     auto ptr = createRC!_test_unpure_system_dest_s__(42);
453     auto arr = rcarray!_test_unpure_system_dest_s__(3);
454 }