include/boost/corosio/detail/object_ref.hpp
100.0% Lines (44 / 44)
81.2% Branches (13 / 16)
100.0% Functions (15 / 15)
Functions (15)
Function
Calls
Lines
Branches
Blocks
boost::corosio::detail::acquire(boost::corosio::io_object::implementation*)
:25
437164x
100.0%
–
100.0%
boost::corosio::detail::try_acquire(boost::corosio::io_object::implementation*)
:46
5151x
100.0%
83.3%
71.4%
boost::corosio::detail::release(boost::corosio::io_object::implementation*)
:68
460346x
100.0%
100.0%
100.0%
boost::corosio::detail::object_ref::~object_ref()
:94
1326983x
100.0%
–
100.0%
boost::corosio::detail::object_ref::object_ref()
:97
25609x
100.0%
–
100.0%
boost::corosio::detail::object_ref::object_ref(boost::corosio::io_object::implementation*)
:100
432606x
100.0%
50.0%
100.0%
boost::corosio::detail::object_ref::try_from(boost::corosio::io_object::implementation*)
:116
214x
100.0%
100.0%
100.0%
boost::corosio::detail::object_ref::object_ref(boost::corosio::detail::object_ref const&)
:125
2946x
100.0%
50.0%
100.0%
boost::corosio::detail::object_ref::object_ref(boost::corosio::detail::object_ref&&)
:132
865608x
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
432812x
100.0%
–
100.0%
boost::corosio::detail::object_ref::reset()
:152
1326987x
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
215x
100.0%
–
100.0%
boost::corosio::detail::object_ref::swap(boost::corosio::detail::object_ref&)
:165
432814x
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 | 437164x | acquire(io_object::implementation* impl) noexcept | ||
| 26 | { | |||
| 27 | 437164x | impl->refs_.fetch_add(1, std::memory_order_relaxed); | ||
| 28 | 437164x | } | ||
| 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 | 5151x | try_acquire(io_object::implementation* impl) noexcept | ||
| 47 | { | |||
| 48 | 5151x | auto n = impl->refs_.load(std::memory_order_relaxed); | ||
| 49 |
4/4✓ Branch 32 → 33 taken 5149 times.
✓ Branch 32 → 34 taken 2 times.
✓ Branch 33 → 18 taken 5147 times.
✓ Branch 33 → 34 taken 2 times.
|
5151x | while (n != 0 && n != pooled_refs) | |
| 50 | { | |||
| 51 |
1/2✓ Branch 30 → 31 taken 5147 times.
✗ Branch 30 → 32 not taken.
|
10294x | if (impl->refs_.compare_exchange_weak( | |
| 52 | n, n + 1, std::memory_order_relaxed)) | |||
| 53 | 5147x | return true; | ||
| 54 | } | |||
| 55 | 4x | 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 | 460346x | 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 4 → 5 taken 18033 times.
✓ Branch 4 → 7 taken 442313 times.
|
920692x | if (impl->refs_.fetch_sub(1, std::memory_order_release) == 1) | |
| 75 | { | |||
| 76 | std::atomic_thread_fence(std::memory_order_acquire); | |||
| 77 | 18033x | impl->retire(); | ||
| 78 | } | |||
| 79 | #endif | |||
| 80 | 460346x | } | ||
| 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 | 1326983x | ~object_ref() { reset(); } | ||
| 95 | ||||
| 96 | /// Construct an object_ref holding no reference. | |||
| 97 | 25609x | object_ref() noexcept = default; | ||
| 98 | ||||
| 99 | /// Construct an object_ref that acquires a new reference to `p`. | |||
| 100 | 432606x | explicit object_ref(io_object::implementation* p) noexcept : p_(p) | ||
| 101 | { | |||
| 102 |
1/2✓ Branch 2 → 3 taken 432606 times.
✗ Branch 2 → 4 not taken.
|
432606x | if (p_) | |
| 103 | 432606x | acquire(p_); | ||
| 104 | 432606x | } | ||
| 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 | 214x | static object_ref try_from(io_object::implementation* p) noexcept | ||
| 117 | { | |||
| 118 | 214x | object_ref r; | ||
| 119 |
2/2✓ Branch 3 → 4 taken 212 times.
✓ Branch 3 → 5 taken 2 times.
|
214x | if (try_acquire(p)) | |
| 120 | 212x | r.p_ = p; | ||
| 121 | 214x | return r; | ||
| 122 | } | |||
| 123 | ||||
| 124 | /// Construct a copy, acquiring another reference to `other`'s target. | |||
| 125 | 2946x | object_ref(object_ref const& other) noexcept : p_(other.p_) | ||
| 126 | { | |||
| 127 |
1/2✓ Branch 2 → 3 taken 2946 times.
✗ Branch 2 → 4 not taken.
|
2946x | if (p_) | |
| 128 | 2946x | acquire(p_); | ||
| 129 | 2946x | } | ||
| 130 | ||||
| 131 | /// Construct by transferring the reference out of `other`. | |||
| 132 | 865608x | object_ref(object_ref&& other) noexcept | ||
| 133 | 865608x | : p_(std::exchange(other.p_, nullptr)) | ||
| 134 | { | |||
| 135 | 865608x | } | ||
| 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 | 432812x | object_ref& operator=(object_ref&& other) noexcept | ||
| 146 | { | |||
| 147 | 432812x | object_ref(std::move(other)).swap(*this); | ||
| 148 | 432812x | return *this; | ||
| 149 | } | |||
| 150 | ||||
| 151 | /// Release the held reference, if any. | |||
| 152 | 1326987x | void reset() noexcept | ||
| 153 | { | |||
| 154 |
2/2✓ Branch 2 → 3 taken 435764 times.
✓ Branch 2 → 6 taken 891223 times.
|
1326987x | if (p_) | |
| 155 | 435764x | release(std::exchange(p_, nullptr)); | ||
| 156 | 1326987x | } | ||
| 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 | 215x | explicit operator bool() const noexcept { return p_ != nullptr; } | ||
| 163 | ||||
| 164 | /// Exchange the implementation pointers held by `*this` and `other`. | |||
| 165 | 432814x | void swap(object_ref& other) noexcept { std::swap(p_, other.p_); } | ||
| 166 | }; | |||
| 167 | ||||
| 168 | } // namespace boost::corosio::detail | |||
| 169 | ||||
| 170 | #endif | |||
| 171 |