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   143187 acquire(io_object::implementation* impl) noexcept 25   119607 acquire(io_object::implementation* impl) noexcept
26   { 26   {
HITCBC 27   143187 impl->refs_.fetch_add(1, std::memory_order_relaxed); 27   119607 impl->refs_.fetch_add(1, std::memory_order_relaxed);
HITCBC 28   143187 } 28   119607 }
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   9950 try_acquire(io_object::implementation* impl) noexcept 46   9957 try_acquire(io_object::implementation* impl) noexcept
47   { 47   {
HITCBC 48   9950 auto n = impl->refs_.load(std::memory_order_relaxed); 48   9957 auto n = impl->refs_.load(std::memory_order_relaxed);
HITCBC 49   9952 while (n != 0 && n != pooled_refs) 49   9959 while (n != 0 && n != pooled_refs)
50   { 50   {
HITCBC 51   19896 if (impl->refs_.compare_exchange_weak( 51   19910 if (impl->refs_.compare_exchange_weak(
52   n, n + 1, std::memory_order_relaxed)) 52   n, n + 1, std::memory_order_relaxed))
HITCBC 53   9946 return true; 53   9953 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   188705 release(io_object::implementation* impl) noexcept 68   163448 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   377410 if (impl->refs_.fetch_sub(1, std::memory_order_release) == 1) 74   326896 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   35572 impl->retire(); 77   33888 impl->retire();
78   } 78   }
79   #endif 79   #endif
HITCBC 80   188705 } 80   163448 }
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   598831 ~object_ref() { reset(); } 94   507083 ~object_ref() { reset(); }
95   95  
96   /// Construct an object_ref holding no reference. 96   /// Construct an object_ref holding no reference.
HITCBC 97   93206 object_ref() noexcept = default; 97   92288 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   139957 explicit object_ref(io_object::implementation* p) noexcept : p_(p) 100   116377 explicit object_ref(io_object::implementation* p) noexcept : p_(p)
101   { 101   {
HITCBC 102   139957 if (p_) 102   116377 if (p_)
HITCBC 103   139957 acquire(p_); 103   116377 acquire(p_);
HITCBC 104   139957 } 104   116377 }
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   739 static object_ref try_from(io_object::implementation* p) noexcept 116   750 static object_ref try_from(io_object::implementation* p) noexcept
117   { 117   {
HITCBC 118   739 object_ref r; 118   750 object_ref r;
HITCBC 119   739 if (try_acquire(p)) 119   750 if (try_acquire(p))
HITCBC 120   737 r.p_ = p; 120   748 r.p_ = p;
HITCBC 121   739 return r; 121   750 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   310389 object_ref(object_ref&& other) noexcept 132   253709 object_ref(object_ref&& other) noexcept
HITCBC 133   310389 : p_(std::exchange(other.p_, nullptr)) 133   253709 : p_(std::exchange(other.p_, nullptr))
134   { 134   {
HITCBC 135   310389 } 135   253709 }
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   184202 object_ref& operator=(object_ref&& other) noexcept 145   150727 object_ref& operator=(object_ref&& other) noexcept
146   { 146   {
HITCBC 147   184202 object_ref(std::move(other)).swap(*this); 147   150727 object_ref(std::move(other)).swap(*this);
HITCBC 148   184202 return *this; 148   150727 return *this;
149   } 149   }
150   150  
151   /// Release the held reference, if any. 151   /// Release the held reference, if any.
HITCBC 152   1098046 void reset() noexcept 152   893357 void reset() noexcept
153   { 153   {
HITCBC 154   1098046 if (p_) 154   893357 if (p_)
HITCBC 155   140697 release(std::exchange(p_, nullptr)); 155   117128 release(std::exchange(p_, nullptr));
HITCBC 156   1098046 } 156   893357 }
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   81841 explicit operator bool() const noexcept { return p_ != nullptr; } 162   77615 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   184204 void swap(object_ref& other) noexcept { std::swap(p_, other.p_); } 165   150729 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