LCOV - code coverage report
Current view: top level - corosio/detail - object_ref.hpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 100.0 % 44 44
Test Date: 2026-10-17 04:01:32 Functions: 100.0 % 15 15

           TLA  Line data    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 HIT      143295 : acquire(io_object::implementation* impl) noexcept
      26                 : {
      27          143295 :     impl->refs_.fetch_add(1, std::memory_order_relaxed);
      28          143295 : }
      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            9996 : try_acquire(io_object::implementation* impl) noexcept
      47                 : {
      48            9996 :     auto n = impl->refs_.load(std::memory_order_relaxed);
      49            9999 :     while (n != 0 && n != pooled_refs)
      50                 :     {
      51           19990 :         if (impl->refs_.compare_exchange_weak(
      52                 :                 n, n + 1, std::memory_order_relaxed))
      53            9992 :             return true;
      54                 :     }
      55               4 :     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          189381 : 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          378762 :     if (impl->refs_.fetch_sub(1, std::memory_order_release) == 1)
      75                 :     {
      76                 :         std::atomic_thread_fence(std::memory_order_acquire);
      77           36094 :         impl->retire();
      78                 :     }
      79                 : #endif
      80          189381 : }
      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          595448 :     ~object_ref() { reset(); }
      95                 : 
      96                 :     /// Construct an object_ref holding no reference.
      97           90354 :     object_ref() noexcept = default;
      98                 : 
      99                 :     /// Construct an object_ref that acquires a new reference to `p`.
     100          140069 :     explicit object_ref(io_object::implementation* p) noexcept : p_(p)
     101                 :     {
     102          140069 :         if (p_)
     103          140069 :             acquire(p_);
     104          140069 :     }
     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             795 :     static object_ref try_from(io_object::implementation* p) noexcept
     117                 :     {
     118             795 :         object_ref r;
     119             795 :         if (try_acquire(p))
     120             793 :             r.p_ = p;
     121             795 :         return r;
     122                 :     }
     123                 : 
     124                 :     /// Construct a copy, acquiring another reference to `other`'s target.
     125               3 :     object_ref(object_ref const& other) noexcept : p_(other.p_)
     126                 :     {
     127               3 :         if (p_)
     128               3 :             acquire(p_);
     129               3 :     }
     130                 : 
     131                 :     /// Construct by transferring the reference out of `other`.
     132          310011 :     object_ref(object_ref&& other) noexcept
     133          310011 :         : p_(std::exchange(other.p_, nullptr))
     134                 :     {
     135          310011 :     }
     136                 : 
     137                 :     /// Release the held reference, if any, and acquire `other`'s target.
     138               2 :     object_ref& operator=(object_ref const& other) noexcept
     139                 :     {
     140               2 :         object_ref(other).swap(*this);
     141               2 :         return *this;
     142                 :     }
     143                 : 
     144                 :     /// Release the held reference, if any, and take ownership of `other`'s.
     145          183905 :     object_ref& operator=(object_ref&& other) noexcept
     146                 :     {
     147          183905 :         object_ref(std::move(other)).swap(*this);
     148          183905 :         return *this;
     149                 :     }
     150                 : 
     151                 :     /// Release the held reference, if any.
     152         1092312 :     void reset() noexcept
     153                 :     {
     154         1092312 :         if (p_)
     155          140865 :             release(std::exchange(p_, nullptr));
     156         1092312 :     }
     157                 : 
     158                 :     /// Return the held implementation pointer.
     159               4 :     io_object::implementation* get() const noexcept { return p_; }
     160                 : 
     161                 :     /// Return true if a reference is held.
     162           83210 :     explicit operator bool() const noexcept { return p_ != nullptr; }
     163                 : 
     164                 :     /// Exchange the implementation pointers held by `*this` and `other`.
     165          183907 :     void swap(object_ref& other) noexcept { std::swap(p_, other.p_); }
     166                 : };
     167                 : 
     168                 : } // namespace boost::corosio::detail
     169                 : 
     170                 : #endif
        

Generated by: LCOV version 2.3