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