1 module yu.memory.sharedref;
2 
3 import core.atomic;
4 import std.experimental.allocator;
5 import std.experimental.allocator.mallocator : Mallocator;
6 import std.experimental.allocator.building_blocks.free_list : SharedFreeList;
7 
8 static import std.algorithm;
9 static import std.algorithm.mutation;
10 import std.traits;
11 import yu.traits : isInheritClass, Pointer;
12 
13 struct ISharedRef(Allocator, T, bool Shared = true) {
14     enum isSaticAlloc = (stateSize!Allocator == 0);
15     static if (isSaticAlloc)
16         alias Alloc = typeof(Allocator.instance);
17     else
18         alias Alloc = Allocator;
19 
20     enum isShared = Shared || is(T == shared);
21     enum isSharedRef = true;
22     alias ValueType = Pointer!T;
23     alias Deleter = void function(ref Alloc, ValueType);
24     alias Data = ExternalRefCountData!(Alloc, isShared);
25     alias DataWithDeleter = ExternalRefCountDataWithDeleter!(Alloc, ValueType, isShared);
26     alias TWeakRef = IWeakRef!(Allocator, T, Shared);
27     alias TSharedRef = ISharedRef!(Allocator, T, Shared);
28 
29     static if (isSaticAlloc) {
30         this(ValueType ptr) {
31             internalConstruct(ptr, &defaultDeleter);
32         }
33 
34         this(ValueType ptr, Deleter deleter) {
35             internalConstruct(ptr, deleter);
36         }
37     } else {
38         this(Alloc alloc, ValueType ptr) {
39             _alloc = alloc;
40             internalConstruct(ptr, &defaultDeleter);
41         }
42 
43         this(Alloc alloc, ValueType ptr, Deleter deleter) {
44             _alloc = alloc;
45             internalConstruct(ptr, deleter);
46         }
47 
48         @property Alloc allocator() nothrow {
49             return _alloc;
50         }
51     }
52 
53     this(this){
54         if (_dd) {
55             _dd.strongRef();
56             _dd.weakRef();
57         }
58     }
59 
60     this(WEAK)(auto ref WEAK wptr)  
61         if(is(WEAK == struct) && WEAK.isWeakRef && __traits(isSame, WEAK.Data, Data))
62     {
63         internalSet(wptr._dd, wptr._alloc, cast(ValueType)wptr._ptr);
64     }
65 
66     ~this() {
67         deref();
68     }
69 
70     alias data this;
71 
72     @property ValueType data() nothrow {
73         return _ptr;
74     }
75 
76     @property bool isNull() const nothrow {
77         return (_ptr is null);
78     }
79 
80     pragma(inline) void swap(ref TSharedRef tref) {
81         std.algorithm.mutation.swap(tref._dd, this._dd);
82         std.algorithm.mutation.swap(tref._ptr, this._ptr);
83         static if (!isSaticAlloc)
84             std.algorithm.mutation.swap(tref._alloc, this._alloc);
85     }
86 
87     pragma(inline, true) void rest() {
88         clear();
89     }
90 
91     pragma(inline) void clear() {
92         TSharedRef copy = TSharedRef.init;
93         swap(copy);
94     }
95 
96     static if (isSaticAlloc) {
97         pragma(inline, true) void rest()(ValueType ptr) {
98             TSharedRef copy = TSharedRef(ptr);
99             swap(copy);
100         }
101 
102         pragma(inline, true) void rest()(ValueType ptr, Deleter deleter) {
103             TSharedRef copy = TSharedRef(ptr, deleter);
104             swap(copy);
105         }
106     } else {
107         pragma(inline, true) void rest()(Alloc alloc, ValueType ptr) {
108             TSharedRef copy = TSharedRef(alloc, ptr);
109             swap(copy);
110         }
111 
112         pragma(inline, true) void rest()(Alloc alloc, ValueType ptr, Deleter deleter) {
113             TSharedRef copy = TSharedRef(alloc, ptr, deleter);
114             swap(copy);
115         }
116     }
117 
118     TWeakRef toWeakRef() {
119         return TWeakRef(this);
120     }
121 
122     auto castTo(U)() {
123         ISharedRef!(Alloc, U, isShared) result;
124         if (isNull)
125             return result;
126         alias CastType = Pointer!U;
127         CastType u = cast(CastType) _ptr;
128         if (u !is null)
129             result.internalSet(_dd, _alloc, u);
130         return result;
131     }
132 
133     void opAssign(THIS)(auto ref THIS rv)if(is(THIS : typeof(this))){
134         if(rv._dd is _dd) return;
135         auto copy = cast(typeof(this))rv;
136         swap(copy);
137     }
138 
139     void opAssign(WEAK)(auto ref WEAK rhs) 
140         if(is(WEAK == struct) && WEAK.isWeakRef && __traits(isSame, WEAK.Data, Data)) 
141     {
142         internalSet(rhs._dd, rhs._alloc, cast(ValueType)rhs._ptr);
143     }
144 
145     static if (isPointer!ValueType) {
146         ref T opUnary(string op)() if (op == "*") {
147             return *_ptr;
148         }
149     }
150 
151 private:
152 
153     static void defaultDeleter(ref Alloc alloc, ValueType value)  {
154         alloc.dispose(value);
155     }
156 
157     void deref() {
158         _ptr = null;
159         deref(_dd, _alloc);
160     }
161 
162     static void deref(ref Data dd, ref Alloc alloc) {
163         if (!dd)
164             return;
165         if (!dd.strongDef()) {
166             dd.free(alloc);
167         }
168         if (!dd.weakDef()) {
169             scope(exit) dd = null;
170             sharedRefAllocator.dispose(dd);
171         }
172     }
173 
174     void internalConstruct(ValueType ptr, Deleter deleter) {
175         _ptr = ptr;
176         if (ptr !is null) {
177             _dd = sharedRefAllocator.make!(DataWithDeleter)(ptr, deleter);
178             static if(hasMember!(T,"__InitializeFromSharedPointer")) {
179                 auto tpytr = cast(Unqual!ValueType)(_ptr);
180                 tpytr.__InitializeFromSharedPointer(this);
181             }
182         }
183     }
184 
185     void internalSet(Data o, ref Alloc alloc, ValueType ptr) {
186         static if (!isSaticAlloc) {
187             Alloc tmpalloc = _alloc;
188             _alloc = alloc;
189         } else {
190             alias tmpalloc = Alloc.instance;
191         }
192         if (o) {
193             if (o.strongref > 0) {
194                 o.strongRef();
195                 o.weakRef();
196                 _ptr = ptr;
197             } else {
198                 _ptr = null;
199                 o = null;
200             }
201         }
202         std.algorithm.mutation.swap(_dd, o);
203         deref(o, tmpalloc);
204     }
205 
206     ValueType _ptr; // 
207     Data _dd;
208     static if (!isSaticAlloc)
209         Alloc _alloc;
210     else
211         alias _alloc = Alloc.instance;
212 }
213 
214 struct IWeakRef(Allocator, T, bool Shared = true) {
215     enum isSaticAlloc = (stateSize!Allocator == 0);
216     static if (isSaticAlloc)
217         alias Alloc = typeof(Allocator.instance);
218     else
219         alias Alloc = Allocator;
220     enum isShared =  Shared || is(T == shared);
221     enum isWeakRef = true;
222     alias ValueType = Pointer!T;
223     alias Data = ExternalRefCountData!(Alloc, isShared);
224     alias TWeakRef = IWeakRef!(Allocator, T, Shared);
225     alias TSharedRef = ISharedRef!(Allocator,T, Shared);
226 
227     this(SHARED)(auto ref SHARED tref)
228         if(is(SHARED == struct) && SHARED.isSharedRef && __traits(isSame, SHARED.Data, Data)) 
229     {
230         this._ptr = cast(ValueType)tref._ptr;
231         this._dd = tref._dd;
232         if (_dd)
233             _dd.weakRef();
234         static if (!isSaticAlloc)
235             this._alloc = tref._alloc;
236     }
237 
238     this(this){
239         if (_dd)
240             _dd.weakRef();
241     }
242 
243     pragma(inline, true) bool isNull() nothrow {
244         return (_dd is null || _ptr is null || _dd.strongref == 0);
245     }
246 
247     pragma(inline, true) ValueType data() nothrow {
248         return isNull() ? null : _ptr;
249     }
250 
251     pragma(inline) void clear() {
252         TWeakRef copy = TWeakRef.init;
253         swap(copy);
254     }
255 
256     pragma(inline) void swap(ref TWeakRef tref) {
257         std.algorithm.mutation.swap(tref._dd, this._dd);
258         std.algorithm.mutation.swap(tref._ptr, this._ptr);
259         static if (!isSaticAlloc)
260             std.algorithm.mutation.swap(tref._alloc, this._alloc);
261     }
262 
263     pragma(inline, true) TSharedRef toStrongRef() {
264         return TSharedRef(this);
265     }
266 
267     void opAssign(THIS)(auto ref THIS rv)if(is(THIS : typeof(this))){
268         if(rv._dd is _dd) return;
269         auto copy = rv;
270         swap(copy);
271     }
272 
273     void opAssign(SHARED)(auto ref SHARED rhs) 
274         if(is(SHARED == struct) && SHARED.isSharedRef && __traits(isSame, SHARED.Data, Data))
275     {
276         internalSet(rhs._dd, rhs._alloc, cast(ValueType)rhs._ptr);
277     }
278 
279 private:
280     void deref() {
281         _ptr = null;
282         if (!_dd) return;
283         if (!_dd.weakDef()) {
284             scope(exit) _dd = null;
285             sharedRefAllocator.dispose(_dd);
286         }
287     }
288 
289     void internalSet(Data o, ref Alloc alloc, ValueType ptr) {
290         if (_dd is o)
291             return;
292         if (o) {
293             o.weakRef();
294             _ptr = ptr;
295         }
296         if (_dd && !_dd.weakDef())
297             sharedRefAllocator.dispose(_dd);
298         _dd = o;
299         static if (!isSaticAlloc)
300             _alloc = alloc;
301     }
302 
303     ValueType _ptr; // 只为保留指针在栈中,如果指针是GC分配的内存,而ExternalRefCountData非GC的,则不用把非GC内存添加到GC的扫描列表中
304     Data _dd;
305     static if (!isSaticAlloc)
306         Alloc _alloc;
307     else
308         alias _alloc = Alloc.instance;
309 }
310 
311 
312 private:
313 static shared SharedFreeList!(Mallocator, chooseAtRuntime) sharedRefAllocator;
314 
315 abstract class ExternalRefCountData(Alloc, bool isShared) {
316     pragma(inline, true) final int strongDef() nothrow {
317         static if (isShared)
318             return atomicOp!("-=")(_strongref, 1);
319         else
320             return --_strongref;
321     }
322 
323     pragma(inline, true) final int strongRef() nothrow {
324         static if (isShared)
325             return atomicOp!("+=")(_strongref, 1);
326         else
327             return ++_strongref;
328     }
329 
330     pragma(inline, true) final int weakDef() nothrow {
331         static if (isShared)
332             return atomicOp!("-=")(_weakref, 1);
333         else
334             return --_weakref;
335     }
336 
337     pragma(inline, true) final int weakRef() nothrow {
338         static if (isShared)
339             return atomicOp!("+=")(_weakref, 1);
340         else
341             return ++_weakref;
342     }
343 
344     pragma(inline, true) final @property int weakref() nothrow {
345         static if (isShared)
346             return atomicLoad(_weakref);
347         else
348             return _weakref;
349     }
350 
351     pragma(inline, true) final @property int strongref() nothrow {
352         static if (isShared)
353             return atomicLoad(_strongref);
354         else
355             return _strongref;
356     }
357 
358     void free(ref Alloc alloc);
359 
360     static if (isShared) {
361         shared int _weakref = 1;
362         shared int _strongref = 1;
363     } else {
364         int _weakref = 1;
365         int _strongref = 1;
366     }
367 }
368 
369 final class ExternalRefCountDataWithDeleter(Alloc, ValueType, bool isShared)
370     : ExternalRefCountData!(Alloc, isShared) {
371     //pragma(msg, "is  ahsred " ~ isShared.stringof);
372     alias Deleter = void function(ref Alloc, ValueType);
373 
374     this(ValueType ptr, Deleter dele) {
375         value = ptr;
376         deleater = dele;
377     }
378 
379     override void free(ref Alloc alloc) {
380         if (deleater && value)
381             deleater(alloc, value);
382         deleater = null;
383         value = null;
384     }
385 
386     Deleter deleater;
387     ValueType value;
388 }