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