1 #pragma once
2
3 /// \file AutoPtr.h
4 /// \brief Simplified implementation of std::unique_ptr, using only default deleter.
5 /// \author Pavel Sevecek (sevecek at sirrah.troja.mff.cuni.cz)
6 /// \date 2016-2021
7
8 #include "objects/wrappers/AutoPtr.h"
9 #include <atomic>
10
11 NAMESPACE_SPH_BEGIN
12
13 namespace Detail {
14 class ControlBlockHolder : public Polymorphic {
15 private:
16 std::atomic<int> useCnt;
17 std::atomic<int> weakCnt;
18
19 public:
ControlBlockHolder()20 INLINE ControlBlockHolder() {
21 useCnt = 1;
22 weakCnt = 1;
23 }
24
increaseUseCnt()25 INLINE int increaseUseCnt() {
26 const int cnt = ++useCnt;
27 SPH_ASSERT(cnt > 0);
28 return cnt;
29 }
30
getUseCount()31 INLINE int getUseCount() const {
32 return useCnt;
33 }
34
increaseWeakCnt()35 INLINE int increaseWeakCnt() {
36 const int cnt = ++weakCnt;
37 SPH_ASSERT(cnt > 0);
38 return cnt;
39 }
40
increaseUseCntIfNonzero()41 INLINE bool increaseUseCntIfNonzero() {
42 while (true) {
43 int cnt = useCnt;
44 if (cnt == 0 || useCnt.compare_exchange_strong(cnt, cnt + 1)) {
45 return cnt != 0;
46 }
47 }
48 }
49
decreaseUseCnt()50 INLINE void decreaseUseCnt() {
51 const int cnt = --useCnt;
52 SPH_ASSERT(cnt >= 0);
53 if (cnt == 0) {
54 this->deletePtr();
55 }
56 }
57
decreaseWeakCnt()58 INLINE void decreaseWeakCnt() {
59 const int cnt = --weakCnt;
60 SPH_ASSERT(cnt >= 0);
61 if (cnt == 0) {
62 this->deleteBlock();
63 }
64 }
65
66 virtual void* getPtr() = 0;
67
68 virtual void deletePtr() = 0;
69
deleteBlock()70 INLINE void deleteBlock() {
71 alignedDelete(this);
72 }
73 };
74
75 template <typename T>
76 class ControlBlock : public ControlBlockHolder {
77 private:
78 T* ptr;
79
80 public:
ControlBlock(T * ptr)81 ControlBlock(T* ptr)
82 : ptr(ptr) {}
83
getPtr()84 INLINE virtual void* getPtr() override {
85 SPH_ASSERT(ptr);
86 return ptr;
87 }
88
deletePtr()89 virtual void deletePtr() override {
90 alignedDelete(ptr);
91 }
92 };
93 } // namespace Detail
94
95 template <typename T>
96 class SharedPtr {
97 template <typename>
98 friend class SharedPtr;
99 template <typename>
100 friend class WeakPtr;
101 friend class SharedToken;
102 friend class WeakToken;
103 template <typename>
104 friend class LockingPtr;
105
106 protected:
107 T* ptr;
108 Detail::ControlBlockHolder* block;
109
SharedPtr(T * ptr,Detail::ControlBlockHolder * block)110 explicit SharedPtr(T* ptr, Detail::ControlBlockHolder* block)
111 : ptr(ptr)
112 , block(block) {}
113
114 public:
SharedPtr()115 SharedPtr()
116 : ptr(nullptr)
117 , block(nullptr) {}
118
SharedPtr(T * ptr)119 explicit SharedPtr(T* ptr)
120 : ptr(ptr) {
121 if (ptr) {
122 block = alignedNew<Detail::ControlBlock<T>>(ptr);
123 } else {
124 block = nullptr;
125 }
126 setShareable(*this);
127 }
128
SharedPtr(const SharedPtr & other)129 SharedPtr(const SharedPtr& other)
130 : ptr(other.ptr) {
131 this->copyBlock(other);
132 }
133
134 template <typename T2>
SharedPtr(const SharedPtr<T2> & other)135 SharedPtr(const SharedPtr<T2>& other)
136 : ptr(other.ptr) {
137 this->copyBlock(other);
138 }
139
SharedPtr(SharedPtr && other)140 SharedPtr(SharedPtr&& other)
141 : ptr(other.ptr)
142 , block(other.block) {
143 other.ptr = nullptr;
144 other.block = nullptr;
145 }
146
147 template <typename T2>
SharedPtr(SharedPtr<T2> && other)148 SharedPtr(SharedPtr<T2>&& other)
149 : ptr(other.ptr)
150 , block(other.block) {
151 other.ptr = nullptr;
152 other.block = nullptr;
153 }
154
155 template <typename T2>
SharedPtr(AutoPtr<T2> && ptr)156 SharedPtr(AutoPtr<T2>&& ptr)
157 : SharedPtr(ptr.release()) {}
158
SharedPtr(std::nullptr_t)159 SharedPtr(std::nullptr_t)
160 : ptr(nullptr)
161 , block(nullptr) {}
162
163 SharedPtr& operator=(const SharedPtr& other) {
164 this->reset();
165 ptr = other.ptr;
166 this->copyBlock(other);
167 return *this;
168 }
169
170 template <typename T2>
171 SharedPtr& operator=(const SharedPtr<T2>& other) {
172 this->reset();
173 ptr = other.ptr;
174 this->copyBlock(other);
175 return *this;
176 }
177
178 SharedPtr& operator=(SharedPtr&& other) {
179 std::swap(ptr, other.ptr);
180 std::swap(block, other.block);
181 return *this;
182 }
183
184 template <typename T2>
185 SharedPtr& operator=(SharedPtr<T2>&& other) {
186 this->reset();
187 ptr = other.ptr;
188 block = other.block;
189 other.ptr = nullptr;
190 other.block = nullptr;
191 return *this;
192 }
193
194 SharedPtr& operator=(std::nullptr_t) {
195 this->reset();
196 return *this;
197 }
198
~SharedPtr()199 ~SharedPtr() {
200 this->reset();
201 }
202
203 INLINE T* operator->() const {
204 SPH_ASSERT(ptr);
205 return ptr;
206 }
207
208 INLINE T& operator*() const {
209 SPH_ASSERT(ptr);
210 return *ptr;
211 }
212
213 INLINE explicit operator bool() const {
214 return ptr != nullptr;
215 }
216
217 INLINE bool operator!() const {
218 return ptr == nullptr;
219 }
220
get()221 INLINE RawPtr<T> get() const {
222 return ptr;
223 }
224
reset()225 INLINE void reset() {
226 if (block) {
227 block->decreaseUseCnt();
228 block->decreaseWeakCnt();
229 block = nullptr;
230 }
231 ptr = nullptr;
232 }
233
release()234 INLINE T* release() {
235 if (block) {
236 block->deleteBlock();
237 block = nullptr;
238 return ptr;
239 } else {
240 return nullptr;
241 }
242 }
243
getUseCount()244 INLINE Size getUseCount() {
245 if (!block) {
246 return 0;
247 } else {
248 return block->getUseCount();
249 }
250 }
251
252 template <typename... TArgs>
decltype(auto)253 INLINE decltype(auto) operator()(TArgs&&... args) const {
254 SPH_ASSERT(ptr);
255 return (*ptr)(std::forward<TArgs>(args)...);
256 }
257
258 private:
259 template <typename T2>
copyBlock(const SharedPtr<T2> & other)260 INLINE void copyBlock(const SharedPtr<T2>& other) {
261 if (other.block) {
262 SPH_ASSERT(ptr != nullptr);
263 block = other.block;
264 block->increaseUseCnt();
265 block->increaseWeakCnt();
266 } else {
267 block = nullptr;
268 }
269 }
270 };
271
272
273 template <typename T>
274 bool operator==(const SharedPtr<T>& ptr, std::nullptr_t) {
275 return !ptr;
276 }
277
278 template <typename T>
279 bool operator==(std::nullptr_t, const SharedPtr<T>& ptr) {
280 return !ptr;
281 }
282
283 template <typename T>
284 bool operator!=(const SharedPtr<T>& ptr, std::nullptr_t) {
285 return bool(ptr);
286 }
287
288 template <typename T>
289 bool operator!=(std::nullptr_t, const SharedPtr<T>& ptr) {
290 return bool(ptr);
291 }
292
293 template <typename T>
294 bool operator==(const SharedPtr<T>& ptr1, const SharedPtr<T>& ptr2) {
295 return ptr1.get() == ptr2.get();
296 }
297
298 template <typename T>
299 bool operator!=(const SharedPtr<T>& ptr1, const SharedPtr<T>& ptr2) {
300 return ptr1.get() != ptr2.get();
301 }
302
303 template <typename T>
304 bool operator<(const SharedPtr<T>& ptr1, const SharedPtr<T>& ptr2) {
305 return ptr1.get() < ptr2.get();
306 }
307
308 template <typename T>
309 class WeakPtr {
310 private:
311 Detail::ControlBlockHolder* block;
312
313 public:
WeakPtr()314 WeakPtr()
315 : block(nullptr) {}
316
WeakPtr(const WeakPtr & other)317 WeakPtr(const WeakPtr& other)
318 : block(other.block) {
319 if (block) {
320 block->increaseWeakCnt();
321 }
322 }
323
324 template <typename T2>
WeakPtr(const WeakPtr<T2> & other)325 WeakPtr(const WeakPtr<T2>& other)
326 : block(other.block) {
327 if (block) {
328 block->increaseWeakCnt();
329 }
330 }
331
332 template <typename T2>
WeakPtr(const SharedPtr<T2> & ptr)333 WeakPtr(const SharedPtr<T2>& ptr)
334 : block(ptr.block) {
335 if (block) {
336 block->increaseWeakCnt();
337 }
338 }
339
WeakPtr(std::nullptr_t)340 WeakPtr(std::nullptr_t)
341 : block(nullptr) {}
342
~WeakPtr()343 ~WeakPtr() {
344 this->reset();
345 }
346
347 WeakPtr& operator=(const WeakPtr& other) {
348 this->reset();
349 block = other.block;
350 if (block) {
351 block->increaseWeakCnt();
352 }
353 return *this;
354 }
355
356 template <typename T2>
357 WeakPtr& operator=(const WeakPtr<T2>& other) {
358 this->reset();
359 block = other.block;
360 if (block) {
361 block->increaseWeakCnt();
362 }
363 return *this;
364 }
365
366 WeakPtr& operator=(std::nullptr_t) {
367 this->reset();
368 return *this;
369 }
370
lock()371 SharedPtr<T> lock() const {
372 if (block && block->increaseUseCntIfNonzero()) {
373 SharedPtr<T> ptr;
374 ptr.block = block;
375 ptr.ptr = static_cast<T*>(block->getPtr());
376 block->increaseWeakCnt();
377 return ptr;
378 } else {
379 return nullptr;
380 }
381 }
382
reset()383 INLINE void reset() {
384 if (block) {
385 block->decreaseWeakCnt();
386 block = nullptr;
387 }
388 }
389
getUseCount()390 INLINE Size getUseCount() const {
391 if (!block) {
392 return 0;
393 } else {
394 return block->getUseCount();
395 }
396 }
397
398 INLINE explicit operator bool() const {
399 return this->getUseCount() > 0;
400 }
401
402 INLINE bool operator!() const {
403 return this->getUseCount() == 0;
404 }
405 };
406
407 template <typename T, typename... TArgs>
makeShared(TArgs &&...args)408 INLINE SharedPtr<T> makeShared(TArgs&&... args) {
409 return SharedPtr<T>(alignedNew<T>(std::forward<TArgs>(args)...));
410 }
411
412 template <typename T>
413 class Shareable {
414 private:
415 WeakPtr<T> ptr;
416
417 public:
418 using SHAREABLE_TAG = void;
419
setWeakPtr(const WeakPtr<T> & weakPtr)420 void setWeakPtr(const WeakPtr<T>& weakPtr) {
421 ptr = weakPtr;
422 }
423
sharedFromThis()424 SharedPtr<T> sharedFromThis() const {
425 SharedPtr<T> sharedPtr = ptr.lock();
426 SPH_ASSERT(sharedPtr);
427 return sharedPtr;
428 }
429
weakFromThis()430 WeakPtr<T> weakFromThis() const {
431 return ptr;
432 }
433 };
434
435 /// \todo this is a weird solution, it must be doable with more standard approach
436
437 template <typename T, typename TEnabler = void>
438 struct IsShareable {
439 static constexpr bool value = false;
440 };
441 template <typename T>
442 struct IsShareable<T, typename T::SHAREABLE_TAG> {
443 static constexpr bool value = true;
444 };
445
446 template <typename T>
447 std::enable_if_t<IsShareable<T>::value> setShareable(const SharedPtr<T>& ptr) {
448 ptr->setWeakPtr(ptr);
449 }
450
451 template <typename T>
452 std::enable_if_t<!IsShareable<T>::value> setShareable(const SharedPtr<T>& UNUSED(ptr)) {
453 // do nothing
454 }
455
456 NAMESPACE_SPH_END
457