TLA Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 : // Copyright (c) 2026 Steve Gerbino
4 : //
5 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 : //
8 : // Official repository: https://github.com/cppalliance/corosio
9 : //
10 :
11 : #ifndef BOOST_COROSIO_DETAIL_OBJECT_REF_HPP
12 : #define BOOST_COROSIO_DETAIL_OBJECT_REF_HPP
13 :
14 : #include <boost/corosio/detail/platform.hpp>
15 : #include <boost/corosio/io/io_object.hpp>
16 : #include <atomic>
17 : #include <cstddef>
18 : #include <limits>
19 : #include <utility>
20 :
21 : namespace boost::corosio::detail {
22 :
23 : /// Add one reference to `impl`. `impl` must be non-null.
24 : inline void
25 HIT 143295 : acquire(io_object::implementation* impl) noexcept
26 : {
27 143295 : impl->refs_.fetch_add(1, std::memory_order_relaxed);
28 143295 : }
29 :
30 : /** The count an `object_pool` stamps on an impl parked for reuse.
31 :
32 : It holds no reference; see `object_pool`.
33 : */
34 : inline constexpr std::size_t pooled_refs =
35 : (std::numeric_limits<std::size_t>::max)();
36 :
37 : /** Add one reference to `impl` unless it is retiring or parked.
38 :
39 : A count at zero means `retire()` is running or about to, and a
40 : count of `pooled_refs` means the impl waits in its pool for a new
41 : owner; neither may be revived.
42 :
43 : @return True if a reference was added.
44 : */
45 : inline bool
46 9996 : try_acquire(io_object::implementation* impl) noexcept
47 : {
48 9996 : auto n = impl->refs_.load(std::memory_order_relaxed);
49 9999 : while (n != 0 && n != pooled_refs)
50 : {
51 19990 : if (impl->refs_.compare_exchange_weak(
52 : n, n + 1, std::memory_order_relaxed))
53 9992 : return true;
54 : }
55 4 : return false;
56 : }
57 :
58 : /** Drop one reference; runs `retire()` at zero. `impl` must be
59 : non-null.
60 :
61 : Uses release on the decrement and an acquire fence before the
62 : zero-action so writes made while holding a reference are visible
63 : to the recycler. TSan cannot model a standalone fence, so under
64 : it the decrement itself is acq_rel instead -- strictly stronger,
65 : and TSan's happens-before tracking models the exchange directly.
66 : */
67 : inline void
68 189381 : release(io_object::implementation* impl) noexcept
69 : {
70 : #if BOOST_COROSIO_TSAN
71 : if (impl->refs_.fetch_sub(1, std::memory_order_acq_rel) == 1)
72 : impl->retire();
73 : #else
74 378762 : if (impl->refs_.fetch_sub(1, std::memory_order_release) == 1)
75 : {
76 : std::atomic_thread_fence(std::memory_order_acquire);
77 36094 : impl->retire();
78 : }
79 : #endif
80 189381 : }
81 :
82 : /** Owning intrusive reference to an I/O implementation.
83 :
84 : Replaces `std::shared_ptr<void>` op keepalives: copy acquires,
85 : move steals, destruction releases. Kept deliberately minimal —
86 : one pointer, half the size of a shared_ptr, and no control block.
87 : */
88 : class object_ref
89 : {
90 : io_object::implementation* p_ = nullptr;
91 :
92 : public:
93 : /// Destroy the object_ref, releasing the held reference, if any.
94 595448 : ~object_ref() { reset(); }
95 :
96 : /// Construct an object_ref holding no reference.
97 90354 : object_ref() noexcept = default;
98 :
99 : /// Construct an object_ref that acquires a new reference to `p`.
100 140069 : explicit object_ref(io_object::implementation* p) noexcept : p_(p)
101 : {
102 140069 : if (p_)
103 140069 : acquire(p_);
104 140069 : }
105 :
106 : /** Create a reference to `p` unless its count already reached zero.
107 :
108 : The way to take a reference from a raw pointer: a context that
109 : holds none may find the count at zero, and must not revive it.
110 :
111 : @param p The implementation to reference.
112 :
113 : @return A reference to `p`, or an empty `object_ref` if `p` is
114 : retiring.
115 : */
116 795 : static object_ref try_from(io_object::implementation* p) noexcept
117 : {
118 795 : object_ref r;
119 795 : if (try_acquire(p))
120 793 : r.p_ = p;
121 795 : return r;
122 : }
123 :
124 : /// Construct a copy, acquiring another reference to `other`'s target.
125 3 : object_ref(object_ref const& other) noexcept : p_(other.p_)
126 : {
127 3 : if (p_)
128 3 : acquire(p_);
129 3 : }
130 :
131 : /// Construct by transferring the reference out of `other`.
132 310011 : object_ref(object_ref&& other) noexcept
133 310011 : : p_(std::exchange(other.p_, nullptr))
134 : {
135 310011 : }
136 :
137 : /// Release the held reference, if any, and acquire `other`'s target.
138 2 : object_ref& operator=(object_ref const& other) noexcept
139 : {
140 2 : object_ref(other).swap(*this);
141 2 : return *this;
142 : }
143 :
144 : /// Release the held reference, if any, and take ownership of `other`'s.
145 183905 : object_ref& operator=(object_ref&& other) noexcept
146 : {
147 183905 : object_ref(std::move(other)).swap(*this);
148 183905 : return *this;
149 : }
150 :
151 : /// Release the held reference, if any.
152 1092312 : void reset() noexcept
153 : {
154 1092312 : if (p_)
155 140865 : release(std::exchange(p_, nullptr));
156 1092312 : }
157 :
158 : /// Return the held implementation pointer.
159 4 : io_object::implementation* get() const noexcept { return p_; }
160 :
161 : /// Return true if a reference is held.
162 83210 : explicit operator bool() const noexcept { return p_ != nullptr; }
163 :
164 : /// Exchange the implementation pointers held by `*this` and `other`.
165 183907 : void swap(object_ref& other) noexcept { std::swap(p_, other.p_); }
166 : };
167 :
168 : } // namespace boost::corosio::detail
169 :
170 : #endif
|