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