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