Stroika Library 3.0d18
 
Loading...
Searching...
No Matches
SharedPtr.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include <mutex>
5
6#include "Stroika/Foundation/Execution/Common.h"
8#include "Stroika/Foundation/Execution/Throw.h"
10
11namespace Stroika::Foundation::Memory {
12 DISABLE_COMPILER_MSC_WARNING_START (4996);
13 DISABLE_COMPILER_GCC_WARNING_START ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
14 DISABLE_COMPILER_CLANG_WARNING_START ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
15
16 namespace Private_ {
17 // OK to declare this way because we cannot have threads before main, and since declared this way till be
18 // properly zero initialized
19 inline conditional_t<Execution::kSpinLock_IsFasterThan_mutex, Execution::SpinLock, mutex> sSharedPtrCopyLock_;
20 }
21
22 /*
23 ********************************************************************************
24 ************ Private_::ReferenceCounterContainerType_ **************************
25 ********************************************************************************
26 */
27 inline Private_::ReferenceCounterContainerType_::ReferenceCounterContainerType_ ()
28 : fCount{0}
29 , fDeleteCounter_{true}
30 {
31 }
32 inline Private_::ReferenceCounterContainerType_::ReferenceCounterContainerType_ (bool deleteCounter)
33 : fCount{0}
34 , fDeleteCounter_{deleteCounter}
35 {
36 }
37
38 /*
39 ********************************************************************************
40 *************************** Private_::Envelope_ ********************************
41 ********************************************************************************
42 */
43 namespace Private_ {
44 template <typename T>
45 class Envelope_ {
46 private:
47 T* fPtr_;
48 ReferenceCounterContainerType_* fCountHolder_;
49
50 public:
51 Envelope_ (T* ptr, ReferenceCounterContainerType_* countHolder)
52 : fPtr_ (ptr)
53 , fCountHolder_ (countHolder)
54 {
55 Require ((fPtr_ == nullptr) == (fCountHolder_ == nullptr));
56 }
57 inline Envelope_ (Envelope_&& from) noexcept
58 : fPtr_ (from.GetPtr ())
59 , fCountHolder_ (from.fCountHolder_)
60 {
61 from.fPtr_ = nullptr;
62 from.fCountHolder_ = nullptr;
63 }
64 template <typename T2>
65 inline Envelope_ (Envelope_<T2>&& from) noexcept
66 : fPtr_ (from.GetPtr ())
67 , fCountHolder_ (from.fCountHolder_)
68 {
69 from.fPtr_ = nullptr;
70 from.fCountHolder_ = nullptr;
71 }
72 inline Envelope_ (const Envelope_& from) noexcept
73 : fPtr_ (from.GetPtr ())
74 , fCountHolder_ (from.fCountHolder_)
75 {
76 }
77 template <typename T2>
78 inline Envelope_ (const Envelope_<T2>& from) noexcept
79 : fPtr_ (from.GetPtr ())
80 , fCountHolder_ (from.fCountHolder_)
81 {
82 }
83 template <typename T2>
84 inline Envelope_ (const Envelope_<T2>& from, T* newP) noexcept
85 : fPtr_ (newP)
86 , fCountHolder_ (from.fCountHolder_)
87 {
88 // reason for this is for dynamic cast. We allow replacing the P with a newP, but the
89 // actual ptr cannot change, and this assert check automatically converts pointers to
90 // a common base pointer type
91 Require (newP == from.GetPtr ());
92 }
93 inline Envelope_& operator= (const Envelope_& rhs)
94 {
95 fPtr_ = rhs.fPtr_;
96 fCountHolder_ = rhs.fCountHolder_;
97 return *this;
98 }
99 template <typename T2>
100 inline Envelope_& operator= (const Envelope_<T2>& rhs)
101 {
102 fPtr_ = rhs.fPtr_;
103 fCountHolder_ = rhs.fCountHolder_;
104 return *this;
105 }
106 inline Envelope_& operator= (Envelope_&& rhs) noexcept
107 {
108 fPtr_ = rhs.fPtr_;
109 fCountHolder_ = rhs.fCountHolder_;
110 rhs.fPtr_ = nullptr;
111 rhs.fCountHolder_ = nullptr;
112 return *this;
113 }
114 template <typename T2>
115 inline Envelope_& operator= (Envelope_<T2>&& rhs)
116 {
117 fPtr_ = rhs.fPtr_;
118 fCountHolder_ = rhs.fCountHolder_;
119 rhs.fPtr_ = nullptr;
120 rhs.fCountHolder_ = nullptr;
121 return *this;
122 }
123 inline void swap (Envelope_& rhs)
124 {
125 swap (fPtr_, rhs.fPtr_);
126 swap (fCountHolder_, rhs.fCountHolder_);
127 }
128 inline T* GetPtr () const noexcept
129 {
130 return fPtr_;
131 }
132 inline void SetPtr (T* p) noexcept
133 {
134 fPtr_ = p;
135 }
136 inline SharedPtrBase::ReferenceCountType CurrentRefCount () const noexcept
137 {
138 return fCountHolder_ == nullptr ? 0 : fCountHolder_->fCount.load ();
139 }
140 inline void Increment () noexcept
141 {
142 RequireNotNull (fCountHolder_);
143 ++fCountHolder_->fCount;
144 }
145 inline bool Decrement () noexcept
146 {
147 Require (CurrentRefCount () > 0);
148 if (--fCountHolder_->fCount == 0) {
149 return true;
150 }
151 return false;
152 }
153 inline void DoDeleteCounter () noexcept
154 {
155 RequireNotNull (fCountHolder_);
156 if (fCountHolder_->fDeleteCounter_) {
157 ManuallyBlockAllocated<ReferenceCounterContainerType_>::Delete (fCountHolder_);
158 }
159 fCountHolder_ = nullptr;
160 }
161#if qStroika_FeatureSupported_Valgrind
162 // See http://valgrind.org/docs/manual/hg-manual.html section 7.5
163 inline void ValgrindAnnotateGotLockZeroCaseClearing ()
164 {
165 RequireNotNull (fCountHolder_);
166 ANNOTATE_HAPPENS_AFTER (&fCountHolder_->fCount);
167 ANNOTATE_HAPPENS_BEFORE_FORGET_ALL (&fCountHolder_->fCount);
168 }
169 inline void ValgrindAnnotateNotGotZeroCase ()
170 {
171 RequireNotNull (fCountHolder_);
172 ANNOTATE_HAPPENS_BEFORE (&fCountHolder_->fCount);
173 }
174#endif
175 private:
176 template <typename T2>
177 friend class Envelope_;
178 };
179 }
180
181 /*
182 ********************************************************************************
183 ********************************** SharedPtr<T> ********************************
184 ********************************************************************************
185 */
186 template <typename T>
187 inline SharedPtr<T>::SharedPtr () noexcept
188 : fEnvelope_ (nullptr, nullptr)
189 {
190 }
191 template <typename T>
192 inline SharedPtr<T>::SharedPtr (nullptr_t) noexcept
193 : fEnvelope_ (nullptr, nullptr)
194 {
195 }
196 template <typename T>
197 template <typename T2, enable_if_t<is_convertible_v<T2*, T*>>*>
198 inline SharedPtr<T>::SharedPtr (T2* from)
199 : fEnvelope_ (mkEnvelope_ (from))
200 {
201 Assert (fEnvelope_.GetPtr () == from);
202 if (from != nullptr) {
203 // NB: the fEnvelope_.CurrentRefCount () USUALLY == 0, but not necessarily, if the refcount is stored
204 // in the 'from' - (see SharedPtrBase) - in which case the refcount might already be larger.
205 fEnvelope_.Increment ();
206 }
207 }
208 template <typename T>
209 inline SharedPtr<T>::SharedPtr (const Envelope_& from) noexcept
210 : fEnvelope_ (from)
211 {
212 if (fEnvelope_.GetPtr () != nullptr) {
213 fEnvelope_.Increment ();
214 }
215 }
216 template <typename T>
217 inline SharedPtr<T>::SharedPtr (const SharedPtr& from) noexcept
218 : fEnvelope_ (from.fEnvelope_)
219 {
220 if (fEnvelope_.GetPtr () != nullptr) {
221 fEnvelope_.Increment ();
222 }
223 }
224 template <typename T>
225 inline SharedPtr<T>::SharedPtr (SharedPtr&& from) noexcept
226 : fEnvelope_ (move (from.fEnvelope_))
227 {
228 Assert (from.fEnvelope_.GetPtr () == nullptr);
229 // no need to increment refcount here because the entire envelope moved from from to this, and so total counts same
230 }
231 template <typename T>
232 template <typename T2, enable_if_t<is_convertible_v<T2*, T*>>*>
233 SharedPtr<T>::SharedPtr (const SharedPtr<T2>& from) noexcept
234 : fEnvelope_ (from.fEnvelope_)
235 {
236 if (fEnvelope_.GetPtr () != nullptr) {
237 fEnvelope_.Increment ();
238 }
239 }
240 template <typename T>
241 template <typename T2, enable_if_t<is_convertible_v<T2*, T*>>*>
242 SharedPtr<T>::SharedPtr (SharedPtr<T2>&& from) noexcept
243 : fEnvelope_ (move (from.fEnvelope_))
244 {
245 Assert (from.fEnvelope_.GetPtr () == nullptr);
246 // no need to increment refcount here because the entire envelope moved from from to this, and so total counts same
247 }
248 template <typename T>
249 template <typename T2>
250 inline typename SharedPtr<T>::Envelope_
251 SharedPtr<T>::mkEnvelope_ (T2* from, enable_if_t<is_convertible_v<T2*, Private_::ReferenceCounterContainerType_*>>*)
252 {
253 if (from == nullptr) {
254 return Envelope_{nullptr, nullptr};
255 }
256 else {
257 return Envelope_{from, from};
258 }
259 }
260 template <typename T>
261 template <typename T2>
262 typename SharedPtr<T>::Envelope_ SharedPtr<T>::mkEnvelope_ (T2* from,
263 enable_if_t<!is_convertible_v<T2*, Private_::ReferenceCounterContainerType_*>>*)
264 {
265 return Envelope_{from, from == nullptr ? nullptr : ManuallyBlockAllocated<Private_::ReferenceCounterContainerType_>::New ()};
266 }
267 template <typename T>
268 SharedPtr<T>& SharedPtr<T>::operator= (const SharedPtr& rhs) noexcept
269 {
270 if (rhs.fEnvelope_.GetPtr () != fEnvelope_.GetPtr ()) {
271 if (fEnvelope_.GetPtr () != nullptr) {
272 if (fEnvelope_.Decrement ()) {
273#if qStroika_FeatureSupported_Valgrind
274 fEnvelope_.ValgrindAnnotateGotLockZeroCaseClearing ();
275#endif
276 fEnvelope_.DoDeleteCounter ();
277 delete fEnvelope_.GetPtr ();
278 fEnvelope_.SetPtr (nullptr);
279 }
280#if qStroika_FeatureSupported_Valgrind
281 else {
282 fEnvelope_.ValgrindAnnotateNotGotZeroCase ();
283 }
284#endif
285 }
286 fEnvelope_ = rhs.fEnvelope_;
287 if (fEnvelope_.GetPtr () != nullptr) {
288 Assert (fEnvelope_.CurrentRefCount () > 0);
289 fEnvelope_.Increment ();
290 }
291 }
292 return *this;
293 }
294 template <typename T>
295 SharedPtr<T>& SharedPtr<T>::operator= (SharedPtr&& rhs) noexcept
296 {
297 if (rhs.fEnvelope_.GetPtr () != fEnvelope_.GetPtr ()) {
298 if (fEnvelope_.GetPtr () != nullptr) {
299 if (fEnvelope_.Decrement ()) {
300#if qStroika_FeatureSupported_Valgrind
301 fEnvelope_.ValgrindAnnotateGotLockZeroCaseClearing ();
302#endif
303 fEnvelope_.DoDeleteCounter ();
304 delete fEnvelope_.GetPtr ();
305 fEnvelope_.SetPtr (nullptr);
306 }
307#if qStroika_FeatureSupported_Valgrind
308 else {
309 fEnvelope_.ValgrindAnnotateNotGotZeroCase ();
310 }
311#endif
312 }
313 fEnvelope_ = move (rhs.fEnvelope_); // no need to bump refcounts - moved from one to another
314 Assert (rhs.fEnvelope_.GetPtr () == nullptr); // NB: we ONLY gaurantee this if not self-moving
315 }
316 return *this;
317 }
318 template <typename T>
319 inline SharedPtr<T>::~SharedPtr ()
320 {
321 if (fEnvelope_.GetPtr () != nullptr) {
322 if (fEnvelope_.Decrement ()) {
323#if qStroika_FeatureSupported_Valgrind
324 fEnvelope_.ValgrindAnnotateGotLockZeroCaseClearing ();
325#endif
326 fEnvelope_.DoDeleteCounter ();
327 delete fEnvelope_.GetPtr ();
328 }
329#if qStroika_FeatureSupported_Valgrind
330 else {
331 fEnvelope_.ValgrindAnnotateNotGotZeroCase ();
332 }
333#endif
334 }
335 }
336 template <typename T>
337 inline bool SharedPtr<T>::IsNull () const noexcept
338 {
339 return fEnvelope_.GetPtr () == nullptr;
340 }
341 template <typename T>
342 inline T& SharedPtr<T>::GetRep () const noexcept
343 {
344 RequireNotNull (fEnvelope_.GetPtr ());
345 Assert (fEnvelope_.CurrentRefCount () > 0);
346 return *fEnvelope_.GetPtr ();
347 }
348 template <typename T>
349 inline T* SharedPtr<T>::operator->() const noexcept
350 {
351 return &GetRep ();
352 }
353 template <typename T>
354 inline T& SharedPtr<T>::operator* () const noexcept
355 {
356 return GetRep ();
357 }
358 template <typename T>
359 inline SharedPtr<T>::operator T* () const noexcept
360 {
361 return fEnvelope_.GetPtr ();
362 }
363 template <typename T>
364 inline T* SharedPtr<T>::get () const noexcept
365 {
366 return fEnvelope_.GetPtr ();
367 }
368 template <typename T>
369 inline void SharedPtr<T>::release () noexcept
370 {
371 if (fEnvelope_.GetPtr () != nullptr) {
372 if (fEnvelope_.Decrement ()) {
373#if qStroika_FeatureSupported_Valgrind
374 fEnvelope_.ValgrindAnnotateGotLockZeroCaseClearing ();
375#endif
376 fEnvelope_.DoDeleteCounter ();
377 delete fEnvelope_.GetPtr ();
378 fEnvelope_.SetPtr (nullptr);
379 }
380#if qStroika_FeatureSupported_Valgrind
381 else {
382 fEnvelope_.ValgrindAnnotateNotGotZeroCase ();
383 }
384#endif
385 }
386 }
387 template <typename T>
388 inline void SharedPtr<T>::clear () noexcept
389 {
390 release ();
391 }
392 template <typename T>
393 inline void SharedPtr<T>::reset (T* p)
394 {
395 if (fEnvelope_.GetPtr () != p) {
396 *this = SharedPtr<T> (p);
397 }
398 }
399 template <typename T>
400 template <typename T2>
401 inline SharedPtr<T2> SharedPtr<T>::Dynamic_Cast () const noexcept
402 {
403 return SharedPtr<T2> (typename SharedPtr<T2>::Envelope_ (fEnvelope_, dynamic_cast<T2*> (get ())));
404 }
405 template <typename T>
406 inline void SharedPtr<T>::swap (SharedPtr& rhs)
407 {
408 swap (fEnvelope_, rhs.fEnvelope_);
409 }
410 template <typename T>
411 inline typename SharedPtr<T>::ReferenceCountType SharedPtr<T>::CurrentRefCount () const noexcept
412 {
413 return fEnvelope_.CurrentRefCount ();
414 }
415 template <typename T>
416 inline typename SharedPtr<T>::ReferenceCountType SharedPtr<T>::use_count () const noexcept
417 {
418 return fEnvelope_.CurrentRefCount ();
419 }
420 template <typename T>
421 inline bool SharedPtr<T>::IsUnique () const noexcept
422 {
423 return fEnvelope_.CurrentRefCount () == 1;
424 }
425 template <typename T>
426 inline bool SharedPtr<T>::unique () const noexcept
427 {
428 // respect the stl-ish names
429 return IsUnique ();
430 }
431 template <typename T>
432 constexpr bool SharedPtr<T>::operator== (const SharedPtr& rhs) const
433 {
434 return fEnvelope_.GetPtr () == rhs.fEnvelope_.GetPtr ();
435 }
436 template <typename T>
437 constexpr bool SharedPtr<T>::operator== (nullptr_t) const
438 {
439 return fEnvelope_.GetPtr () == nullptr;
440 }
441 template <typename T>
442 constexpr strong_ordering SharedPtr<T>::operator<=> (const SharedPtr& rhs) const
443 {
444 return fEnvelope_.GetPtr () <=> rhs.fEnvelope_.GetPtr ();
445 }
446 template <typename T>
447 inline SharedPtr<T>::operator bool () const noexcept
448 {
449 return get () != nullptr;
450 }
451
452 /*
453 ********************************************************************************
454 ************************* enable_shared_from_this<T> ***************************
455 ********************************************************************************
456 */
457 template <typename T>
458 constexpr inline enable_shared_from_this<T>::enable_shared_from_this ()
459 : ReferenceCounterContainerType_ (false)
460 {
461 }
462 template <typename T>
463 constexpr inline enable_shared_from_this<T>::enable_shared_from_this (const enable_shared_from_this& /*src*/)
464 : ReferenceCounterContainerType_ (false)
465 {
466 }
467 template <typename T>
468 inline SharedPtr<T> enable_shared_from_this<T>::shared_from_this ()
469 {
470 /*
471 * The Constructor for SharedPtr<T> expects a T*. However, enable_shared_from_this doesn't
472 * own a T*. But recall, the ONLY legal way to use this enable_shared_from_this is:
473 *
474 * struct TTT : Memory::enable_shared_from_this<TTT> {
475 * string x;
476 * ....
477 * };
478 *
479 * So T must be a subclass of TTT, in this case (or equals T);
480 *
481 * So if we have a legal pointer to enable_shared_from_this<T>, then it MUST also be castable to a pointer to T*!!!
482 */
483 T* ptr = static_cast<T*> (this);
484 return (SharedPtr<T> (typename SharedPtr<T>::Envelope_ (ptr, this)));
485 }
486}
487
488namespace std {
489 template <typename TO_TYPE_T, typename FROM_TYPE_T>
490 inline Stroika::Foundation::Memory::SharedPtr<TO_TYPE_T> dynamic_pointer_cast (const Stroika::Foundation::Memory::SharedPtr<FROM_TYPE_T>& sp) noexcept
491 {
492 return sp.template Dynamic_Cast<TO_TYPE_T> ();
493 }
494 template <typename T>
495 inline Stroika::Foundation::Memory::SharedPtr<T> atomic_load_explicit (const Stroika::Foundation::Memory::SharedPtr<T>* copyFrom, memory_order)
496 {
497 using namespace Stroika::Foundation;
498 RequireNotNull (copyFrom);
499 [[maybe_unused]] auto&& critSec = lock_guard{Memory::Private_::sSharedPtrCopyLock_};
500 Stroika::Foundation::Memory::SharedPtr<T> result = *copyFrom;
501 return result;
502 }
503 template <typename T>
504 inline Stroika::Foundation::Memory::SharedPtr<T> atomic_load (const Stroika::Foundation::Memory::SharedPtr<T>* p)
505 {
506 RequireNotNull (p);
507 return atomic_load_explicit (p, memory_order_seq_cst);
508 }
509 template <typename T>
510 inline void atomic_store_explicit (Stroika::Foundation::Memory::SharedPtr<T>* storeTo, Stroika::Foundation::Memory::SharedPtr<T> o, memory_order)
511 {
512 using namespace Stroika::Foundation;
513 [[maybe_unused]] lock_guard critSec{Memory::Private_::sSharedPtrCopyLock_};
514 storeTo->swap (o);
515 }
516 template <typename T>
517 inline void atomic_store (Stroika::Foundation::Memory::SharedPtr<T>* storeTo, Stroika::Foundation::Memory::SharedPtr<T> o)
518 {
519 atomic_store_explicit (storeTo, o, memory_order_seq_cst);
520 }
521 DISABLE_COMPILER_MSC_WARNING_END (4996);
522 DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
523 DISABLE_COMPILER_CLANG_WARNING_END ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
524}
#define RequireNotNull(p)
Definition Assertions.h:347
STL namespace.