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   138460 acquire(io_object::implementation* impl) noexcept 25   140756 acquire(io_object::implementation* impl) noexcept
26   { 26   {
HITCBC 27   138460 impl->refs_.fetch_add(1, std::memory_order_relaxed); 27   140756 impl->refs_.fetch_add(1, std::memory_order_relaxed);
HITCBC 28   138460 } 28   140756 }
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   9940 try_acquire(io_object::implementation* impl) noexcept 46   9960 try_acquire(io_object::implementation* impl) noexcept
47   { 47   {
HITCBC 48   9940 auto n = impl->refs_.load(std::memory_order_relaxed); 48   9960 auto n = impl->refs_.load(std::memory_order_relaxed);
HITCBC 49   9940 while (n != 0 && n != pooled_refs) 49   9968 while (n != 0 && n != pooled_refs)
50   { 50   {
HITCBC 51   19872 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   9936 return true; 53   9956 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   183436 release(io_object::implementation* impl) noexcept 68   186020 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   366872 if (impl->refs_.fetch_sub(1, std::memory_order_release) == 1) 74   372040 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   35040 impl->retire(); 77   35308 impl->retire();
78   } 78   }
79   #endif 79   #endif
HITCBC 80   183436 } 80   186020 }
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   578748 ~object_ref() { reset(); } 94   584326 ~object_ref() { reset(); }
95   95  
96   /// Construct an object_ref holding no reference. 96   /// Construct an object_ref holding no reference.
HITCBC 97   89815 object_ref() noexcept = default; 97   89361 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   135234 explicit object_ref(io_object::implementation* p) noexcept : p_(p) 100   137526 explicit object_ref(io_object::implementation* p) noexcept : p_(p)
101   { 101   {
HITCBC 102   135234 if (p_) 102   137526 if (p_)
HITCBC 103   135234 acquire(p_); 103   137526 acquire(p_);
HITCBC 104   135234 } 104   137526 }
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   747 static object_ref try_from(io_object::implementation* p) noexcept 116   740 static object_ref try_from(io_object::implementation* p) noexcept
117   { 117   {
HITCBC 118   747 object_ref r; 118   740 object_ref r;
HITCBC 119   747 if (try_acquire(p)) 119   740 if (try_acquire(p))
HITCBC 120   745 r.p_ = p; 120   738 r.p_ = p;
HITCBC 121   747 return r; 121   740 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   299387 object_ref(object_ref&& other) noexcept 132   303514 object_ref(object_ref&& other) noexcept
HITCBC 133   299387 : p_(std::exchange(other.p_, nullptr)) 133   303514 : p_(std::exchange(other.p_, nullptr))
134   { 134   {
HITCBC 135   299387 } 135   303514 }
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   178179 object_ref& operator=(object_ref&& other) noexcept 145   180060 object_ref& operator=(object_ref&& other) noexcept
146   { 146   {
HITCBC 147   178179 object_ref(std::move(other)).swap(*this); 147   180060 object_ref(std::move(other)).swap(*this);
HITCBC 148   178179 return *this; 148   180060 return *this;
149   } 149   }
150   150  
151   /// Release the held reference, if any. 151   /// Release the held reference, if any.
HITCBC 152   1052796 void reset() noexcept 152   1071286 void reset() noexcept
153   { 153   {
HITCBC 154   1052796 if (p_) 154   1071286 if (p_)
HITCBC 155   135982 release(std::exchange(p_, nullptr)); 155   138267 release(std::exchange(p_, nullptr));
HITCBC 156   1052796 } 156   1071286 }
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   81818 explicit operator bool() const noexcept { return p_ != nullptr; } 162   81285 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   178181 void swap(object_ref& other) noexcept { std::swap(p_, other.p_); } 165   180062 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