1 /*
2  * Hunt - A refined core library for D programming language.
3  *
4  * Copyright (C) 2018-2019 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.util.WeakReference;
13 
14 /**
15  * This module implements weak references.
16  *
17  * Authors: Alex Rønne Petersen, w0rp
18  *
19  * Credit and thanks goes to Alex Rønne Petersen for implementing
20  * another weak reference type. This type is pretty much a fork of his
21  * code, some of it still surviving.
22  */
23 
24 import core.memory;
25 import core.atomic;
26 
27 private alias void delegate(Object) DEvent;
28 private extern (C) void rt_attachDisposeEvent(Object h, DEvent e);
29 private extern (C) void rt_detachDisposeEvent(Object h, DEvent e);
30 
31 private alias extern (C) void function(Object, DEvent) @system pure nothrow PureEventFunc;
32 
33 /**
34  * This class implements a weak reference wrapper for class T.
35  *
36  * A weak reference will not prevent the object from being collected
37  * in a garbage collection cycle. If and when the object is collected,
38  * the internal reference to object will become null.
39  *
40  * This weak reference wrapper is thread safe.
41  *
42  * Params:
43  *     T = The type of class.
44  *
45  * See_Also:
46  *      https://github.com/w0rp/dstruct/blob/master/source/dstruct/weak_reference.d
47  *      https://forum.dlang.org/post/kd03f2$26ju$1@digitalmars.com
48  */
49 class WeakReference(T) if (is(T == class) || is(T == interface)) {
50     private shared size_t _ptr;
51 
52     @trusted pure nothrow private void freeWrapper() {
53         if (_ptr == 0) {
54             // We already cleaned up, don't do it again.
55             return;
56         }
57 
58         // Detach the previously attached dispose event, it is done.
59         (cast(PureEventFunc)&rt_detachDisposeEvent)(cast(Object) cast(void*) _ptr, &disposed);
60 
61         // Set the invalid pointer to null so we know it's gone.
62         atomicStore(_ptr, cast(size_t) 0);
63     }
64 
65     private void disposed(Object) {
66         freeWrapper();
67     }
68 
69     /**
70      * Create a weak reference wrapper for a given object.
71      *
72      * Params:
73      *     object = The object to hold a reference to.
74      */
75     @trusted pure nothrow this(T object) {
76         if (object is null) {
77             // No work needs to be done for null.
78             return;
79         }
80 
81         // Set the pointer atomically in a size_t so it's not a valid pointer.
82         // Use cast(void**) to avoid opCast problems.
83         atomicStore(_ptr, cast(size_t) cast(void**) object);
84 
85         // Stop the GC from scanning inside this class.
86         // This will make the interior reference a weak reference.
87         GC.setAttr(cast(void*) this, GC.BlkAttr.NO_SCAN);
88 
89         // Call a special D runtime function for nulling our reference
90         // to the object when the object is destroyed.
91         (cast(PureEventFunc)&rt_attachDisposeEvent)(cast(Object) object, &disposed);
92     }
93 
94     @trusted pure nothrow ~this() {
95         freeWrapper();
96     }
97 
98     /**
99      * Return the referenced object held in this weak reference wrapper.
100      * If and when the object is collected, this function will return null.
101      *
102      * Returns: The referenced object.
103      */
104     @trusted pure nothrow T get() inout {
105         auto ptr = cast(void*) atomicLoad(_ptr);
106 
107         // Check if the object is still alive before we return it.
108         // It might be killed in another thread.
109         return GC.addrOf(ptr) ? cast(T) ptr : null;
110     }
111 
112     /**
113      * Params:
114      *     other = Another object.
115      *
116      * Returns: True the other object is a weak reference to the same object.
117      */
118     @safe pure nothrow override bool opEquals(Object other) {
119         if (other is this) {
120             return true;
121         }
122 
123         if (auto otherWeak = cast(WeakReference!T) other) {
124             return _ptr == otherWeak._ptr;
125         }
126 
127         return false;
128     }
129 
130     /// ditto
131     // @trusted pure nothrow bool opEquals(const(Object) other) {
132     //     return this.opEquals(cast(Object) other);
133     // }
134 }
135 
136 /**
137  * This is a convenience function for creating a new
138  * weak reference wrapper for a given object.
139  *
140  * Params:
141  *     object = The object to create a reference for.
142  *
143  * Returns: A new weak reference to the given object.
144  */
145 WeakReference!T weak(T)(T object) if (is(T == class)) {
146     return new WeakReference!T(object);
147 }
148 
149 // Test that the reference is held.
150 unittest {
151     class SomeType {
152     }
153 
154     SomeType x = new SomeType();
155     auto y = weak(x);
156 
157     assert(y.get() is x);
158 }
159 
160 // Test that the reference is removed when the object is destroyed.
161 unittest {
162     class SomeType {
163     }
164 
165     SomeType x = new SomeType();
166     auto y = weak(x);
167 
168     destroy(x);
169 
170     assert(y.get() is null);
171 }
172 
173 // Test equality based on the reference held in the wrappers.
174 // BUG: Reported defects -@zxp at 1/11/2019, 10:24:21 AM
175 // https://github.com/w0rp/dstruct/issues/2
176 // unittest {
177 //     class SomeType {
178 //     }
179 
180 //     SomeType x = new SomeType();
181 //     auto y = weak(x);
182 //     auto z = weak(x);
183 
184 //     // assert(y !is z);
185 //     // assert(y == z);
186 
187 // }
188 
189 // Test equality after nulling things.
190 // unittest {
191 //     class SomeType {
192 //     }
193 
194 //     SomeType x = new SomeType();
195 //     auto y = weak(x);
196 //     auto z = weak(x);
197 
198 //     destroy(x);
199 
200 //     assert(y !is z);
201 //     // BUG: Reported defects -@zxp at 1/7/2019, 4:59:31 PM
202 //     // 
203 //     // assert(y == z); // bug
204 // }
205 
206 // Test tail-const weak references.
207 unittest {
208     class SomeType {
209     }
210 
211     const x = new SomeType();
212     const y = new SomeType();
213 
214     auto z = weak(x);
215     z = weak(y);
216 }