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 }