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