1 /** 2 Shared Pointers 3 4 Copyright: 5 Copyright © 2023-2025, Kitsunebi Games 6 Copyright © 2023-2025, Inochi2D Project 7 8 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 9 Authors: Luna Nielsen 10 */ 11 module nulib.memory.shared_ptr; 12 import nulib.memory.weak_ptr; 13 import nulib.memory.internal; 14 import numem.core.traits; 15 import numem; 16 17 /** 18 A shared pointer 19 20 Shared pointers are a form of automatic reference counting. An internal 21 heap-allocated object stores a reference to the refcounted object. 22 23 You may borrow weak references from $(D shared_ptr), these weak references 24 may become invalid at $(I any) point, so make sure to check the state of the 25 object using $(D isValid). 26 27 Example: 28 --- 29 auto wp = shared_new!int(42); 30 if (wp.isValid) { 31 // Use the value. 32 } 33 --- 34 35 Threadsafety: 36 The internal reference count kept by shared_ptr is $(B not) atomic. 37 As such you should take care with accessing the value and refcount 38 stored within. 39 40 See_Also: 41 $(D nulib.memory.unique_ptr.unique_ptr) 42 $(D nulib.memory.weak_ptr.weak_ptr) 43 */ 44 struct shared_ptr(T) { 45 @nogc: 46 private: 47 __refcount_t!(T)* ptr = null; 48 49 public: 50 alias value this; 51 52 /** 53 The value stored within the smart pointer. 54 */ 55 @property ref T value() @trusted nothrow { 56 return ptr.get(); 57 } 58 59 /** 60 Whether the smart pointer value is still valid. 61 */ 62 @property bool isValid() @trusted nothrow { 63 return ptr && ptr.strongrefs > 0 && ptr.ptr !is null; 64 } 65 66 /// Destructor. 67 ~this() @trusted { 68 if (ptr) { 69 ptr.release!true; 70 ptr = null; 71 } 72 } 73 74 /** 75 Copy Constructor. 76 */ 77 this(ref inout(typeof(this)) src) { 78 auto srcobj = cast(Unqual!(typeof(this)))src; 79 80 this.ptr = srcobj.ptr; 81 this.ptr.retain!false; 82 } 83 84 /** 85 Constructs a shared_ptr with the given memory address. 86 */ 87 this(Ref!T ptr) @system { 88 this.ptr = __refcount_t!(T).createNew(ptr); 89 } 90 91 /** 92 Borrows a weak reference from the shared pointer. 93 94 Returns: 95 A new weak pointer, pointing to the same storage as 96 the shared pointer. 97 */ 98 weak_ptr!T borrow() @trusted { 99 return weak_ptr!T(ptr); 100 } 101 } 102 103 /** 104 Creates a new shared pointer. 105 106 Params: 107 args = The arguments to pass to $(D T)'s constructor. 108 109 Returns: 110 A shared pointer pointing to the newly allocated object. 111 */ 112 shared_ptr!T shared_new(T, Args...)(Args args) @trusted { 113 return shared_ptr!T(nogc_new!T(args)); 114 } 115 116 // Tests whether a shared pointer can be created. 117 @("shared_ptr: instantiation") 118 unittest { 119 static 120 class A { } 121 shared_ptr!A p = shared_new!A(); 122 assert(p); 123 } 124 125 @("shared_ptr: deletion") 126 unittest { 127 static bool dtest_v1; 128 static bool dtest_v2; 129 130 static 131 class DeletionTest { 132 ~this() { dtest_v1 = true; } 133 } 134 135 auto p = shared_new!DeletionTest(); 136 137 // Setup refcount debugging. 138 p.ptr.userdata = &dtest_v2; 139 p.ptr.onDeleted = (void* userdata) { 140 bool* udbool = cast(bool*)userdata; 141 *udbool = true; 142 }; 143 144 // Borrowed copy. 145 auto borrowed = p.borrow(); 146 147 148 // Delete old ref. 149 nogc_delete(p); 150 assert(dtest_v1); // ~this() was run. 151 152 nogc_delete(borrowed); 153 assert(dtest_v2); // __refcount_t was deleted. 154 }