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   133529 acquire(io_object::implementation* impl) noexcept 25   138046 acquire(io_object::implementation* impl) noexcept
26   { 26   {
HITCBC 27   133529 impl->refs_.fetch_add(1, std::memory_order_relaxed); 27   138046 impl->refs_.fetch_add(1, std::memory_order_relaxed);
HITCBC 28   133529 } 28   138046 }
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   9963 try_acquire(io_object::implementation* impl) noexcept 46   9980 try_acquire(io_object::implementation* impl) noexcept
47   { 47   {
HITCBC 48   9963 auto n = impl->refs_.load(std::memory_order_relaxed); 48   9980 auto n = impl->refs_.load(std::memory_order_relaxed);
HITCBC 49   9965 while (n != 0 && n != pooled_refs) 49   9988 while (n != 0 && n != pooled_refs)
50   { 50   {
HITCBC 51   19922 if (impl->refs_.compare_exchange_weak( 51   19968 if (impl->refs_.compare_exchange_weak(
52   n, n + 1, std::memory_order_relaxed)) 52   n, n + 1, std::memory_order_relaxed))
HITCBC 53   9959 return true; 53   9976 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   179301 release(io_object::implementation* impl) noexcept 68   183482 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   358602 if (impl->refs_.fetch_sub(1, std::memory_order_release) == 1) 74   366964 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   35813 impl->retire(); 77   35460 impl->retire();
78   } 78   }
79   #endif 79   #endif
HITCBC 80   179301 } 80   183482 }
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   558790 ~object_ref() { reset(); } 94   577191 ~object_ref() { reset(); }
95   95  
96   /// Construct an object_ref holding no reference. 96   /// Construct an object_ref holding no reference.
HITCBC 97   92051 object_ref() noexcept = default; 97   92276 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   130299 explicit object_ref(io_object::implementation* p) noexcept : p_(p) 100   134816 explicit object_ref(io_object::implementation* p) noexcept : p_(p)
101   { 101   {
HITCBC 102   130299 if (p_) 102   134816 if (p_)
HITCBC 103   130299 acquire(p_); 103   134816 acquire(p_);
HITCBC 104   130299 } 104   134816 }
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   749 static object_ref try_from(io_object::implementation* p) noexcept 116   767 static object_ref try_from(io_object::implementation* p) noexcept
117   { 117   {
HITCBC 118   749 object_ref r; 118   767 object_ref r;
HITCBC 119   749 if (try_acquire(p)) 119   767 if (try_acquire(p))
HITCBC 120   747 r.p_ = p; 120   765 r.p_ = p;
HITCBC 121   749 return r; 121   767 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   285874 object_ref(object_ref&& other) noexcept 132   297008 object_ref(object_ref&& other) noexcept
HITCBC 133   285874 : p_(std::exchange(other.p_, nullptr)) 133   297008 : p_(std::exchange(other.p_, nullptr))
134   { 134   {
HITCBC 135   285874 } 135   297008 }
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   169584 object_ref& operator=(object_ref&& other) noexcept 145   176394 object_ref& operator=(object_ref&& other) noexcept
146   { 146   {
HITCBC 147   169584 object_ref(std::move(other)).swap(*this); 147   176394 object_ref(std::move(other)).swap(*this);
HITCBC 148   169584 return *this; 148   176394 return *this;
149   } 149   }
150   150  
151   /// Release the held reference, if any. 151   /// Release the held reference, if any.
HITCBC 152   1009687 void reset() noexcept 152   1047640 void reset() noexcept
153   { 153   {
HITCBC 154   1009687 if (p_) 154   1047640 if (p_)
HITCBC 155   131049 release(std::exchange(p_, nullptr)); 155   135584 release(std::exchange(p_, nullptr));
HITCBC 156   1009687 } 156   1047640 }
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   81406 explicit operator bool() const noexcept { return p_ != nullptr; } 162   82990 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   169586 void swap(object_ref& other) noexcept { std::swap(p_, other.p_); } 165   176396 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