The OpenD Programming Language

1 /++
2 $(H1 Thread-safe slim reference-counted shared pointers).
3 
4 This implementation does not support polymorphism.
5 +/
6 module mir.rc.slim_ptr;
7 
8 import mir.rc.context;
9 import mir.type_info;
10 import std.traits;
11 
12 package static immutable allocationExcMsg = "mir_slim_rcptr: out of memory error.";
13 package static immutable getExcMsg = "mir_slim_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 does not support polymorphism.
25 
26 The implementation never adds roots into the GC.
27 +/
28 struct mir_slim_rcptr(T)
29 {
30     static if (is(T == class) || is(T == interface) || is(T == struct) || is(T == union))
31         static assert(!__traits(isNested, T), "mir_slim_rcptr does not support nested types.");
32 
33     ///
34     static if (is(T == class) || is(T == interface))
35         package Unqual!T _value;
36     else
37         package T* _value;
38 
39     package ref mir_rc_context context() inout return scope pure nothrow @nogc @trusted @property
40     {
41         assert(_value);
42         return (cast(mir_rc_context*)_value)[-1];
43     }
44 
45     package void _reset()
46     {
47         _value = null;
48     }
49 
50     inout(void)* _thisPtr() inout return scope @trusted @property
51     {
52         return cast(inout(void)*) _value;
53     }
54 
55     package alias ThisTemplate = .mir_slim_rcptr;
56 
57     /// ditto
58     alias opUnary(string op : "*") = _get_value;
59     /// ditto
60     alias _get_value this;
61 
62     static if (is(T == class) || is(T == interface))
63         ///
64         pragma(inline, true)
65         inout(T) _get_value() return scope inout @property
66         {
67             assert(this, getExcMsg);
68             return _value;
69         }
70     else
71         ///
72         pragma(inline, true)
73         ref inout(T) _get_value() return scope inout @property
74         {
75             assert(this, getExcMsg);
76             return *_value;
77         }
78 
79     ///
80     void proxySwap(ref typeof(this) rhs) pure nothrow @nogc @safe
81     {
82         auto t = this._value;
83         this._value = rhs._value;
84         rhs._value = t;
85     }
86 
87     ///
88     this(typeof(null))
89     {
90     }
91 
92     ///
93     mixin CommonRCImpl;
94 
95 
96     ///
97     pragma(inline, true)
98     bool opEquals(typeof(null)) @safe scope const pure nothrow @nogc
99     {
100         return !this;
101     }
102 
103     /// ditto
104     bool opEquals(Y)(auto ref scope const ThisTemplate!Y rhs) @safe scope const pure nothrow @nogc
105     {
106         if (_thisPtr is null)
107             return rhs._thisPtr is null;
108         if (rhs._thisPtr is null)
109             return false;
110         return _get_value == rhs._get_value;
111     }
112 
113     ///
114     auto opCmp(Y)(auto ref scope const ThisTemplate!Y rhs) @trusted scope const pure nothrow @nogc
115     {
116         if (_thisPtr is null)
117             return (rhs._thisPtr is null) - 1;
118         if (rhs._thisPtr is null)
119             return 1;
120         return _get_value.opCmp(rhs._get_value);
121     }
122 
123     ///
124     size_t toHash() @trusted scope const pure nothrow @nogc
125     {
126         if (_thisPtr is null)
127             return 0;
128         return hashOf(_get_value);
129     }
130 
131     ///
132     ~this() nothrow
133     {
134         static if (hasElaborateDestructor!T || hasDestructor!T)
135         {
136             if (false) // break @safe and pure attributes
137             {
138                 Unqual!T* object;
139                 (*object).__xdtor;
140             }
141         }
142         if (this)
143         {
144             (() @trusted { mir_rc_decrease_counter(context); })();
145             debug _reset;
146         }
147     }
148 
149     static if (is(T == const) || is(T == immutable))
150     this(return ref scope const typeof(this) rhs) @trusted pure nothrow @nogc
151     {
152         if (rhs)
153         {
154             this._value = cast(typeof(this._value))rhs._value;
155             mir_rc_increase_counter(context);
156         }
157     }
158 
159     static if (is(T == immutable))
160     this(return ref scope const typeof(this) rhs) immutable @trusted pure nothrow @nogc
161     {
162         if (rhs)
163         {
164             this._value = cast(typeof(this._value))rhs._value;
165             mir_rc_increase_counter(context);
166         }
167     }
168 
169     static if (is(T == immutable))
170     this(return ref scope const typeof(this) rhs) const @trusted pure nothrow @nogc
171     {
172         if (rhs)
173         {
174             this._value = cast(typeof(this._value))rhs._value;
175             mir_rc_increase_counter(context);
176         }
177     }
178 
179     this(return ref scope inout typeof(this) rhs) inout @trusted pure nothrow @nogc
180     {
181         if (rhs)
182         {
183             this._value = rhs._value;
184             mir_rc_increase_counter(context);
185         }
186     }
187 
188     ///
189     ref opAssign(typeof(null)) scope return @trusted // pure nothrow @nogc
190     {
191         this = typeof(this).init;
192     }
193 
194     ///
195     ref opAssign(return typeof(this) rhs) scope return @trusted // pure nothrow @nogc
196     {
197         this.proxySwap(rhs);
198         return this;
199     }
200 
201     ///
202     ref opAssign(Q)(return ThisTemplate!Q rhs) scope return @trusted // pure nothrow @nogc
203         if (isImplicitlyConvertible!(Q*, T*))
204     {
205         this.proxySwap(*()@trusted{return cast(typeof(this)*)&rhs;}());
206         return this;
207     }
208 }
209 
210 ///
211 alias SlimRCPtr = mir_slim_rcptr;
212 
213 ///
214 template createSlimRC(T)
215     if (!is(T == interface) && !__traits(isAbstractClass, T))
216 {
217     ///
218     mir_slim_rcptr!T createSlimRC(Args...)(auto ref Args args)
219     {
220         typeof(return) ret;
221         with (ret) () @trusted {
222             auto context = mir_rc_create(mir_get_type_info!T, 1, mir_get_payload_ptr!T);
223             if (!context)
224             {
225                 version(D_Exceptions)
226                     { import mir.exception : toMutable; throw allocationError.toMutable; }
227                 else
228                     assert(0, allocationExcMsg);
229             }
230             _value = cast(typeof(_value))(context + 1);
231         } ();
232         import mir.functional: forward;
233         import mir.conv: emplace;
234         cast(void) emplace!T(ret._value, forward!args);
235         return ret;
236     }
237 }
238 
239 ///
240 version(mir_test)
241 @safe pure @nogc nothrow
242 unittest
243 {
244     auto a = createSlimRC!double(10);
245     auto b = a;
246     assert(*b == 10);
247     *b = 100;
248     assert(*a == 100);
249 }
250 
251 /// Classes
252 version(mir_test)
253 @safe pure @nogc nothrow
254 unittest
255 {
256     static class C
257     {
258         int index;
259         double value;
260         ref double bar() @safe pure nothrow @nogc { return value; }
261         this(double d) { value = d; }
262 
263         override size_t toHash() const scope @safe pure nothrow @nogc
264         {
265             return index;
266         }
267     }
268     auto a = createSlimRC!C(10);
269     assert(a._counter == 1);
270     auto b = a;
271     assert(a._counter == 2);
272     assert((*b).value == 10);
273     b.value = 100; // access via alias this syntax
274     assert(a.value == 100);
275     assert(a._counter == 2);
276 }
277 
278 /// Structs
279 version(mir_test)
280 @safe pure @nogc nothrow
281 unittest
282 {
283     struct S
284     {
285         double e;
286     }
287     struct C
288     {
289         int i;
290         S s;
291         // 'alias' should be accesable by reference
292         // or a class/interface
293         alias s this;
294     }
295 
296     auto a = createSlimRC!C(10, S(3));
297     auto s = a;
298     assert(s._counter == 2);
299     assert(s.e == 3);
300 }
301 
302 /// Classes with empty constructor
303 version(mir_test)
304 @safe pure @nogc nothrow
305 unittest
306 {
307     static class C
308     {
309         int index = 34;
310 
311         override size_t toHash() const scope @safe pure nothrow @nogc
312         {
313             return index;
314         }
315     }
316     assert(createSlimRC!C.index == 34);
317 }
318 
319 version(unittest):
320 
321 package struct _test_unpure_system_dest_s__ {
322     static int numStructs;
323     int i;
324 
325     this(this This)(int i) {
326         this.i = i;
327         ++numStructs;
328     }
329 
330     ~this() @system nothrow {
331         auto d = new int[2];
332         --numStructs;
333     }
334 }
335 
336 version(mir_test)
337 @system nothrow
338 unittest
339 {
340     import mir.rc.array;
341     auto ptr = createSlimRC!_test_unpure_system_dest_s__(42);
342     auto arr = rcarray!_test_unpure_system_dest_s__(3);
343 }