Serenity Operating System
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
15namespace AK {
16
17class AtomicRefCountedBase {
18 AK_MAKE_NONCOPYABLE(AtomicRefCountedBase);
19 AK_MAKE_NONMOVABLE(AtomicRefCountedBase);
20
21public:
22 using RefCountType = unsigned int;
23 using AllowOwnPtr = FalseType;
24
25 void ref() const
26 {
27 auto old_ref_count = m_ref_count.fetch_add(1, AK::MemoryOrder::memory_order_relaxed);
28 VERIFY(old_ref_count > 0);
29 VERIFY(!Checked<RefCountType>::addition_would_overflow(old_ref_count, 1));
30 }
31
32 [[nodiscard]] bool try_ref() const
33 {
34 RefCountType expected = m_ref_count.load(AK::MemoryOrder::memory_order_relaxed);
35 for (;;) {
36 if (expected == 0)
37 return false;
38 VERIFY(!Checked<RefCountType>::addition_would_overflow(expected, 1));
39 if (m_ref_count.compare_exchange_strong(expected, expected + 1, AK::MemoryOrder::memory_order_acquire))
40 return true;
41 }
42 }
43
44 [[nodiscard]] RefCountType ref_count() const
45 {
46 return m_ref_count.load(AK::MemoryOrder::memory_order_relaxed);
47 }
48
49protected:
50 AtomicRefCountedBase() = default;
51 ~AtomicRefCountedBase()
52 {
53 VERIFY(m_ref_count.load(AK::MemoryOrder::memory_order_relaxed) == 0);
54 }
55
56 RefCountType deref_base() const
57 {
58 auto old_ref_count = m_ref_count.fetch_sub(1, AK::MemoryOrder::memory_order_acq_rel);
59 VERIFY(old_ref_count > 0);
60 return old_ref_count - 1;
61 }
62
63 mutable Atomic<RefCountType> m_ref_count { 1 };
64};
65
66template<typename T>
67class AtomicRefCounted : public AtomicRefCountedBase {
68public:
69 bool unref() const
70 {
71 auto* that = const_cast<T*>(static_cast<T const*>(this));
72 auto new_ref_count = deref_base();
73 if (new_ref_count == 0) {
74 if constexpr (requires { that->will_be_destroyed(); })
75 that->will_be_destroyed();
76 delete that;
77 return true;
78 }
79 return false;
80 }
81};
82
83}
84
85#if USING_AK_GLOBALLY
86using AK::AtomicRefCounted;
87using AK::AtomicRefCountedBase;
88#endif