include/boost/corosio/detail/object_ref.hpp
100.0% Lines (53 / 53)
94.4% Branches (17 / 18)
100.0% Functions (15 / 15)
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 |