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 }