/src/serenity/AK/AtomicRefCounted.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2018-2022, Andreas Kling <kling@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #pragma once |
8 | | |
9 | | #include <AK/Assertions.h> |
10 | | #include <AK/Atomic.h> |
11 | | #include <AK/Checked.h> |
12 | | #include <AK/Noncopyable.h> |
13 | | #include <AK/Platform.h> |
14 | | |
15 | | namespace AK { |
16 | | |
17 | | class AtomicRefCountedBase { |
18 | | AK_MAKE_NONCOPYABLE(AtomicRefCountedBase); |
19 | | AK_MAKE_NONMOVABLE(AtomicRefCountedBase); |
20 | | |
21 | | public: |
22 | | using RefCountType = unsigned int; |
23 | | using AllowOwnPtr = FalseType; |
24 | | |
25 | | void ref() const |
26 | 45 | { |
27 | 45 | auto old_ref_count = m_ref_count.fetch_add(1, AK::MemoryOrder::memory_order_relaxed); |
28 | 45 | VERIFY(old_ref_count > 0); |
29 | 45 | VERIFY(!Checked<RefCountType>::addition_would_overflow(old_ref_count, 1)); |
30 | 45 | } |
31 | | |
32 | | [[nodiscard]] bool try_ref() const |
33 | 0 | { |
34 | 0 | RefCountType expected = m_ref_count.load(AK::MemoryOrder::memory_order_relaxed); |
35 | 0 | for (;;) { |
36 | 0 | if (expected == 0) |
37 | 0 | return false; |
38 | 0 | VERIFY(!Checked<RefCountType>::addition_would_overflow(expected, 1)); |
39 | 0 | if (m_ref_count.compare_exchange_strong(expected, expected + 1, AK::MemoryOrder::memory_order_acquire)) |
40 | 0 | return true; |
41 | 0 | } |
42 | 0 | } |
43 | | |
44 | | [[nodiscard]] RefCountType ref_count() const |
45 | 0 | { |
46 | 0 | return m_ref_count.load(AK::MemoryOrder::memory_order_relaxed); |
47 | 0 | } |
48 | | |
49 | | protected: |
50 | 45 | AtomicRefCountedBase() = default; |
51 | | ~AtomicRefCountedBase() |
52 | 45 | { |
53 | 45 | VERIFY(m_ref_count.load(AK::MemoryOrder::memory_order_relaxed) == 0); |
54 | 45 | } |
55 | | |
56 | | RefCountType deref_base() const |
57 | 90 | { |
58 | 90 | auto old_ref_count = m_ref_count.fetch_sub(1, AK::MemoryOrder::memory_order_acq_rel); |
59 | 90 | VERIFY(old_ref_count > 0); |
60 | 90 | return old_ref_count - 1; |
61 | 90 | } |
62 | | |
63 | | mutable Atomic<RefCountType> m_ref_count { 1 }; |
64 | | }; |
65 | | |
66 | | template<typename T> |
67 | | class AtomicRefCounted : public AtomicRefCountedBase { |
68 | | public: |
69 | | bool unref() const |
70 | 90 | { |
71 | 90 | auto* that = const_cast<T*>(static_cast<T const*>(this)); |
72 | 90 | auto new_ref_count = deref_base(); |
73 | 90 | if (new_ref_count == 0) { |
74 | | if constexpr (requires { that->will_be_destroyed(); }) |
75 | | that->will_be_destroyed(); |
76 | 45 | delete that; |
77 | 45 | return true; |
78 | 45 | } |
79 | 45 | return false; |
80 | 90 | } |
81 | | }; |
82 | | |
83 | | } |
84 | | |
85 | | #if USING_AK_GLOBALLY |
86 | | using AK::AtomicRefCounted; |
87 | | using AK::AtomicRefCountedBase; |
88 | | #endif |