include/boost/corosio/detail/object_ref.hpp

100.0% Lines (53 / 53) 94.4% Branches (17 / 18) 100.0% Functions (15 / 15)
object_ref.hpp
f(x) Functions (15)
Function Calls Lines Branches Blocks
boost::corosio::detail::acquire(boost::corosio::io_object::implementation*) :25 410680x 100.0% – 100.0% boost::corosio::detail::try_acquire(boost::corosio::io_object::implementation*) :46 9882x 100.0% 100.0% 100.0% boost::corosio::detail::release(boost::corosio::io_object::implementation*) :68 471421x 100.0% 100.0% 100.0% boost::corosio::detail::object_ref::~object_ref() :94 3055520x 100.0% – 100.0% boost::corosio::detail::object_ref::object_ref() :97 465729x 100.0% – 100.0% boost::corosio::detail::object_ref::object_ref(boost::corosio::io_object::implementation*) :100 814899x 100.0% 100.0% 100.0% boost::corosio::detail::object_ref::try_from(boost::corosio::io_object::implementation*) :116 743x 100.0% 100.0% 80.0% boost::corosio::detail::object_ref::object_ref(boost::corosio::detail::object_ref const&) :125 6x 100.0% 50.0% 100.0% boost::corosio::detail::object_ref::object_ref(boost::corosio::detail::object_ref&&) :132 1775180x 100.0% – 100.0% boost::corosio::detail::object_ref::operator=(boost::corosio::detail::object_ref const&) :138 2x 100.0% – 100.0% boost::corosio::detail::object_ref::operator=(boost::corosio::detail::object_ref&&) :145 507224x 100.0% – 100.0% boost::corosio::detail::object_ref::reset() :152 2951612x 100.0% 100.0% 100.0% boost::corosio::detail::object_ref::get() const :159 4x 100.0% – 100.0% boost::corosio::detail::object_ref::operator bool() const :162 124373x 100.0% – 100.0% boost::corosio::detail::object_ref::swap(boost::corosio::detail::object_ref&) :165 507226x 100.0% – 100.0%
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco ([email protected])
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 410680x acquire(io_object::implementation* impl) noexcept
26 {
27 410680x impl->refs_.fetch_add(1, std::memory_order_relaxed);
28 410680x }
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 9882x try_acquire(io_object::implementation* impl) noexcept
47 {
48 9882x auto n = impl->refs_.load(std::memory_order_relaxed);
49
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 9882 times.
✓ Branch 2 taken 9880 times.
✓ Branch 3 taken 4 times.
9884x while (n != 0 && n != pooled_refs)
50 {
51
4/4
✓ Branch 0 taken 9878 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 9878 times.
✓ Branch 3 taken 2 times.
19760x if (impl->refs_.compare_exchange_weak(
52 9880x n, n + 1, std::memory_order_relaxed))
53 9878x return true;
54 }
55 4x return false;
56 9882x }
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 471421x 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
2/2
✓ Branch 0 taken 420550 times.
✓ Branch 1 taken 50871 times.
471421x if (impl->refs_.fetch_sub(1, std::memory_order_release) == 1)
75 {
76 50871x std::atomic_thread_fence(std::memory_order_acquire);
77 50871x impl->retire();
78 50871x }
79 #endif
80 471421x }
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 232863x io_object::implementation* p_ = nullptr;
91
92 public:
93 /// Destroy the object_ref, releasing the held reference, if any.
94 3055520x ~object_ref() { reset(); }
95
96 /// Construct an object_ref holding no reference.
97 698592x object_ref() noexcept = default;
98
99 /// Construct an object_ref that acquires a new reference to `p`.
100 814899x explicit object_ref(io_object::implementation* p) noexcept : p_(p)
101 407453x {
102
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 407450 times.
407446x if (p_)
103 407450x acquire(p_);
104 814907x }
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 743x static object_ref try_from(io_object::implementation* p) noexcept
117 {
118 743x object_ref r;
119
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 741 times.
743x if (try_acquire(p))
120 741x r.p_ = p;
121 743x return r;
122 743x }
123
124 /// Construct a copy, acquiring another reference to `other`'s target.
125 6x object_ref(object_ref const& other) noexcept : p_(other.p_)
126 3x {
127
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3x if (p_)
128 3x acquire(p_);
129 6x }
130
131 /// Construct by transferring the reference out of `other`.
132 1775180x object_ref(object_ref&& other) noexcept
133 887595x : p_(std::exchange(other.p_, nullptr))
134 887585x {
135 1775180x }
136
137 /// Release the held reference, if any, and acquire `other`'s target.
138 2x object_ref& operator=(object_ref const& other) noexcept
139 {
140 2x object_ref(other).swap(*this);
141 2x return *this;
142 }
143
144 /// Release the held reference, if any, and take ownership of `other`'s.
145 507224x object_ref& operator=(object_ref&& other) noexcept
146 {
147 507224x object_ref(std::move(other)).swap(*this);
148 507224x return *this;
149 }
150
151 /// Release the held reference, if any.
152 2951612x void reset() noexcept
153 {
154
2/2
✓ Branch 0 taken 2543409 times.
✓ Branch 1 taken 408203 times.
2951612x if (p_)
155 408203x release(std::exchange(p_, nullptr));
156 2951612x }
157
158 /// Return the held implementation pointer.
159 4x io_object::implementation* get() const noexcept { return p_; }
160
161 /// Return true if a reference is held.
162 124373x explicit operator bool() const noexcept { return p_ != nullptr; }
163
164 /// Exchange the implementation pointers held by `*this` and `other`.
165 507226x void swap(object_ref& other) noexcept { std::swap(p_, other.p_); }
166 };
167
168 } // namespace boost::corosio::detail
169
170 #endif
171