xref: /openbmc/sdbusplus/include/sdbusplus/async/stdexec/__detail/__intrusive_ptr.hpp (revision c53fe51ca59c5a1d7711aa5c1c7eb5b5ac6e6850)
1 /*
2  * Copyright (c) 2022 NVIDIA Corporation
3  *
4  * Licensed under the Apache License Version 2.0 with LLVM Exceptions
5  * (the "License"); you may not use this file except in compliance with
6  * the License. You may obtain a copy of the License at
7  *
8  *   https://llvm.org/LICENSE.txt
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #pragma once
17 
18 #include "../concepts.hpp"
19 #include "__meta.hpp"
20 
21 #include <atomic>
22 #include <memory>
23 #include <new>
24 
25 #if STDEXEC_TSAN()
26 #include <sanitizer/tsan_interface.h>
27 #endif
28 
29 namespace stdexec
30 {
31 namespace __ptr
32 {
33 template <class _Ty>
34 struct __make_intrusive_t;
35 
36 template <class _Ty>
37 class __intrusive_ptr;
38 
39 template <class _Ty>
40 struct __enable_intrusive_from_this
41 {
42     __intrusive_ptr<_Ty> __intrusive_from_this() noexcept;
43     __intrusive_ptr<const _Ty> __intrusive_from_this() const noexcept;
44 
45   private:
46     friend _Ty;
47     void __inc_ref() noexcept;
48     void __dec_ref() noexcept;
49 };
50 
51 STDEXEC_PRAGMA_PUSH()
52 STDEXEC_PRAGMA_IGNORE_GNU("-Wtsan")
53 
54 template <class _Ty>
55 struct __control_block
56 {
57     alignas(_Ty) unsigned char __value_[sizeof(_Ty)];
58     std::atomic<unsigned long> __refcount_;
59 
60     template <class... _Us>
61     explicit __control_block(_Us&&... __us) noexcept(noexcept(_Ty{
62         __declval<_Us>()...})) :
63         __refcount_(1u)
64     {
65         // Construct the value *after* the initialization of the
66         // atomic in case the constructor of _Ty calls
67         // __intrusive_from_this() (which increments the atomic):
68         ::new ((void*)__value_) _Ty{(_Us&&)__us...};
69     }
70 
71     ~__control_block()
72     {
73         __value().~_Ty();
74     }
75 
76     _Ty& __value() const noexcept
77     {
78         return *(_Ty*)__value_;
79     }
80 
81     void __inc_ref_() noexcept
82     {
83         __refcount_.fetch_add(1, std::memory_order_relaxed);
84     }
85 
86     void __dec_ref_() noexcept
87     {
88         if (1u == __refcount_.fetch_sub(1, std::memory_order_release))
89         {
90             std::atomic_thread_fence(std::memory_order_acquire);
91             // TSan does not support std::atomic_thread_fence, so we
92             // need to use the TSan-specific __tsan_acquire instead:
93             STDEXEC_TSAN(__tsan_acquire(&__refcount_));
94             delete this;
95         }
96     }
97 };
98 
99 STDEXEC_PRAGMA_POP()
100 
101 template <class _Ty>
102 class __intrusive_ptr
103 {
104     using _UncvTy = std::remove_cv_t<_Ty>;
105     friend struct __make_intrusive_t<_Ty>;
106     friend struct __enable_intrusive_from_this<_UncvTy>;
107 
108     __control_block<_UncvTy>* __data_{nullptr};
109 
110     explicit __intrusive_ptr(__control_block<_UncvTy>* __data) noexcept :
111         __data_(__data)
112     {}
113 
114     void __inc_ref_() noexcept
115     {
116         if (__data_)
117         {
118             __data_->__inc_ref_();
119         }
120     }
121 
122     void __dec_ref_() noexcept
123     {
124         if (__data_)
125         {
126             __data_->__dec_ref_();
127         }
128     }
129 
130   public:
131     using element_type = _Ty;
132 
133     __intrusive_ptr() = default;
134 
135     __intrusive_ptr(__intrusive_ptr&& __that) noexcept :
136         __data_(std::exchange(__that.__data_, nullptr))
137     {}
138 
139     __intrusive_ptr(const __intrusive_ptr& __that) noexcept :
140         __data_(__that.__data_)
141     {
142         __inc_ref_();
143     }
144 
145     __intrusive_ptr(__enable_intrusive_from_this<_Ty>* __that) noexcept :
146         __intrusive_ptr(__that ? __that->__intrusive_from_this()
147                                : __intrusive_ptr())
148     {}
149 
150     __intrusive_ptr& operator=(__intrusive_ptr&& __that) noexcept
151     {
152         [[maybe_unused]] __intrusive_ptr __old{
153             std::exchange(__data_, std::exchange(__that.__data_, nullptr))};
154         return *this;
155     }
156 
157     __intrusive_ptr& operator=(const __intrusive_ptr& __that) noexcept
158     {
159         return operator=(__intrusive_ptr(__that));
160     }
161 
162     __intrusive_ptr&
163         operator=(__enable_intrusive_from_this<_Ty>* __that) noexcept
164     {
165         return operator=(__that ? __that->__intrusive_from_this()
166                                 : __intrusive_ptr());
167     }
168 
169     ~__intrusive_ptr()
170     {
171         __dec_ref_();
172     }
173 
174     void reset() noexcept
175     {
176         operator=({});
177     }
178 
179     void swap(__intrusive_ptr& __that) noexcept
180     {
181         std::swap(__data_, __that.__data_);
182     }
183 
184     _Ty* get() const noexcept
185     {
186         return &__data_->__value();
187     }
188 
189     _Ty* operator->() const noexcept
190     {
191         return &__data_->__value();
192     }
193 
194     _Ty& operator*() const noexcept
195     {
196         return __data_->__value();
197     }
198 
199     explicit operator bool() const noexcept
200     {
201         return __data_ != nullptr;
202     }
203 
204     bool operator!() const noexcept
205     {
206         return __data_ == nullptr;
207     }
208 
209     bool operator==(const __intrusive_ptr&) const = default;
210 
211     bool operator==(std::nullptr_t) const noexcept
212     {
213         return __data_ == nullptr;
214     }
215 };
216 
217 template <class _Ty>
218 __intrusive_ptr<_Ty>
219     __enable_intrusive_from_this<_Ty>::__intrusive_from_this() noexcept
220 {
221     auto* __data = (__control_block<_Ty>*)static_cast<_Ty*>(this);
222     __data->__inc_ref_();
223     return __intrusive_ptr<_Ty>{__data};
224 }
225 
226 template <class _Ty>
227 __intrusive_ptr<const _Ty>
228     __enable_intrusive_from_this<_Ty>::__intrusive_from_this() const noexcept
229 {
230     auto* __data = (__control_block<_Ty>*)static_cast<const _Ty*>(this);
231     __data->__inc_ref_();
232     return __intrusive_ptr<const _Ty>{__data};
233 }
234 
235 template <class _Ty>
236 void __enable_intrusive_from_this<_Ty>::__inc_ref() noexcept
237 {
238     auto* __data = (__control_block<_Ty>*)static_cast<_Ty*>(this);
239     __data->__inc_ref_();
240 }
241 
242 template <class _Ty>
243 void __enable_intrusive_from_this<_Ty>::__dec_ref() noexcept
244 {
245     auto* __data = (__control_block<_Ty>*)static_cast<_Ty*>(this);
246     __data->__dec_ref_();
247 }
248 
249 template <class _Ty>
250 struct __make_intrusive_t
251 {
252     template <class... _Us>
253         requires constructible_from<_Ty, _Us...>
254     __intrusive_ptr<_Ty> operator()(_Us&&... __us) const
255     {
256         using _UncvTy = std::remove_cv_t<_Ty>;
257         return __intrusive_ptr<_Ty>{
258             ::new __control_block<_UncvTy>{(_Us&&)__us...}};
259     }
260 };
261 } // namespace __ptr
262 
263 using __ptr::__enable_intrusive_from_this;
264 using __ptr::__intrusive_ptr;
265 template <class _Ty>
266 inline constexpr __ptr::__make_intrusive_t<_Ty> __make_intrusive{};
267 
268 } // namespace stdexec
269