-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatomic_smart_ptr.h
More file actions
2154 lines (2015 loc) · 102 KB
/
Copy pathatomic_smart_ptr.h
File metadata and controls
2154 lines (2015 loc) · 102 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/***************************************************************************
Copyright (C) 2002-2026 Kentaro Kitagawa
kitag@issp.u-tokyo.ac.jp
This file is dual-licensed under your choice of EITHER:
* Apache License, Version 2.0
(http://www.apache.org/licenses/LICENSE-2.0, or see
LICENSE-APACHE-2.0 in this directory)
-- OR --
* GNU General Public License, version 2 of the License,
or (at your option) any later version
(http://www.gnu.org/licenses/old-licenses/gpl-2.0.html,
or see LICENSE-GPL-2.0 in this directory).
Pick whichever license suits your project. Unless required
by applicable law or agreed to in writing, this file is
distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied
***************************************************************************/
#ifndef ATOMIC_SMART_PTR_H_
#define ATOMIC_SMART_PTR_H_
#include "atomic.h" // integral atomic<T> specialization + atomic_mfence.h
// (cyclic include — atomic.h's ATOMIC_H_ guard
// is set before it #include's us; the top half
// is fully expanded by then.)
#include <functional>
#include <utility>
#include <type_traits>
#include <cstdint> // uintptr_t — tagged-pointer local refcount (not transitive under libstdc++ 14+)
#include <assert.h>
//! Trait to disable load_shared_() for specific types at compile time.
//! To disable for type T, add `using load_shared_disabled_tag = void;` to T.
//! Detected via SFINAE — no template specialization required.
namespace detail_asp {
template <typename T, typename = void>
struct load_shared_enabled_impl : std::true_type {};
template <typename T>
struct load_shared_enabled_impl<T, typename std::conditional<true, void,
typename T::load_shared_disabled_tag>::type> : std::false_type {};
}
template <typename T>
struct load_shared_enabled : detail_asp::load_shared_enabled_impl<T> {};
#ifndef BACKOFF_IN_ATOMIC_SMART_PTR
//if defined as 0, backoff by pause4spin()[=__mm_spin/yield] will be completely killed.
//if > 0, 2^(retry) spin count will by divided by BACKOFF_IN_ATOMIC_SMART_PTR.
#define BACKOFF_IN_ATOMIC_SMART_PTR 0 //disabled by default, in accord with our tests for ARM64 and high-core-count x86_64.
#endif
//! \brief This is an atomic variant of \a std::unique_ptr.
//! An instance of atomic_unique_ptr can be shared among threads by the use of \a swap(\a _shared_target_).\n
//! Namely, it is destructive reading.
//! Use atomic_shared_ptr when the pointer is required to be shared among scopes and threads.\n
//! This implementation relies on an atomic-swap machine code, e.g. lock xchg, or std::atomic.
//! \sa atomic_shared_ptr, atomic_unique_ptr_test.cpp
template <typename T>
class atomic_unique_ptr {
typedef T* t_ptr;
public:
atomic_unique_ptr() noexcept : m_ptr(nullptr) {}
explicit atomic_unique_ptr(t_ptr t) noexcept : m_ptr(t) {}
~atomic_unique_ptr() {delete (t_ptr)m_ptr;}
void reset(t_ptr t = nullptr) noexcept {
t_ptr old = m_ptr.exchange(t);
delete old;
}
//! \param[in,out] x \p x is atomically swapped.
//! Nevertheless, this object is not atomically replaced.
//! That is, the object pointed by "this" must not be shared among threads.
void swap(atomic_unique_ptr &x) noexcept {
m_ptr = x.m_ptr.exchange(m_ptr);
}
bool operator!() const noexcept {return !(t_ptr)m_ptr;}
operator bool() const noexcept {return (t_ptr)m_ptr;}
//! This function lacks thread-safety.
T &operator*() const noexcept { assert((t_ptr)m_ptr); return (T &) *(t_ptr)m_ptr;}
//! This function lacks thread-safety.
t_ptr operator->() const noexcept { assert((t_ptr)m_ptr); return (t_ptr)m_ptr;}
//! This function lacks thread-safety.
t_ptr get() const noexcept { return (t_ptr )m_ptr;}
atomic_unique_ptr(const atomic_unique_ptr &) = delete;
atomic_unique_ptr& operator=(const atomic_unique_ptr &) = delete;
private:
atomic<t_ptr> m_ptr;
};
//! Common non-template base for weak-capable control blocks. Layout:
//! `[refcnt | weak_refcnt]` at the start of every gref that supports
//! `local_weak_ptr<T>`. Both `gref_<T>` (separate alloc, default) and
//! `gref_weakable_<T>` (emplaced) inherit from this, enabling
//! `local_weak_ptr<T>` to store a type-erased `gref_weak_base_*`
//! without needing T to be complete at class-definition time.
//=============================================================================
// §biased refcount — OPT-IN, PER-TYPE. ***Auditing the lock-free core? SKIP
// this whole section and every `if constexpr (is_biased_directpublish<T>::value)`
// one-liner below — they fold to NOTHING for every type that does not inherit the
// marker (i.e. ALL types today; nothing opts in), leaving the plain atomic path.***
//
// A type T that inherits `atomic_biased_directpublish` stores its strong refcount
// NEGATED while owner-private (born -1; copy/reset mutate it non-atomically via
// relaxed load+store), and PUBLISH negates it to +count. ~3.3× on owner-private
// churn (micro). CONTRACT — sound ONLY for a DIRECT-PUBLISH control block: one
// that is exclusively the value of an `atomic_shared_ptr<T>` slot and is NEVER
// transitively contained in another control block (a `local_shared_ptr<T>` member
// of some other CB). Marking a transitively-shared type (e.g. the STM's Packet /
// PacketList / Linkage) is UNSOUND (a reader copying the nested handle races the
// owner's non-atomic store). Verified by GenMC test 10; see
// [[project_biased_refcount_planA]]. No KAME type opts in (measured: no net win).
struct atomic_biased_directpublish {};
template<class T> struct is_biased_directpublish : std::is_base_of<atomic_biased_directpublish, T> {};
//! All biased refcount logic lives here so the hot paths stay a single skippable
//! `if constexpr` line each. `rc` is the control block's strong refcnt.
static inline void biased_born_(atomic<uintptr_t> &rc) noexcept { //!< fresh CB → private -1
rc.store((uintptr_t)(-(intptr_t)1), std::memory_order_relaxed);
}
static inline void biased_inc_(atomic<uintptr_t> &rc) noexcept { //!< copy: ++count
uintptr_t v = rc.load(std::memory_order_relaxed);
if((intptr_t)v < 0) rc.store(v - 1, std::memory_order_relaxed); //!< private: non-atomic
else rc.fetch_add(1, std::memory_order_relaxed); //!< shared: atomic
}
static inline bool biased_dec_is_dead_(atomic<uintptr_t> &rc) noexcept { //!< reset: --count → dead?
uintptr_t v = rc.load(std::memory_order_relaxed);
if((intptr_t)v < 0) { uintptr_t nv = v + 1; //!< private: branchless add+cbz
rc.store(nv, std::memory_order_relaxed); return nv == 0; }
return rc.decAndTest(); //!< shared: atomic acq_rel
}
static inline void biased_publish_(atomic<uintptr_t> &rc) noexcept { //!< publish: -count → +count
uintptr_t v = rc.load(std::memory_order_relaxed);
if((intptr_t)v < 0) rc.store((uintptr_t)(-(intptr_t)v), std::memory_order_release); //!< idempotent on +
}
static inline uintptr_t biased_count_(uintptr_t v) noexcept { //!< magnitude (use_count)
return (intptr_t)v < 0 ? (uintptr_t)(-(intptr_t)v) : v;
}
//=============================================================================
struct gref_weak_base_ {
typedef uintptr_t Refcnt;
atomic<Refcnt> refcnt;
atomic<Refcnt> weak_refcnt;
//! Try to promote weak → strong (`local_weak_ptr::lock()`). CAS
//! `refcnt` from N to N+1 iff N > 0. Returns true if the object is
//! still alive.
bool try_promote() noexcept {
Refcnt cur = refcnt.load(std::memory_order_relaxed);
for(;;) {
if(cur == 0) return false;
if(refcnt.compare_exchange_weak(cur, cur + 1,
std::memory_order_acq_rel, std::memory_order_relaxed))
return true;
}
}
protected:
gref_weak_base_() noexcept : refcnt(1), weak_refcnt(1) {}
};
//! Non-intrusive control block with weak support (DEFAULT mode): T
//! allocated separately, gref holds a pointer to it. Supports
//! `local_weak_ptr<T>`. Two-counter design: `refcnt` (strong refs) +
//! `weak_refcnt` (weak refs + 1 while strong > 0). Strong → 0
//! destroys T; control block lives until weak → 0.
//! \sa atomic_shared_ptr, local_weak_ptr
template <typename T>
struct atomic_shared_ptr_gref_ : gref_weak_base_ {
explicit atomic_shared_ptr_gref_(T *p) noexcept : ptr(p) {}
~atomic_shared_ptr_gref_() noexcept {
assert(refcnt == 0);
//!< Fast path in `release_strong_zero` deletes without decrementing
//!< when weak_refcnt == 1 (no live weak_ptr). Slow path leaves 0.
//!< Both are valid pre-delete states.
assert(weak_refcnt == 0 || weak_refcnt == 1);
//!< T already destroyed in release_strong_zero; ptr is nullptr.
}
//! The pointer to the object.
T *ptr;
typedef uintptr_t Refcnt;
static void release_strong_zero(atomic_shared_ptr_gref_ *p) noexcept {
//!< Destroy T first (separate allocation).
delete p->ptr;
p->ptr = nullptr;
//!< Drop the implicit +1 weak. Fast path: if weak_refcnt == 1
//!< no live `local_weak_ptr<T>` exists (the "1" IS the implicit),
//!< so skip the atomic fetch_sub.
if(p->weak_refcnt.load(std::memory_order_acquire) == 1) {
delete p;
}
else if(p->weak_refcnt.fetch_sub(1, std::memory_order_acq_rel) == 1) {
delete p;
}
}
static void release_weak_zero(atomic_shared_ptr_gref_ *p) noexcept {
delete p;
}
atomic_shared_ptr_gref_(const atomic_shared_ptr_gref_ &) = delete;
};
//! ===========================================================================
//! USAGE — picking a control-block mode for atomic_shared_ptr<T> /
//! local_shared_ptr<T>. You inherit an (optional) MARKER on T; ref_traits<T>
//! (further down) reads it and selects the layout. Nothing else to wire up.
//!
//! default (inherit nothing) : 2 allocations (T + control block), weak OK
//! struct T { ... };
//! local_shared_ptr<T> p(new T(...));
//!
//! : atomic_emplaced (== atomic_weakable) : 1 allocation (T embedded), weak OK
//! struct T : atomic_emplaced { ... };
//! auto p = make_local_shared<T>(args...);
//! // emplaced T MUST use make_local_shared(), NOT local_shared_ptr<T>(new T)
//!
//! : atomic_strictrefonly : 2 allocations, NO weak (saves 8 B + 1 atomic op)
//! struct T : atomic_strictrefonly { ... };
//! local_shared_ptr<T> p(new T(...));
//!
//! : atomic_countable : intrusive — refcnt lives INSIDE T, no separate
//! control block, fastest hot path, NO weak. Auto-detected when T is
//! complete at first use.
//! struct T : atomic_countable { ... };
//!
//! self-referential intrusive node (T embeds an atomic_shared_ptr<T> link, so
//! T is INCOMPLETE at first use -> marker auto-detection cannot see it):
//! opt in explicitly with `force_intrusive_ref<T>` (below), and give T the
//! intrusive contract -- `typedef ... Refcnt;` + `atomic<Refcnt> refcnt;`
//! (and, optionally, a `void atomic_intrusive_dispose() noexcept` method).
//!
//! The per-mode cost/feature MATRIX is the table just below (next to
//! `atomic_emplaced`). Worked examples: tests/atomic_intrusive_dispose_test.cpp
//! and tests/atomic_intrusive_chain_test.cpp (self-referential).
//! ===========================================================================
//! Opt-out marker: T uses strict reference counting only (no
//! `weak_refcnt`). Inherit from this to suppress `local_weak_ptr<T>`
//! support — saves 8 bytes per control block and one atomic op at
//! construction. Cannot be combined with `atomic_emplaced` or
//! `atomic_weakable` (the SFINAE branches are mutually exclusive).
struct atomic_strictrefonly {};
//! Non-intrusive control block WITHOUT weak support: T allocated
//! separately, gref holds a pointer. Used when `T : atomic_strictrefonly`.
//! \sa atomic_shared_ptr
template <typename T>
struct atomic_shared_ptr_gref_strictrefonly_ {
explicit atomic_shared_ptr_gref_strictrefonly_(T *p) noexcept : ptr(p), refcnt(1) {}
//!< Polymorphic T's dispatch via virtual destructor on T* (e.g.
//!< Payload : virtual ~Payload). Non-polymorphic T's are
//!< constructed only with Y == T (static_assert in
//!< local_shared_ptr).
~atomic_shared_ptr_gref_strictrefonly_() noexcept { assert(refcnt == 0); delete ptr; }
//! The pointer to the object.
T *ptr;
typedef uintptr_t Refcnt;
//! Reference counter.
atomic<Refcnt> refcnt;
atomic_shared_ptr_gref_strictrefonly_(const atomic_shared_ptr_gref_strictrefonly_ &) = delete;
};
//! Marker base classes (opt-in). Empty structs (or trivial — see
//! `atomic_countable` below) — empty base optimisation makes them
//! zero-cost in `sizeof(T)` when applicable.
//!
//! Choosing a mode:
//!
//! | Marker | Alloc | weak_ptr | get() cost | Use case |
//! |-----------------------|-------|----------|------------|-----------------|
//! | `atomic_countable` | 1× | no | branchless | hottest types |
//! | `atomic_emplaced` | 1× | yes | offset+null| weakable hot |
//! | `atomic_strictrefonly`| 2× | no | offset+null| small / cold |
//! | (none — default) | 2× | yes | offset+null| anything else |
//!
//! `atomic_weakable` is a back-compat alias for `atomic_emplaced`.
//!
//! All bookkeeping (`Ref` type, deleter, layout) is then driven from
//! `ref_traits<T>` — see below.
struct atomic_emplaced {};
struct atomic_weakable : atomic_emplaced {};
//! Intrusive refcnt base — T inherits and gets a built-in refcnt field.
//! sizeof(T) includes the refcnt; `local_shared_ptr<T>` stores T*
//! directly (no separate control block). Fastest hot path.
struct atomic_countable {
atomic_countable() noexcept : refcnt(1) {}
atomic_countable(const atomic_countable &) noexcept : refcnt(1) {}
~atomic_countable() { assert(refcnt == 0); }
atomic_countable &operator=(const atomic_countable &) = delete;
typedef uintptr_t Refcnt;
atomic<Refcnt> refcnt;
};
//! Non-intrusive control block, T embedded + weak refcount — single
//! allocation, supports `local_weak_ptr<T>`. Used when
//! `T : atomic_emplaced` (including `T : atomic_weakable`).
//! Two-counter design inherited from `gref_weak_base_`:
//! * `refcnt` — strong refs (local_shared_ptr / atomic_shared_ptr)
//! * `weak_refcnt` — weak refs + 1 while strong > 0 ("alive" sentinel)
//! Strong → 0 destroys T; control block lives until weak → 0.
template <typename T>
struct atomic_shared_ptr_gref_weakable_ : gref_weak_base_ {
typedef uintptr_t Refcnt;
alignas(T) unsigned char data_storage[sizeof(T)];
template <typename ...Args>
explicit atomic_shared_ptr_gref_weakable_(Args&&... args) {
new ( &data_storage) T(std::forward<Args>(args)...);
}
~atomic_shared_ptr_gref_weakable_() noexcept {
assert(refcnt == 0);
//!< Fast path in `release_strong_zero` deletes without decrementing
//!< when weak_refcnt == 1 (no live weak_ptr). Slow path leaves 0.
//!< Both are valid pre-delete states.
assert(weak_refcnt == 0 || weak_refcnt == 1);
}
T *ptr_() noexcept { return reinterpret_cast<T *>( &data_storage); }
const T *ptr_() const noexcept {
return reinterpret_cast<const T *>( &data_storage);
}
static void release_strong_zero(atomic_shared_ptr_gref_weakable_ *p) noexcept {
p->ptr_()->~T();
//!< Drop the implicit +1 weak. Fast path: if weak_refcnt == 1
//!< no live `local_weak_ptr<T>` exists (the "1" IS the implicit),
//!< so skip the atomic fetch_sub. Safe — no new weak_ptr can
//!< come into existence (no strong → no copy from shared; no
//!< existing weak → no copy from weak).
if(p->weak_refcnt.load(std::memory_order_acquire) == 1) {
delete p;
}
else if(p->weak_refcnt.fetch_sub(1, std::memory_order_acq_rel) == 1) {
delete p;
}
}
static void release_weak_zero(atomic_shared_ptr_gref_weakable_ *p) noexcept {
delete p;
}
//! try_promote() inherited from gref_weak_base_.
atomic_shared_ptr_gref_weakable_(const atomic_shared_ptr_gref_weakable_ &) = delete;
};
template <typename X, typename Y, typename Z, typename E> struct atomic_shared_ptr_base;
template <typename X> class atomic_shared_ptr;
template <typename X, typename Y> class local_shared_ptr;
template <typename X> class scoped_atomic_view;
template <typename X> class local_weak_ptr;
//! Compile-time mode selection for atomic_shared_ptr_base. Encodes
//! "which `Ref` does T use" and the boolean flags that govern the
//! deleter / get() / reset_unsafe paths. All other logic lives in
//! the single `atomic_shared_ptr_base` template below.
//!
//! Incomplete-T support: a self-referencing struct
//! (`struct N { local_shared_ptr<N> next; };`) instantiates
//! `local_shared_ptr<N>` BEFORE N is complete — at that point
//! `std::is_base_of<atomic_countable, N>` is a hard error rather
//! than `false`. So gate the marker-detection on `sizeof(T)` via
//! SFINAE: when T is incomplete, all three category flags default
//! to `false` and `Ref = atomic_shared_ptr_gref_<T>` (the default
//! two-alloc gref — its only T usage is `T *ptr;` which works on
//! incomplete T).
//!
//! Trade-off: self-referential types lose the intrusive/emplaced/
//! strict optimisations. Intrusive (`atomic_countable`) is naturally
//! incompatible via AUTO-detection — the `is_base_of` marker probe needs T
//! complete — but a SELF-REFERENTIAL intrusive type (one that embeds an
//! `atomic_shared_ptr<T>` link inside itself, e.g. a lock-free list/DLL node)
//! can OPT IN explicitly via `force_intrusive_ref<T>` (below), which is
//! consulted WITHOUT requiring T complete. `atomic_emplaced` just saves the
//! second allocation, so its pay-back is bounded. The first instantiation of
//! `ref_traits<T>` wins (template-instantiation caching is per-args), so any
//! other user of `local_shared_ptr<T>` in the same TU sees the same decision.
//! (§36b) Opt-in to force the INTRUSIVE control block for a type that the
//! auto-detection cannot see as intrusive because it is INCOMPLETE at the
//! point of first use — specifically a SELF-REFERENTIAL intrusive type holding
//! an `atomic_shared_ptr<T>` link inside itself. Specialise to `std::true_type`
//! (before the first use of `atomic_shared_ptr<T>` / `local_shared_ptr<T>`):
//!
//! template <...> struct force_intrusive_ref<MyNode<...>> : std::true_type {};
//!
//! The forced type then takes the intrusive `Ref = T` path (no separate control
//! block; disposal via `T::atomic_intrusive_dispose` if present, else `delete`),
//! and `local_weak_ptr<T>` is disabled (`has_weak == false`). T must supply the
//! intrusive contract — a `typedef ... Refcnt;` and an `atomic<Refcnt> refcnt;`
//! member — exactly as `atomic_countable` would; it need NOT inherit
//! `atomic_countable` (so it can avoid that base's `~assert(refcnt==0)` when the
//! type has non-refcount disposal paths of its own).
//!
//! This is a COMPILE-TIME trait-dispatch hook only; it does NOT touch the
//! lock-free refcount / CAS / tagged-local-ref SMR core (its GenMC/TLA
//! verification is unaffected).
template <typename T> struct force_intrusive_ref : std::false_type {};
//! (§36c) Opt-out of marker AUTO-detection for a NON-intrusive type that is
//! INCOMPLETE — or whose instantiation would be CIRCULAR — at the first use of
//! `local_shared_ptr<T>` / `atomic_shared_ptr<T>`. The `sizeof(T)` completeness
//! probe in `ref_traits_auto` instantiates `T`; for a plain incomplete class
//! (`struct N;`) that soft-fails and the incomplete fallback is chosen, but for
//! a not-yet-instantiated class TEMPLATE id (e.g. `Holder<A>` used while `A` is
//! still being defined, where `Holder<A>` transitively needs `A` complete) the
//! probe forces `Holder<A>`'s instantiation and the resulting error is a HARD
//! error, NOT soft SFINAE — so `local_shared_ptr<Holder<A>>` cannot be a member
//! of `A` the way `std::shared_ptr<Holder<A>>` can. Specialise this to
//! `std::true_type` (before that first use) to skip the probe and take the
//! default non-intrusive two-alloc `gref_<T>` control block — identical to the
//! auto-detected incomplete fallback, and the only `T` usage is `T *ptr;` which
//! is valid on an incomplete T. `local_weak_ptr<T>` stays available.
//!
//! template <class X> struct force_incomplete_ref<Holder<X>> : std::true_type {};
//!
//! Mutually exclusive with `force_intrusive_ref<T>` (intrusive wins). Pure
//! compile-time trait dispatch — does NOT touch the lock-free SMR core.
template <typename T> struct force_incomplete_ref : std::false_type {};
//! AUTO-detection traits (used only for NON-forced types). A `sizeof(T)`
//! completeness gate distinguishes the two: incomplete T → non-intrusive
//! gref_<T> fallback; complete T → marker-base (`is_base_of`) detection.
//! Kept SEPARATE from the dispatcher `ref_traits` below so that the
//! `sizeof(T)` probe is NEVER substituted for a force-intrusive type (which is
//! typically self-referential and INCOMPLETE at first use — a `sizeof` there is
//! a hard error, not soft SFINAE, because it happens mid-definition).
template <typename T, typename = void>
struct ref_traits_auto {
//! Incomplete T: default to plain non-intrusive gref_<T>.
static constexpr bool is_intrusive = false;
static constexpr bool is_emplaced = false;
static constexpr bool is_strict = false;
using Ref = atomic_shared_ptr_gref_<T>;
static constexpr bool has_weak = true;
};
template <typename T>
struct ref_traits_auto<T, std::void_t<decltype(sizeof(T))>> {
//! Complete T: full marker-base detection.
static constexpr bool is_intrusive
= std::is_base_of<atomic_countable, T>::value;
static constexpr bool is_emplaced
= std::is_base_of<atomic_emplaced, T>::value && !is_intrusive;
static constexpr bool is_strict
= std::is_base_of<atomic_strictrefonly, T>::value && !is_intrusive;
//!< intrusive → T itself ; emplaced → gref_weakable_<T> ;
//!< strict → gref_strictrefonly_<T> ; otherwise → gref_<T>.
using Ref = typename std::conditional<is_intrusive, T,
typename std::conditional<is_emplaced, atomic_shared_ptr_gref_weakable_<T>,
typename std::conditional<is_strict, atomic_shared_ptr_gref_strictrefonly_<T>,
atomic_shared_ptr_gref_<T>>::type>::type>::type;
//!< Whether `local_weak_ptr<T>` is allowed (gref_weak_base_ is in
//!< the Ref chain). Intrusive and strict opt out.
static constexpr bool has_weak = !is_intrusive && !is_strict;
};
//! Dispatcher. The `int Mode` second parameter — defaulted from the two
//! force traits (each needs only T's template-id, NOT a complete T) — picks:
//! 0 = AUTO-detection (`sizeof(T)` marker probe; requires T completable),
//! 1 = forced INTRUSIVE (`force_intrusive_ref<T>`; NO `sizeof` anywhere),
//! 2 = forced INCOMPLETE (`force_incomplete_ref<T>`; non-intrusive gref_<T>
//! fallback, NO `sizeof` — for circular/incomplete template-id members).
//! Intrusive wins if both are (mis)specialised.
template <typename T>
constexpr int ref_force_mode() noexcept {
return force_intrusive_ref<T>::value ? 1
: (force_incomplete_ref<T>::value ? 2 : 0);
}
template <typename T, int Mode = ref_force_mode<T>()>
struct ref_traits : ref_traits_auto<T> {};
//! (§36b) Forced-intrusive — `Ref = T` (no separate control block; disposal via
//! `T::atomic_intrusive_dispose` if present, else `delete`), `local_weak_ptr<T>`
//! disabled. Reached WITHOUT a `sizeof(T)` probe, so it is valid even when T is
//! incomplete (a self-referential intrusive list/DLL node).
//!
//! Provides `Refcnt` HERE (in the trait, which is complete) so that
//! `atomic_shared_ptr_base` can take the refcount type from the trait rather
//! than from `Ref::Refcnt`. For a self-referential intrusive class TEMPLATE,
//! `Ref::Refcnt` would force a qualified-name lookup that INSTANTIATES (and so
//! completes) the still-incomplete chunk specialisation → circular with its
//! own `atomic_shared_ptr<chunk>` member → a hard error on GCC (clang is
//! lenient). A concrete intrusive type escapes this because its `Refcnt` is
//! reachable via a complete base (e.g. `atomic_countable`) without instantiating
//! the type; a template specialisation has no such escape. All control blocks
//! in this header use `uintptr_t` for the count, so the trait fixes it here.
template <typename T>
struct ref_traits<T, 1> {
static constexpr bool is_intrusive = true;
static constexpr bool is_emplaced = false;
static constexpr bool is_strict = false;
using Ref = T;
using Refcnt = uintptr_t;
static constexpr bool has_weak = false;
};
//! (§36c) Forced-incomplete — default non-intrusive two-alloc `gref_<T>`
//! control block (its only T usage is `T *ptr;`, valid on incomplete T), NO
//! `sizeof(T)` probe. Mirrors the auto-detected incomplete fallback so a
//! circular/incomplete template-id can be a `local_shared_ptr<T>` member.
template <typename T>
struct ref_traits<T, 2> {
static constexpr bool is_intrusive = false;
static constexpr bool is_emplaced = false;
static constexpr bool is_strict = false;
using Ref = atomic_shared_ptr_gref_<T>;
static constexpr bool has_weak = true;
};
//! (§36b) Opt-in custom disposer for the INTRUSIVE mode (`atomic_countable`).
//! If `T` provides a static `T::atomic_intrusive_dispose(T*)`, the `deleter`
//! calls it — with the object still LIVE (so it can read members, e.g. a
//! region/chunk size) — INSTEAD of the heap `delete p` when the intrusive
//! refcnt reaches 0. This lets a placement-new'd pool-region object dispose
//! via its own teardown (run `~T()` + `deallocate_chunk`) rather than
//! `::operator delete`. It affects ONLY the terminal, single-threaded release
//! leaf (the unique last releaser) — it does NOT touch the lock-free
//! refcount / CAS / tagged-local-ref protocol, so the GenMC/TLA verification
//! of the concurrency core is unchanged.
template <typename T, typename = void>
struct has_intrusive_dispose : std::false_type {};
template <typename T>
struct has_intrusive_dispose<
T, std::void_t<decltype(T::atomic_intrusive_dispose(std::declval<T*>()))>>
: std::true_type {};
//! \brief Single base class for atomic_shared_ptr / local_shared_ptr.
//! Mode is driven by `ref_traits<T>`; all four paths
//! (default / strict / emplaced / intrusive) share this template.
//!
//! `LOCAL_REF_CAPACITY` (lower bits of the tagged pointer available for
//! the local refcount): equals the minimum alignment guaranteed for
//! `Ref` storage. `Ref` heap-allocated via `new` is `sizeof(intptr_t)`-
//! aligned; for intrusive, `Ref = T` and T has `atomic<uintptr_t> refcnt`
//! so alignment is at least `sizeof(double)`. Both give 3 usable bits
//! (max local refcount = 7) on 64-bit.
//!
//! Do NOT use `alignas` / `alignof` on Ref — see CLAUDE.md.
//!
//! `KAME_LOCAL_REF_CAPACITY_OVERRIDE`: stress-testing knob; force a
//! smaller capacity to simulate high-CPU contention without changing
//! actual allocator alignment.
template <typename T, typename reflocal_t, typename reflocal_var_t,
typename Enable = void>
struct atomic_shared_ptr_base {
protected:
using Traits = ref_traits<T>;
using Ref = typename Traits::Ref;
// Take `Refcnt` from the TRAIT when it provides one, else from `Ref`. The
// trait is always complete, whereas `Ref::Refcnt` on a self-referential
// intrusive class TEMPLATE forces the chunk's instantiation → circular with
// its own `atomic_shared_ptr<chunk>` member (hard error on GCC). See the
// forced-intrusive `ref_traits<T,true>` spec (which sets `Refcnt`).
template <typename TR, typename = void>
struct refcnt_of_ { using type = typename Ref::Refcnt; };
template <typename TR>
struct refcnt_of_<TR, std::void_t<typename TR::Refcnt>> {
using type = typename TR::Refcnt;
};
using Refcnt = typename refcnt_of_<Traits>::type;
static int deleter(Ref *p) noexcept {
if constexpr (Traits::is_intrusive) {
if constexpr (has_intrusive_dispose<T>::value) {
//!< (§36b) custom region disposer — object still LIVE so it
//!< can read its size etc., then run ~T() + deallocate_chunk.
T::atomic_intrusive_dispose(p);
} else {
//!< T's dtor runs (incl. ~atomic_countable's `assert(refcnt == 0)`).
delete p;
}
} else if constexpr (Traits::has_weak) {
//!< Two-counter release: destroy T, drop implicit weak.
Ref::release_strong_zero(p);
} else {
//!< strict mode: `~Ref` does `delete ptr`.
delete p;
}
return 1;
}
//! Adopt a freshly-allocated object. Emplaced types disallow this
//! path (use `make_local_shared<T>(args)`) — the static_assert fires
//! only if reset_unsafe is actually instantiated.
template<typename Y> void reset_unsafe(Y *y) noexcept(Traits::is_intrusive) {
static_assert( !Traits::is_emplaced,
"Emplaced T: use make_local_shared<T>(args), not local_shared_ptr<T>(T*)");
if constexpr (Traits::is_intrusive) {
m_ref = (reflocal_t)static_cast<T*>(y); //!< T's ctor set refcnt=1.
} else {
m_ref = (reflocal_t)new Ref(y); //!< Wrap y in a fresh CB.
}
if constexpr (is_biased_directpublish<T>::value) //!< §biased — skippable: born private (-1)
biased_born_(((Ref*)(reflocal_t)m_ref)->refcnt);
}
T *get() noexcept {
if constexpr (Traits::is_intrusive) {
//!< Branchless: `(T*)0` is `nullptr`, no offset (Ref IS T).
return (T*)(reflocal_t)this->m_ref;
} else if(this->m_ref) {
Ref *p = (Ref*)(reflocal_t)this->m_ref;
if constexpr (Traits::is_emplaced) return p->ptr_();
else return p->ptr;
}
return nullptr;
}
const T *get() const noexcept {
return const_cast<atomic_shared_ptr_base *>(this)->get();
}
int _use_count_() const noexcept {
uintptr_t v = ((const Ref*)(reflocal_t)this->m_ref)->refcnt;
if constexpr (is_biased_directpublish<T>::value) return (int)biased_count_(v); //!< §biased — skippable
return (int)v;
}
reflocal_var_t m_ref;
#ifdef KAME_LOCAL_REF_CAPACITY_OVERRIDE
enum {LOCAL_REF_CAPACITY = KAME_LOCAL_REF_CAPACITY_OVERRIDE};
#else
enum {LOCAL_REF_CAPACITY =
(Traits::is_intrusive ? sizeof(double) : sizeof(intptr_t))};
#endif
};
//! \brief This class provides non-reentrant interfaces for atomic_shared_ptr: operator->(), operator*() and so on.\n
//! Use this class in non-reentrant scopes instead of costly atomic_shared_ptr.
//! \sa atomic_shared_ptr, atomic_unique_ptr, atomic_shared_ptr_test.cpp.
template <typename T, typename reflocal_var_t = uintptr_t>
class local_shared_ptr : protected atomic_shared_ptr_base<T, uintptr_t, reflocal_var_t> {
public:
local_shared_ptr() noexcept { this->m_ref = (TaggedPtr)nullptr; }
template<typename Y> explicit local_shared_ptr(Y *y) {
// For non-polymorphic T, Y must equal T (otherwise the
// virtual-less destructor in the deleter path would slice).
// Polymorphic T's accept derived Y via virtual ~T.
static_assert(std::is_same<T, Y>::value || std::has_virtual_destructor<T>::value,
"local_shared_ptr<T>(Y*): T must be polymorphic when Y != T");
this->reset_unsafe(y);
}
explicit local_shared_ptr(const atomic_shared_ptr<T> &t) noexcept { this->m_ref = reinterpret_cast<TaggedPtr>(t.load_shared_()); }
template<typename Y> local_shared_ptr(const atomic_shared_ptr<Y> &y) {
static_assert(sizeof(static_cast<const T*>(y.get())), "");
this->m_ref = reinterpret_cast<TaggedPtr>(y.load_shared_());
}
inline local_shared_ptr(const local_shared_ptr<T, reflocal_var_t> &t) noexcept;
template<typename Y, typename Z> inline local_shared_ptr(const local_shared_ptr<Y, Z> &y) noexcept;
local_shared_ptr(local_shared_ptr<T, reflocal_var_t> &&t) noexcept {
this->m_ref = t.m_ref;
t.m_ref = (TaggedPtr)nullptr;
}
template<typename Y, typename Z> local_shared_ptr(local_shared_ptr<Y, Z> &&y) noexcept {
this->m_ref = y.m_ref;
y.m_ref = (TaggedPtr)nullptr;
}
inline ~local_shared_ptr();
local_shared_ptr &operator=(const local_shared_ptr &t) noexcept {
local_shared_ptr(t).swap( *this);
return *this;
}
template<typename Y, typename Z> local_shared_ptr &operator=(const local_shared_ptr<Y, Z> &y) noexcept {
local_shared_ptr(y).swap( *this);
return *this;
}
local_shared_ptr &operator=(local_shared_ptr &&t) noexcept {
t.swap( *this);
t.reset();
return *this;
}
template<typename Y, typename Z> local_shared_ptr &operator=(local_shared_ptr<Y, Z> &&y) noexcept {
//! Build a destination-typed temporary from \a y (move-converting
//! ctor, nulls \a y), then same-type swap — mirrors the copy
//! converting assignment above. A direct `y.swap(*this)` only
//! compiles for Y==T, but that case uses the non-template overload;
//! this enables qualification-converting moves such as
//! `local_shared_ptr<const T> = make_local_shared<T>(...)`.
local_shared_ptr(std::move(y)).swap( *this);
return *this;
}
//! \param[in] t The pointer held by this instance is replaced with that of \a t.
local_shared_ptr &operator=(const atomic_shared_ptr<T> &t) noexcept {
this->reset();
this->m_ref = reinterpret_cast<TaggedPtr>(t.load_shared_());
return *this;
}
//! \param[in] y The pointer held by this instance is replaced with that of \a y.
template<typename Y> local_shared_ptr &operator=(const atomic_shared_ptr<Y> &y) noexcept {
static_assert(sizeof(static_cast<const T*>(y.get())), "");
this->reset();
this->m_ref = reinterpret_cast<TaggedPtr>(y.load_shared_());
return *this;
}
//! \param[in,out] x \p The pointer held by \a x is swapped with that of this instance.
inline void swap(local_shared_ptr &x) noexcept;
//! \param[in,out] x \p The pointer held by \a x is atomically swapped with that of this instance.
void swap(atomic_shared_ptr<T> &x) noexcept;
//! The pointer held by this instance is reset to null pointer.
inline void reset() noexcept;
//! The pointer held by this instance is reset with a pointer \a y.
template<typename Y> void reset(Y *y) {
static_assert(std::is_same<T, Y>::value || std::has_virtual_destructor<T>::value,
"local_shared_ptr<T>::reset(Y*): T must be polymorphic when Y != T");
reset();
this->reset_unsafe(y);
}
//! Const-transparent (std::shared_ptr semantics): `get()`,
//! `operator*`, `operator->` always return non-const `T*` / `T&`
//! regardless of the constness of `*this`. The smart pointer's
//! constness applies to the pointer itself (cannot reset / swap),
//! not to the pointee. This matches what migrating code from
//! `std::shared_ptr<T>` expects.
T *get() const noexcept {
return const_cast<local_shared_ptr *>(this)->atomic_shared_ptr_base<T, uintptr_t, reflocal_var_t>::get();
}
T &operator*() const noexcept { assert( *this); return *get();}
T *operator->() const noexcept { assert( *this); return get();}
bool operator!() const noexcept {return !this->m_ref;}
operator bool() const noexcept {return this->m_ref;}
template<typename Y, typename Z> bool operator==(const local_shared_ptr<Y, Z> &x) const noexcept {
static_assert(sizeof(static_cast<const T*>(x.get())), "");
return (this->ref_ptr_() == (const Ref *)x.ref_ptr_());}
template<typename Y> bool operator==(const atomic_shared_ptr<Y> &x) const noexcept {
static_assert(sizeof(static_cast<const T*>(x.get())), "");
return (this->ref_ptr_() == (const Ref *)x.ref_ptr_());}
template<typename Y, typename Z> bool operator!=(const local_shared_ptr<Y, Z> &x) const noexcept {
static_assert(sizeof(static_cast<const T*>(x.get())), "");
return (this->ref_ptr_() != (const Ref *)x.ref_ptr_());}
template<typename Y> bool operator!=(const atomic_shared_ptr<Y> &x) const noexcept {
static_assert(sizeof(static_cast<const T*>(x.get())), "");
return (this->ref_ptr_() != (const Ref *)x.ref_ptr_());}
int use_count() const noexcept { return this->_use_count_();}
bool unique() const noexcept {return use_count() == 1;}
//!< Tag for the `local_weak_ptr::lock()`-internal ctor below.
struct adopt_promoted_t {};
//!< Adopt a Ref* with the strong refcnt already bumped (typically
//!< by a successful `try_promote()` from `local_weak_ptr::lock()`).
//!< Local count starts at 0; the global +1 already exists.
inline local_shared_ptr(adopt_promoted_t, typename atomic_shared_ptr_base<T, uintptr_t, reflocal_var_t>::Ref *r) noexcept {
this->m_ref = reinterpret_cast<TaggedPtr>(r);
}
protected:
template <typename Y, typename Z> friend class local_shared_ptr;
template <typename Y> friend class atomic_shared_ptr;
template <typename Y> friend class scoped_atomic_view; // operator local_shared_ptr<T>() needs m_ref
template <typename Y> friend class local_weak_ptr; // lock() / promote path
typedef typename atomic_shared_ptr_base<T, uintptr_t, reflocal_var_t>::Ref Ref;
typedef typename atomic_shared_ptr_base<T, uintptr_t, reflocal_var_t>::Refcnt Refcnt;
typedef uintptr_t TaggedPtr;
//! A pointer to global reference struct.
Ref* ref_ptr_() const noexcept {return (Ref *)(TaggedPtr)(this->m_ref);}
};
//! \brief Weak counterpart of `local_shared_ptr<T>`. Works with any
//! `T` whose control block carries a `weak_refcnt` — i.e., the default
//! mode (no marker) and `atomic_emplaced` / `atomic_weakable`.
//! NOT supported when `T : atomic_strictrefonly` (compile error at
//! point of use).
//!
//! Stores a type-erased `gref_weak_base_*` so the class can be
//! instantiated when `T` is only forward-declared (e.g.,
//! `PacketWrapper` holds `local_weak_ptr<Linkage>` before Linkage's
//! full definition). Method bodies that need T complete (`reset`,
//! `lock`) use `if constexpr` / SFINAE at instantiation time.
//!
//! `lock()` atomically promotes to `local_shared_ptr<T>` if `T` is
//! still alive.
template <typename T>
class local_weak_ptr {
public:
local_weak_ptr() noexcept : m_ref(nullptr) {}
//! Construct from a `local_shared_ptr<T>` — bumps weak_refcnt by 1.
//! T must be complete at this point (method body instantiation).
template <typename Z>
explicit local_weak_ptr(const local_shared_ptr<T, Z> &sp) noexcept
: m_ref(nullptr) {
static_assert(!std::is_base_of<atomic_strictrefonly, T>::value,
"local_weak_ptr<T>: T must not inherit from atomic_strictrefonly");
if(sp.m_ref) {
using Ref = typename local_shared_ptr<T, Z>::Ref;
m_ref = static_cast<gref_weak_base_ *>(
reinterpret_cast<Ref *>(static_cast<uintptr_t>(sp.m_ref)));
m_ref->weak_refcnt.fetch_add(1, std::memory_order_acq_rel);
//!< §biased — skippable: taking a weak handle (promotable cross-thread)
//!< is a publish point, so a biased CB is negated -count→+count here.
if constexpr (is_biased_directpublish<T>::value) biased_publish_(m_ref->refcnt);
}
}
//! Copy: straightforward global fetch_add (mirrors local_shared_ptr).
local_weak_ptr(const local_weak_ptr &o) noexcept : m_ref(o.m_ref) {
if(m_ref)
m_ref->weak_refcnt.fetch_add(1, std::memory_order_acq_rel);
}
local_weak_ptr(local_weak_ptr &&o) noexcept : m_ref(o.m_ref) { o.m_ref = nullptr; }
~local_weak_ptr() noexcept { reset(); }
local_weak_ptr &operator=(const local_weak_ptr &o) noexcept {
local_weak_ptr(o).swap( *this);
return *this;
}
template <typename Z>
local_weak_ptr &operator=(const local_shared_ptr<T, Z> &sp) noexcept {
local_weak_ptr(sp).swap( *this);
return *this;
}
local_weak_ptr &operator=(local_weak_ptr &&o) noexcept {
o.swap( *this);
o.reset();
return *this;
}
void swap(local_weak_ptr &o) noexcept {
gref_weak_base_ *t = m_ref; m_ref = o.m_ref; o.m_ref = t;
}
//! Drop weak ref; if last, free the CB.
//! T must be complete when instantiated (to select the right
//! derived Ref type for deletion).
void reset() noexcept {
if(m_ref) {
if(m_ref->weak_refcnt.fetch_sub(1, std::memory_order_acq_rel) == 1) {
//!< Select the correct derived gref type for delete.
//!< T is complete at this point (deferred template instantiation).
if constexpr (ref_traits<T>::is_emplaced) {
using Ref = atomic_shared_ptr_gref_weakable_<T>;
Ref::release_weak_zero(static_cast<Ref *>(m_ref));
}
else {
using Ref = atomic_shared_ptr_gref_<T>;
Ref::release_weak_zero(static_cast<Ref *>(m_ref));
}
}
m_ref = nullptr;
}
}
//! Control-block identity test against a LIVE \a sp WITHOUT a
//! weak->strong promotion — zero refcount traffic (lock() costs a
//! try_promote + a release RMW). This never dereferences the weak
//! side; it only compares control-block addresses. m_ref is a bare
//! CB pointer (the weak handle carries no local-count tag) and
//! sp.ref_ptr_() returns sp's masked CB pointer, so equality is
//! exact for any tag layout of \a sp.
//!
//! Correctness contract: the caller must pass a sp that is KNOWN
//! ALIVE (refcnt >= 1). A live CB's address is occupied, so a true
//! result means genuine identity — no freed-and-reused-CB ABA. A
//! dead-but-weakly-pinned CB on THIS side simply won't match a
//! distinct live CB, degrading to a normal miss. Returns false when
//! either side is empty (null != a live CB; matches only null==null).
template <typename Z>
bool same_control_block(const local_shared_ptr<T, Z> &sp) const noexcept {
return m_ref == static_cast<const gref_weak_base_ *>(sp.ref_ptr_());
}
//! Promote to `local_shared_ptr<T>`; returns empty when expired.
//! T must be complete when instantiated.
local_shared_ptr<T> lock() const noexcept {
if( !m_ref || !m_ref->try_promote()) return local_shared_ptr<T>();
//!< Downcast to the actual Ref type for local_shared_ptr's adopt ctor.
using Ref = typename local_shared_ptr<T>::Ref;
return local_shared_ptr<T>(
typename local_shared_ptr<T>::adopt_promoted_t{},
static_cast<Ref *>(m_ref));
}
bool expired() const noexcept {
return !m_ref || m_ref->refcnt.load(std::memory_order_acquire) == 0;
}
bool operator!() const noexcept {return !m_ref;}
explicit operator bool() const noexcept {return m_ref;}
private:
gref_weak_base_ *m_ref;
};
/*! \brief This is an atomic variant of \a std::shared_ptr, and can be shared by atomic and lock-free means.\n
*
* \a atomic_shared_ptr can be shared among threads by the use of \a operator=(_target_), \a swap(_target_).
* An instance of \a atomic_shared_ptr<T> holds:\n
* a) a pointer to \a atomic_shared_ptr_gref_<T>, which is a struct. consisting of a pointer to a T-type object and a global reference counter.\n
* b) a local (temporary) reference counter, which is embedded in the above pointer by using several LSBs that should be usually zero.\n
* The values of a) and b), \a m_ref, are atomically handled with CAS machine codes.
* The purpose of b) the local reference counter is to tell the "observation" to the shared target before increasing the global reference counter.
* This process is implemented in \a acquire_tag_ref_().\n
* A function \a release_tag_ref_() tries to decrease the local counter first. When it fails, the global counter is decreased.\n
* To swap the pointer and local reference counter (which will be reset to zero), the setter must adds the local counting to the global counter before swapping.
* \sa atomic_unique_ptr, local_shared_ptr, atomic_shared_ptr_test.cpp.
*/
template <typename T>
class atomic_shared_ptr : protected local_shared_ptr<T, atomic<uintptr_t>> {
public:
atomic_shared_ptr() noexcept : local_shared_ptr<T, atomic<uintptr_t>>() {}
template<typename Y> explicit atomic_shared_ptr(Y *y) : local_shared_ptr<T, atomic<uintptr_t>>(y) { publish_clear_priv_(); }
atomic_shared_ptr(const atomic_shared_ptr<T> &t) noexcept : local_shared_ptr<T, atomic<uintptr_t>>(t) {} //!< source already shared (PRIV clear)
template<typename Y> atomic_shared_ptr(const atomic_shared_ptr<Y> &y) noexcept : local_shared_ptr<T, atomic<uintptr_t>>(y) {} //!< source already shared
atomic_shared_ptr(const local_shared_ptr<T> &t) noexcept : local_shared_ptr<T, atomic<uintptr_t>>(t) { publish_clear_priv_(); }
template<typename Y> atomic_shared_ptr(const local_shared_ptr<Y> &y) noexcept : local_shared_ptr<T, atomic<uintptr_t>>(y) { publish_clear_priv_(); }
atomic_shared_ptr(atomic_shared_ptr<T> &&t) noexcept {
operator=(std::move(t));
}
template<typename Y> atomic_shared_ptr(atomic_shared_ptr<Y> &&y) noexcept {
operator=(std::move(y));
}
~atomic_shared_ptr() {}
//! \param[in] t The pointer held by this instance is atomically replaced with that of \a t.
atomic_shared_ptr &operator=(const atomic_shared_ptr &t) noexcept {
local_shared_ptr<T>(t).swap( *this);
return *this;
}
//! \param[in] y The pointer held by this instance is atomically replaced with that of \a y.
template<typename Y> atomic_shared_ptr &operator=(const local_shared_ptr<Y> &y) noexcept {
local_shared_ptr<T>(y).swap( *this);
return *this;
}
atomic_shared_ptr &operator=(local_shared_ptr<T> &&t) noexcept {
t.swap( *this);
t.reset();
return *this;
}
template<typename Y> atomic_shared_ptr &operator=(local_shared_ptr<Y> &&y) noexcept {
//! Mirror the copy converting assignment above: build a
//! local_shared_ptr<T> from \a y (move-converting ctor, nulls \a y)
//! then atomic-swap. A direct `y.swap(*this)` only binds for Y==T
//! (handled by the non-template overload), so this enables
//! qualification-converting moves like atomic_shared_ptr<const T> = make_local_shared<T>().
local_shared_ptr<T>(std::move(y)).swap( *this);
return *this;
}
//! The pointer held by this instance is atomically reset to null pointer.
void reset() noexcept {
local_shared_ptr<T>().swap( *this);
}
//! The pointer held by this instance is atomically reset with a pointer \a y.
template<typename Y> void reset(Y *y) {
local_shared_ptr<T>(y).swap( *this);
}
//! \return true if succeeded.
//! \sa compareAndSwap()
bool compareAndSet(const local_shared_ptr<T> &oldvalue, const local_shared_ptr<T> &newvalue) noexcept;
//! \return true if succeeded.
//! \sa compareAndSet()
bool compareAndSwap(local_shared_ptr<T> &oldvalue, const local_shared_ptr<T> &newvalue) noexcept;
//! \return true if succeeded.
//! \sa compareAndSet()
bool compareAndSetWeak(const local_shared_ptr<T> &oldvalue, const local_shared_ptr<T> &newvalue) noexcept;
//! \return true if succeeded.
//! \brief Weakly version using a pre-acquired \a scoped_atomic_view.
//! On success, \a scoped is reset to Empty (tag consumed by CAS). On
//! weak failure (CAS contention), \a scoped remains TagHeld for retry.
//! On pointer change since acquire, \a scoped is eagerly cleaned up
//! to Empty so the caller can detect it via \a scoped.operator bool().
inline bool compareAndSetWeak(scoped_atomic_view<T> &scoped, const local_shared_ptr<T> &newvalue) noexcept;
//! \brief Like compareAndSetWeak(scoped, newr) but on success \a scoped
//! transitions to Owned(newr) instead of Empty. Saves a reload when
//! the caller needs to keep tracking the new value after CAS success.
//! Entry does fetch_add(2) instead of (1); failure undo is fetch_sub(2).
inline bool compareAndSetWeakRetain(scoped_atomic_view<T> &scoped, const local_shared_ptr<T> &newvalue) noexcept;
//! \brief STRONG (spinning) version of compareAndSetWeak(scoped, newr).