include/boost/corosio/detail/object_ref.hpp
100.0% Lines (44 / 44)
100.0% Functions (15 / 15)
Functions (15)
Function
Calls
Lines
Blocks
boost::corosio::detail::acquire(boost::corosio::io_object::implementation*)
:25
143295x
100.0%
100.0%
boost::corosio::detail::try_acquire(boost::corosio::io_object::implementation*)
:46
9996x
100.0%
74.0%
boost::corosio::detail::release(boost::corosio::io_object::implementation*)
:68
189381x
100.0%
100.0%
boost::corosio::detail::object_ref::~object_ref()
:94
595448x
100.0%
100.0%
boost::corosio::detail::object_ref::object_ref()
:97
90354x
100.0%
100.0%
boost::corosio::detail::object_ref::object_ref(boost::corosio::io_object::implementation*)
:100
140069x
100.0%
100.0%
boost::corosio::detail::object_ref::try_from(boost::corosio::io_object::implementation*)
:116
795x
100.0%
100.0%
boost::corosio::detail::object_ref::object_ref(boost::corosio::detail::object_ref const&)
:125
3x
100.0%
100.0%
boost::corosio::detail::object_ref::object_ref(boost::corosio::detail::object_ref&&)
:132
310011x
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
183905x
100.0%
100.0%
boost::corosio::detail::object_ref::reset()
:152
1092312x
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
83210x
100.0%
100.0%
boost::corosio::detail::object_ref::swap(boost::corosio::detail::object_ref&)
:165
183907x
100.0%
100.0%
| Line | TLA | Hits | 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 | 143295x | acquire(io_object::implementation* impl) noexcept | |
| 26 | { | ||
| 27 | 143295x | impl->refs_.fetch_add(1, std::memory_order_relaxed); | |
| 28 | 143295x | } | |
| 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 | 9996x | try_acquire(io_object::implementation* impl) noexcept | |
| 47 | { | ||
| 48 | 9996x | auto n = impl->refs_.load(std::memory_order_relaxed); | |
| 49 | 9999x | while (n != 0 && n != pooled_refs) | |
| 50 | { | ||
| 51 | 19990x | if (impl->refs_.compare_exchange_weak( | |
| 52 | n, n + 1, std::memory_order_relaxed)) | ||
| 53 | 9992x | 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 | 189381x | 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 | 378762x | if (impl->refs_.fetch_sub(1, std::memory_order_release) == 1) | |
| 75 | { | ||
| 76 | std::atomic_thread_fence(std::memory_order_acquire); | ||
| 77 | 36094x | impl->retire(); | |
| 78 | } | ||
| 79 | #endif | ||
| 80 | 189381x | } | |
| 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 | 595448x | ~object_ref() { reset(); } | |
| 95 | |||
| 96 | /// Construct an object_ref holding no reference. | ||
| 97 | 90354x | object_ref() noexcept = default; | |
| 98 | |||
| 99 | /// Construct an object_ref that acquires a new reference to `p`. | ||
| 100 | 140069x | explicit object_ref(io_object::implementation* p) noexcept : p_(p) | |
| 101 | { | ||
| 102 | 140069x | if (p_) | |
| 103 | 140069x | acquire(p_); | |
| 104 | 140069x | } | |
| 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 | 795x | static object_ref try_from(io_object::implementation* p) noexcept | |
| 117 | { | ||
| 118 | 795x | object_ref r; | |
| 119 | 795x | if (try_acquire(p)) | |
| 120 | 793x | r.p_ = p; | |
| 121 | 795x | return r; | |
| 122 | } | ||
| 123 | |||
| 124 | /// Construct a copy, acquiring another reference to `other`'s target. | ||
| 125 | 3x | object_ref(object_ref const& other) noexcept : p_(other.p_) | |
| 126 | { | ||
| 127 | 3x | if (p_) | |
| 128 | 3x | acquire(p_); | |
| 129 | 3x | } | |
| 130 | |||
| 131 | /// Construct by transferring the reference out of `other`. | ||
| 132 | 310011x | object_ref(object_ref&& other) noexcept | |
| 133 | 310011x | : p_(std::exchange(other.p_, nullptr)) | |
| 134 | { | ||
| 135 | 310011x | } | |
| 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 | 183905x | object_ref& operator=(object_ref&& other) noexcept | |
| 146 | { | ||
| 147 | 183905x | object_ref(std::move(other)).swap(*this); | |
| 148 | 183905x | return *this; | |
| 149 | } | ||
| 150 | |||
| 151 | /// Release the held reference, if any. | ||
| 152 | 1092312x | void reset() noexcept | |
| 153 | { | ||
| 154 | 1092312x | if (p_) | |
| 155 | 140865x | release(std::exchange(p_, nullptr)); | |
| 156 | 1092312x | } | |
| 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 | 83210x | explicit operator bool() const noexcept { return p_ != nullptr; } | |
| 163 | |||
| 164 | /// Exchange the implementation pointers held by `*this` and `other`. | ||
| 165 | 183907x | void swap(object_ref& other) noexcept { std::swap(p_, other.p_); } | |
| 166 | }; | ||
| 167 | |||
| 168 | } // namespace boost::corosio::detail | ||
| 169 | |||
| 170 | #endif | ||
| 171 |