100.00% Lines (33/33)
100.00% Functions (13/13)
| TLA | Baseline | Branch | ||||||
|---|---|---|---|---|---|---|---|---|
| Line | Hits | Code | Line | Hits | 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 <utility> | ||||||
| 18 | + | |||||||
| 19 | + | namespace boost::corosio::detail { | ||||||
| 20 | + | |||||||
| 21 | + | /// Add one reference to `impl`. `impl` must be non-null. | ||||||
| 22 | + | inline void | ||||||
| HITGNC | 23 | + | 105679 | acquire(io_object::implementation* impl) noexcept | ||||
| 24 | + | { | ||||||
| HITGNC | 25 | + | 105679 | impl->refs_.fetch_add(1, std::memory_order_relaxed); | ||||
| HITGNC | 26 | + | 105679 | } | ||||
| 27 | + | |||||||
| 28 | + | /** Drop one reference; runs `retire()` at zero. `impl` must be | ||||||
| 29 | + | non-null. | ||||||
| 30 | + | |||||||
| 31 | + | Uses release on the decrement and an acquire fence before the | ||||||
| 32 | + | zero-action so writes made while holding a reference are visible | ||||||
| 33 | + | to the recycler. TSan cannot model a standalone fence, so under | ||||||
| 34 | + | it the decrement itself is acq_rel instead -- strictly stronger, | ||||||
| 35 | + | and TSan's happens-before tracking models the exchange directly. | ||||||
| 36 | + | */ | ||||||
| 37 | + | inline void | ||||||
| HITGNC | 38 | + | 126004 | release(io_object::implementation* impl) noexcept | ||||
| 39 | + | { | ||||||
| 40 | + | #if BOOST_COROSIO_TSAN | ||||||
| 41 | + | if (impl->refs_.fetch_sub(1, std::memory_order_acq_rel) == 1) | ||||||
| 42 | + | impl->retire(); | ||||||
| 43 | + | #else | ||||||
| HITGNC | 44 | + | 252008 | if (impl->refs_.fetch_sub(1, std::memory_order_release) == 1) | ||||
| 45 | + | { | ||||||
| 46 | + | std::atomic_thread_fence(std::memory_order_acquire); | ||||||
| HITGNC | 47 | + | 20325 | impl->retire(); | ||||
| 48 | + | } | ||||||
| 49 | + | #endif | ||||||
| HITGNC | 50 | + | 126004 | } | ||||
| 51 | + | |||||||
| 52 | + | /** Owning intrusive reference to an I/O implementation. | ||||||
| 53 | + | |||||||
| 54 | + | Replaces `std::shared_ptr<void>` op keepalives: copy acquires, | ||||||
| 55 | + | move steals, destruction releases. Kept deliberately minimal — | ||||||
| 56 | + | two words smaller than a shared_ptr and no control block. | ||||||
| 57 | + | */ | ||||||
| 58 | + | class object_ref | ||||||
| 59 | + | { | ||||||
| 60 | + | io_object::implementation* p_ = nullptr; | ||||||
| 61 | + | |||||||
| 62 | + | public: | ||||||
| 63 | + | /// Destroy the object_ref, releasing the held reference, if any. | ||||||
| HITGNC | 64 | + | 950945 | ~object_ref() { reset(); } | ||||
| 65 | + | |||||||
| 66 | + | /// Construct an object_ref holding no reference. | ||||||
| HITGNC | 67 | + | 30938 | object_ref() noexcept = default; | ||||
| 68 | + | |||||||
| 69 | + | /// Construct an object_ref that acquires a new reference to `p`. | ||||||
| HITGNC | 70 | + | 105659 | explicit object_ref(io_object::implementation* p) noexcept : p_(p) | ||||
| 71 | + | { | ||||||
| HITGNC | 72 | + | 105659 | if (p_) | ||||
| HITGNC | 73 | + | 105659 | acquire(p_); | ||||
| HITGNC | 74 | + | 105659 | } | ||||
| 75 | + | |||||||
| 76 | + | /// Construct a copy, acquiring another reference to `other`'s target. | ||||||
| HITGNC | 77 | + | 3 | object_ref(object_ref const& other) noexcept : p_(other.p_) | ||||
| 78 | + | { | ||||||
| HITGNC | 79 | + | 3 | if (p_) | ||||
| HITGNC | 80 | + | 3 | acquire(p_); | ||||
| HITGNC | 81 | + | 3 | } | ||||
| 82 | + | |||||||
| 83 | + | /// Construct by transferring the reference out of `other`. | ||||||
| HITGNC | 84 | + | 511944 | object_ref(object_ref&& other) noexcept | ||||
| HITGNC | 85 | + | 511944 | : p_(std::exchange(other.p_, nullptr)) | ||||
| 86 | + | { | ||||||
| HITGNC | 87 | + | 511944 | } | ||||
| 88 | + | |||||||
| 89 | + | /// Release the held reference, if any, and acquire `other`'s target. | ||||||
| HITGNC | 90 | + | 2 | object_ref& operator=(object_ref const& other) noexcept | ||||
| 91 | + | { | ||||||
| HITGNC | 92 | + | 2 | object_ref(other).swap(*this); | ||||
| HITGNC | 93 | + | 2 | return *this; | ||||
| 94 | + | } | ||||||
| 95 | + | |||||||
| 96 | + | /// Release the held reference, if any, and take ownership of `other`'s. | ||||||
| HITGNC | 97 | + | 408367 | object_ref& operator=(object_ref&& other) noexcept | ||||
| 98 | + | { | ||||||
| HITGNC | 99 | + | 408367 | object_ref(std::move(other)).swap(*this); | ||||
| HITGNC | 100 | + | 408367 | return *this; | ||||
| 101 | + | } | ||||||
| 102 | + | |||||||
| 103 | + | /// Release the held reference, if any. | ||||||
| HITGNC | 104 | + | 1431925 | void reset() noexcept | ||||
| 105 | + | { | ||||||
| HITGNC | 106 | + | 1431925 | if (p_) | ||||
| HITGNC | 107 | + | 105662 | release(std::exchange(p_, nullptr)); | ||||
| HITGNC | 108 | + | 1431925 | } | ||||
| 109 | + | |||||||
| 110 | + | /// Return the held implementation pointer. | ||||||
| HITGNC | 111 | + | 3 | io_object::implementation* get() const noexcept { return p_; } | ||||
| 112 | + | |||||||
| 113 | + | /// Return true if a reference is held. | ||||||
| HITGNC | 114 | + | 13452 | explicit operator bool() const noexcept { return p_ != nullptr; } | ||||
| 115 | + | |||||||
| 116 | + | /// Exchange the implementation pointers held by `*this` and `other`. | ||||||
| HITGNC | 117 | + | 408369 | void swap(object_ref& other) noexcept { std::swap(p_, other.p_); } | ||||
| 118 | + | }; | ||||||
| 119 | + | |||||||
| 120 | + | } // namespace boost::corosio::detail | ||||||
| 121 | + | |||||||
| 122 | + | #endif | ||||||