include/boost/corosio/detail/object_ref.hpp

100.0% Lines (33 / 33) 100.0% Functions (13 / 13)
object_ref.hpp
f(x) Functions (13)
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2026 Steve Gerbino
4 //
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)
7 //
8 // Official repository: https://github.com/cppalliance/corosio
9 //
10
11 #ifndef BOOST_COROSIO_DETAIL_OBJECT_REF_HPP
12 #define BOOST_COROSIO_DETAIL_OBJECT_REF_HPP
13
14 #include <boost/corosio/detail/platform.hpp>
15 #include <boost/corosio/io/io_object.hpp>
16 #include <atomic>
17 #include <utility>
18
19 namespace boost::corosio::detail {
20
21 /// Add one reference to `impl`. `impl` must be non-null.
22 inline void
23 105679x acquire(io_object::implementation* impl) noexcept
24 {
25 105679x impl->refs_.fetch_add(1, std::memory_order_relaxed);
26 105679x }
27
28 /** Drop one reference; runs `retire()` at zero. `impl` must be
29 non-null.
30
31 Uses release on the decrement and an acquire fence before the
32 zero-action so writes made while holding a reference are visible
33 to the recycler. TSan cannot model a standalone fence, so under
34 it the decrement itself is acq_rel instead -- strictly stronger,
35 and TSan's happens-before tracking models the exchange directly.
36 */
37 inline void
38 126004x release(io_object::implementation* impl) noexcept
39 {
40 #if BOOST_COROSIO_TSAN
41 if (impl->refs_.fetch_sub(1, std::memory_order_acq_rel) == 1)
42 impl->retire();
43 #else
44 252008x if (impl->refs_.fetch_sub(1, std::memory_order_release) == 1)
45 {
46 std::atomic_thread_fence(std::memory_order_acquire);
47 20325x impl->retire();
48 }
49 #endif
50 126004x }
51
52 /** Owning intrusive reference to an I/O implementation.
53
54 Replaces `std::shared_ptr<void>` op keepalives: copy acquires,
55 move steals, destruction releases. Kept deliberately minimal —
56 two words smaller than a shared_ptr and no control block.
57 */
58 class object_ref
59 {
60 io_object::implementation* p_ = nullptr;
61
62 public:
63 /// Destroy the object_ref, releasing the held reference, if any.
64 950945x ~object_ref() { reset(); }
65
66 /// Construct an object_ref holding no reference.
67 30938x object_ref() noexcept = default;
68
69 /// Construct an object_ref that acquires a new reference to `p`.
70 105659x explicit object_ref(io_object::implementation* p) noexcept : p_(p)
71 {
72 105659x if (p_)
73 105659x acquire(p_);
74 105659x }
75
76 /// Construct a copy, acquiring another reference to `other`'s target.
77 3x object_ref(object_ref const& other) noexcept : p_(other.p_)
78 {
79 3x if (p_)
80 3x acquire(p_);
81 3x }
82
83 /// Construct by transferring the reference out of `other`.
84 511944x object_ref(object_ref&& other) noexcept
85 511944x : p_(std::exchange(other.p_, nullptr))
86 {
87 511944x }
88
89 /// Release the held reference, if any, and acquire `other`'s target.
90 2x object_ref& operator=(object_ref const& other) noexcept
91 {
92 2x object_ref(other).swap(*this);
93 2x return *this;
94 }
95
96 /// Release the held reference, if any, and take ownership of `other`'s.
97 408367x object_ref& operator=(object_ref&& other) noexcept
98 {
99 408367x object_ref(std::move(other)).swap(*this);
100 408367x return *this;
101 }
102
103 /// Release the held reference, if any.
104 1431925x void reset() noexcept
105 {
106 1431925x if (p_)
107 105662x release(std::exchange(p_, nullptr));
108 1431925x }
109
110 /// Return the held implementation pointer.
111 3x io_object::implementation* get() const noexcept { return p_; }
112
113 /// Return true if a reference is held.
114 13452x explicit operator bool() const noexcept { return p_ != nullptr; }
115
116 /// Exchange the implementation pointers held by `*this` and `other`.
117 408369x void swap(object_ref& other) noexcept { std::swap(p_, other.p_); }
118 };
119
120 } // namespace boost::corosio::detail
121
122 #endif
123