100.00% Lines (44/44) 100.00% Functions (15/15)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3   // Copyright (c) 2026 Steve Gerbino 3   // Copyright (c) 2026 Steve Gerbino
4   // 4   //
5   // Distributed under the Boost Software License, Version 1.0. (See accompanying 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) 6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7   // 7   //
8   // Official repository: https://github.com/cppalliance/corosio 8   // Official repository: https://github.com/cppalliance/corosio
9   // 9   //
10   10  
11   #ifndef BOOST_COROSIO_DETAIL_OBJECT_REF_HPP 11   #ifndef BOOST_COROSIO_DETAIL_OBJECT_REF_HPP
12   #define BOOST_COROSIO_DETAIL_OBJECT_REF_HPP 12   #define BOOST_COROSIO_DETAIL_OBJECT_REF_HPP
13   13  
14   #include <boost/corosio/detail/platform.hpp> 14   #include <boost/corosio/detail/platform.hpp>
15   #include <boost/corosio/io/io_object.hpp> 15   #include <boost/corosio/io/io_object.hpp>
16   #include <atomic> 16   #include <atomic>
17   #include <cstddef> 17   #include <cstddef>
18   #include <limits> 18   #include <limits>
19   #include <utility> 19   #include <utility>
20   20  
21   namespace boost::corosio::detail { 21   namespace boost::corosio::detail {
22   22  
23   /// Add one reference to `impl`. `impl` must be non-null. 23   /// Add one reference to `impl`. `impl` must be non-null.
24   inline void 24   inline void
HITCBC 25   139511 acquire(io_object::implementation* impl) noexcept 25   125390 acquire(io_object::implementation* impl) noexcept
26   { 26   {
HITCBC 27   139511 impl->refs_.fetch_add(1, std::memory_order_relaxed); 27   125390 impl->refs_.fetch_add(1, std::memory_order_relaxed);
HITCBC 28   139511 } 28   125390 }
29   29  
30   /** The count an `object_pool` stamps on an impl parked for reuse. 30   /** The count an `object_pool` stamps on an impl parked for reuse.
31   31  
32   It holds no reference; see `object_pool`. 32   It holds no reference; see `object_pool`.
33   */ 33   */
34   inline constexpr std::size_t pooled_refs = 34   inline constexpr std::size_t pooled_refs =
35   (std::numeric_limits<std::size_t>::max)(); 35   (std::numeric_limits<std::size_t>::max)();
36   36  
37   /** Add one reference to `impl` unless it is retiring or parked. 37   /** Add one reference to `impl` unless it is retiring or parked.
38   38  
39   A count at zero means `retire()` is running or about to, and a 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 40   count of `pooled_refs` means the impl waits in its pool for a new
41   owner; neither may be revived. 41   owner; neither may be revived.
42   42  
43   @return True if a reference was added. 43   @return True if a reference was added.
44   */ 44   */
45   inline bool 45   inline bool
HITCBC 46   9968 try_acquire(io_object::implementation* impl) noexcept 46   9946 try_acquire(io_object::implementation* impl) noexcept
47   { 47   {
HITCBC 48   9968 auto n = impl->refs_.load(std::memory_order_relaxed); 48   9946 auto n = impl->refs_.load(std::memory_order_relaxed);
HITCBC 49   9971 while (n != 0 && n != pooled_refs) 49   9946 while (n != 0 && n != pooled_refs)
50   { 50   {
HITCBC 51   19934 if (impl->refs_.compare_exchange_weak( 51   19884 if (impl->refs_.compare_exchange_weak(
52   n, n + 1, std::memory_order_relaxed)) 52   n, n + 1, std::memory_order_relaxed))
HITCBC 53   9964 return true; 53   9942 return true;
54   } 54   }
HITCBC 55   4 return false; 55   4 return false;
56   } 56   }
57   57  
58   /** Drop one reference; runs `retire()` at zero. `impl` must be 58   /** Drop one reference; runs `retire()` at zero. `impl` must be
59   non-null. 59   non-null.
60   60  
61   Uses release on the decrement and an acquire fence before the 61   Uses release on the decrement and an acquire fence before the
62   zero-action so writes made while holding a reference are visible 62   zero-action so writes made while holding a reference are visible
63   to the recycler. TSan cannot model a standalone fence, so under 63   to the recycler. TSan cannot model a standalone fence, so under
64   it the decrement itself is acq_rel instead -- strictly stronger, 64   it the decrement itself is acq_rel instead -- strictly stronger,
65   and TSan's happens-before tracking models the exchange directly. 65   and TSan's happens-before tracking models the exchange directly.
66   */ 66   */
67   inline void 67   inline void
HITCBC 68   184418 release(io_object::implementation* impl) noexcept 68   170937 release(io_object::implementation* impl) noexcept
69   { 69   {
70   #if BOOST_COROSIO_TSAN 70   #if BOOST_COROSIO_TSAN
71   if (impl->refs_.fetch_sub(1, std::memory_order_acq_rel) == 1) 71   if (impl->refs_.fetch_sub(1, std::memory_order_acq_rel) == 1)
72   impl->retire(); 72   impl->retire();
73   #else 73   #else
HITCBC 74   368836 if (impl->refs_.fetch_sub(1, std::memory_order_release) == 1) 74   341874 if (impl->refs_.fetch_sub(1, std::memory_order_release) == 1)
75   { 75   {
76   std::atomic_thread_fence(std::memory_order_acquire); 76   std::atomic_thread_fence(std::memory_order_acquire);
HITCBC 77   34943 impl->retire(); 77   35605 impl->retire();
78   } 78   }
79   #endif 79   #endif
HITCBC 80   184418 } 80   170937 }
81   81  
82   /** Owning intrusive reference to an I/O implementation. 82   /** Owning intrusive reference to an I/O implementation.
83   83  
84   Replaces `std::shared_ptr<void>` op keepalives: copy acquires, 84   Replaces `std::shared_ptr<void>` op keepalives: copy acquires,
85   move steals, destruction releases. Kept deliberately minimal — 85   move steals, destruction releases. Kept deliberately minimal —
86   one pointer, half the size of a shared_ptr, and no control block. 86   one pointer, half the size of a shared_ptr, and no control block.
87   */ 87   */
88   class object_ref 88   class object_ref
89   { 89   {
90   io_object::implementation* p_ = nullptr; 90   io_object::implementation* p_ = nullptr;
91   91  
92   public: 92   public:
93   /// Destroy the object_ref, releasing the held reference, if any. 93   /// Destroy the object_ref, releasing the held reference, if any.
HITCBC 94   580253 ~object_ref() { reset(); } 94   531979 ~object_ref() { reset(); }
95   95  
96   /// Construct an object_ref holding no reference. 96   /// Construct an object_ref holding no reference.
HITCBC 97   89886 object_ref() noexcept = default; 97   89788 object_ref() noexcept = default;
98   98  
99   /// Construct an object_ref that acquires a new reference to `p`. 99   /// Construct an object_ref that acquires a new reference to `p`.
HITCBC 100   136285 explicit object_ref(io_object::implementation* p) noexcept : p_(p) 100   122164 explicit object_ref(io_object::implementation* p) noexcept : p_(p)
101   { 101   {
HITCBC 102   136285 if (p_) 102   122164 if (p_)
HITCBC 103   136285 acquire(p_); 103   122164 acquire(p_);
HITCBC 104   136285 } 104   122164 }
105   105  
106   /** Create a reference to `p` unless its count already reached zero. 106   /** Create a reference to `p` unless its count already reached zero.
107   107  
108   The way to take a reference from a raw pointer: a context that 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. 109   holds none may find the count at zero, and must not revive it.
110   110  
111   @param p The implementation to reference. 111   @param p The implementation to reference.
112   112  
113   @return A reference to `p`, or an empty `object_ref` if `p` is 113   @return A reference to `p`, or an empty `object_ref` if `p` is
114   retiring. 114   retiring.
115   */ 115   */
HITCBC 116   754 static object_ref try_from(io_object::implementation* p) noexcept 116   741 static object_ref try_from(io_object::implementation* p) noexcept
117   { 117   {
HITCBC 118   754 object_ref r; 118   741 object_ref r;
HITCBC 119   754 if (try_acquire(p)) 119   741 if (try_acquire(p))
HITCBC 120   752 r.p_ = p; 120   739 r.p_ = p;
HITCBC 121   754 return r; 121   741 return r;
122   } 122   }
123   123  
124   /// Construct a copy, acquiring another reference to `other`'s target. 124   /// Construct a copy, acquiring another reference to `other`'s target.
HITCBC 125   3 object_ref(object_ref const& other) noexcept : p_(other.p_) 125   3 object_ref(object_ref const& other) noexcept : p_(other.p_)
126   { 126   {
HITCBC 127   3 if (p_) 127   3 if (p_)
HITCBC 128   3 acquire(p_); 128   3 acquire(p_);
HITCBC 129   3 } 129   3 }
130   130  
131   /// Construct by transferring the reference out of `other`. 131   /// Construct by transferring the reference out of `other`.
HITCBC 132   301144 object_ref(object_ref&& other) noexcept 132   269734 object_ref(object_ref&& other) noexcept
HITCBC 133   301144 : p_(std::exchange(other.p_, nullptr)) 133   269734 : p_(std::exchange(other.p_, nullptr))
134   { 134   {
HITCBC 135   301144 } 135   269734 }
136   136  
137   /// Release the held reference, if any, and acquire `other`'s target. 137   /// Release the held reference, if any, and acquire `other`'s target.
HITCBC 138   2 object_ref& operator=(object_ref const& other) noexcept 138   2 object_ref& operator=(object_ref const& other) noexcept
139   { 139   {
HITCBC 140   2 object_ref(other).swap(*this); 140   2 object_ref(other).swap(*this);
HITCBC 141   2 return *this; 141   2 return *this;
142   } 142   }
143   143  
144   /// Release the held reference, if any, and take ownership of `other`'s. 144   /// Release the held reference, if any, and take ownership of `other`'s.
HITCBC 145   178447 object_ref& operator=(object_ref&& other) noexcept 145   161433 object_ref& operator=(object_ref&& other) noexcept
146   { 146   {
HITCBC 147   178447 object_ref(std::move(other)).swap(*this); 147   161433 object_ref(std::move(other)).swap(*this);
HITCBC 148   178447 return *this; 148   161433 return *this;
149   } 149   }
150   150  
151   /// Release the held reference, if any. 151   /// Release the held reference, if any.
HITCBC 152   1061921 void reset() noexcept 152   939810 void reset() noexcept
153   { 153   {
HITCBC 154   1061921 if (p_) 154   939810 if (p_)
HITCBC 155   137040 release(std::exchange(p_, nullptr)); 155   122906 release(std::exchange(p_, nullptr));
HITCBC 156   1061921 } 156   939810 }
157   157  
158   /// Return the held implementation pointer. 158   /// Return the held implementation pointer.
HITCBC 159   4 io_object::implementation* get() const noexcept { return p_; } 159   4 io_object::implementation* get() const noexcept { return p_; }
160   160  
161   /// Return true if a reference is held. 161   /// Return true if a reference is held.
HITCBC 162   81672 explicit operator bool() const noexcept { return p_ != nullptr; } 162   83252 explicit operator bool() const noexcept { return p_ != nullptr; }
163   163  
164   /// Exchange the implementation pointers held by `*this` and `other`. 164   /// Exchange the implementation pointers held by `*this` and `other`.
HITCBC 165   178449 void swap(object_ref& other) noexcept { std::swap(p_, other.p_); } 165   161435 void swap(object_ref& other) noexcept { std::swap(p_, other.p_); }
166   }; 166   };
167   167  
168   } // namespace boost::corosio::detail 168   } // namespace boost::corosio::detail
169   169  
170   #endif 170   #endif