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   140912 acquire(io_object::implementation* impl) noexcept 25   133690 acquire(io_object::implementation* impl) noexcept
26   { 26   {
HITCBC 27   140912 impl->refs_.fetch_add(1, std::memory_order_relaxed); 27   133690 impl->refs_.fetch_add(1, std::memory_order_relaxed);
HITCBC 28   140912 } 28   133690 }
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   10001 try_acquire(io_object::implementation* impl) noexcept 46   9965 try_acquire(io_object::implementation* impl) noexcept
47   { 47   {
HITCBC 48   10001 auto n = impl->refs_.load(std::memory_order_relaxed); 48   9965 auto n = impl->refs_.load(std::memory_order_relaxed);
HITCBC 49   10007 while (n != 0 && n != pooled_refs) 49   9968 while (n != 0 && n != pooled_refs)
50   { 50   {
HITCBC 51   20006 if (impl->refs_.compare_exchange_weak( 51   19928 if (impl->refs_.compare_exchange_weak(
52   n, n + 1, std::memory_order_relaxed)) 52   n, n + 1, std::memory_order_relaxed))
HITCBC 53   9997 return true; 53   9961 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   186399 release(io_object::implementation* impl) noexcept 68   179796 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   372798 if (impl->refs_.fetch_sub(1, std::memory_order_release) == 1) 74   359592 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   35490 impl->retire(); 77   36145 impl->retire();
78   } 78   }
79   #endif 79   #endif
HITCBC 80   186399 } 80   179796 }
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   586294 ~object_ref() { reset(); } 94   559652 ~object_ref() { reset(); }
95   95  
96   /// Construct an object_ref holding no reference. 96   /// Construct an object_ref holding no reference.
HITCBC 97   92450 object_ref() noexcept = default; 97   93116 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   137682 explicit object_ref(io_object::implementation* p) noexcept : p_(p) 100   130460 explicit object_ref(io_object::implementation* p) noexcept : p_(p)
101   { 101   {
HITCBC 102   137682 if (p_) 102   130460 if (p_)
HITCBC 103   137682 acquire(p_); 103   130460 acquire(p_);
HITCBC 104   137682 } 104   130460 }
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   781 static object_ref try_from(io_object::implementation* p) noexcept 116   742 static object_ref try_from(io_object::implementation* p) noexcept
117   { 117   {
HITCBC 118   781 object_ref r; 118   742 object_ref r;
HITCBC 119   781 if (try_acquire(p)) 119   742 if (try_acquire(p))
HITCBC 120   779 r.p_ = p; 120   740 r.p_ = p;
HITCBC 121   781 return r; 121   742 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   302495 object_ref(object_ref&& other) noexcept 132   285471 object_ref(object_ref&& other) noexcept
HITCBC 133   302495 : p_(std::exchange(other.p_, nullptr)) 133   285471 : p_(std::exchange(other.p_, nullptr))
134   { 134   {
HITCBC 135   302495 } 135   285471 }
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   179460 object_ref& operator=(object_ref&& other) noexcept 145   169466 object_ref& operator=(object_ref&& other) noexcept
146   { 146   {
HITCBC 147   179460 object_ref(std::move(other)).swap(*this); 147   169466 object_ref(std::move(other)).swap(*this);
HITCBC 148   179460 return *this; 148   169466 return *this;
149   } 149   }
150   150  
151   /// Release the held reference, if any. 151   /// Release the held reference, if any.
HITCBC 152   1069681 void reset() noexcept 152   1005541 void reset() noexcept
153   { 153   {
HITCBC 154   1069681 if (p_) 154   1005541 if (p_)
HITCBC 155   138464 release(std::exchange(p_, nullptr)); 155   131203 release(std::exchange(p_, nullptr));
HITCBC 156   1069681 } 156   1005541 }
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   82555 explicit operator bool() const noexcept { return p_ != nullptr; } 162   83900 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   179462 void swap(object_ref& other) noexcept { std::swap(p_, other.p_); } 165   169468 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