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