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 namespace stdexec 26 { 27 namespace __ptr 28 { 29 template <class _Ty> 30 struct __make_intrusive_t; 31 32 template <class _Ty> 33 struct __enable_intrusive_from_this; 34 35 template <class _Ty> 36 struct __control_block 37 { 38 alignas(_Ty) unsigned char __value_[sizeof(_Ty)]; 39 std::atomic<unsigned long> __refcount_; 40 41 template <class... _Us> 42 explicit __control_block(_Us&&... __us) noexcept(noexcept(_Ty{ 43 __declval<_Us>()...})) : 44 __refcount_(1u) 45 { 46 // Construct the value *after* the initialization of the 47 // atomic in case the constructor of _Ty calls 48 // __intrusive_from_this() (which increments the atomic): 49 ::new ((void*)__value_) _Ty{(_Us&&)__us...}; 50 } 51 52 ~__control_block() 53 { 54 __value().~_Ty(); 55 } 56 57 _Ty& __value() const noexcept 58 { 59 return *(_Ty*)__value_; 60 } 61 }; 62 63 template <class _Ty> 64 class __intrusive_ptr 65 { 66 using _UncvTy = std::remove_cv_t<_Ty>; 67 friend struct __make_intrusive_t<_Ty>; 68 friend struct __enable_intrusive_from_this<_UncvTy>; 69 70 __control_block<_UncvTy>* __data_{nullptr}; 71 72 explicit __intrusive_ptr(__control_block<_UncvTy>* __data) noexcept : 73 __data_(__data) 74 {} 75 76 void __addref_() noexcept 77 { 78 if (__data_) 79 { 80 __data_->__refcount_.fetch_add(1, std::memory_order_relaxed); 81 } 82 } 83 84 void __release_() noexcept 85 { 86 if (__data_ && 87 1u == __data_->__refcount_.fetch_sub(1, std::memory_order_release)) 88 { 89 std::atomic_thread_fence(std::memory_order_acquire); 90 delete __data_; 91 } 92 } 93 94 public: 95 __intrusive_ptr() = default; 96 97 __intrusive_ptr(__intrusive_ptr&& __that) noexcept : 98 __data_(std::exchange(__that.__data_, nullptr)) 99 {} 100 101 __intrusive_ptr(const __intrusive_ptr& __that) noexcept : 102 __data_(__that.__data_) 103 { 104 __addref_(); 105 } 106 107 __intrusive_ptr& operator=(__intrusive_ptr&& __that) noexcept 108 { 109 [[maybe_unused]] __intrusive_ptr __old{ 110 std::exchange(__data_, std::exchange(__that.__data_, nullptr))}; 111 return *this; 112 } 113 114 __intrusive_ptr& operator=(const __intrusive_ptr& __that) noexcept 115 { 116 return operator=(__intrusive_ptr(__that)); 117 } 118 119 ~__intrusive_ptr() 120 { 121 __release_(); 122 } 123 124 void reset() noexcept 125 { 126 operator=({}); 127 } 128 129 void swap(__intrusive_ptr& __that) noexcept 130 { 131 std::swap(__data_, __that.__data_); 132 } 133 134 _Ty* get() const noexcept 135 { 136 return &__data_->__value(); 137 } 138 139 _Ty* operator->() const noexcept 140 { 141 return &__data_->__value(); 142 } 143 144 _Ty& operator*() const noexcept 145 { 146 return __data_->__value(); 147 } 148 149 explicit operator bool() const noexcept 150 { 151 return __data_ != nullptr; 152 } 153 154 bool operator!() const noexcept 155 { 156 return __data_ == nullptr; 157 } 158 159 bool operator==(const __intrusive_ptr&) const = default; 160 161 bool operator==(std::nullptr_t) const noexcept 162 { 163 return __data_ == nullptr; 164 } 165 }; 166 167 template <class _Ty> 168 struct __enable_intrusive_from_this 169 { 170 __intrusive_ptr<_Ty> __intrusive_from_this() noexcept 171 { 172 static_assert(0 == offsetof(__control_block<_Ty>, __value_)); 173 _Ty* __this = static_cast<_Ty*>(this); 174 __intrusive_ptr<_Ty> __p{(__control_block<_Ty>*)__this}; 175 __p.__addref_(); 176 return __p; 177 } 178 179 __intrusive_ptr<const _Ty> __intrusive_from_this() const noexcept 180 { 181 static_assert(0 == offsetof(__control_block<_Ty>, __value_)); 182 const _Ty* __this = static_cast<const _Ty*>(this); 183 __intrusive_ptr<const _Ty> __p{(__control_block<_Ty>*)__this}; 184 __p.__addref_(); 185 return __p; 186 } 187 }; 188 189 template <class _Ty> 190 struct __make_intrusive_t 191 { 192 template <class... _Us> 193 requires constructible_from<_Ty, _Us...> 194 __intrusive_ptr<_Ty> operator()(_Us&&... __us) const 195 { 196 using _UncvTy = std::remove_cv_t<_Ty>; 197 return __intrusive_ptr<_Ty>{ 198 ::new __control_block<_UncvTy>{(_Us&&)__us...}}; 199 } 200 }; 201 } // namespace __ptr 202 203 using __ptr::__enable_intrusive_from_this; 204 using __ptr::__intrusive_ptr; 205 template <class _Ty> 206 inline constexpr __ptr::__make_intrusive_t<_Ty> __make_intrusive{}; 207 208 } // namespace stdexec 209