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 }