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/AtomicRefCounted.h>
12#include <AK/StdLibExtras.h>
13#include <Kernel/Arch/Processor.h>
14#include <Kernel/Library/LockRefPtr.h>
15#include <Kernel/ScopedCritical.h>
16
17namespace AK {
18
19template<typename T>
20class LockWeakable;
21template<typename T>
22class LockWeakPtr;
23
24class LockWeakLink final : public AtomicRefCounted<LockWeakLink> {
25 template<typename T>
26 friend class LockWeakable;
27 template<typename T>
28 friend class LockWeakPtr;
29
30public:
31 template<typename T, typename PtrTraits = LockRefPtrTraits<T>>
32 LockRefPtr<T, PtrTraits> strong_ref() const
33 requires(IsBaseOf<AtomicRefCountedBase, T>)
34 {
35 LockRefPtr<T, PtrTraits> ref;
36
37 {
38 // We don't want to be preempted while we are trying to obtain
39 // a strong reference
40 Kernel::ScopedCritical critical;
41 if (!(m_consumers.fetch_add(1u << 1, AK::MemoryOrder::memory_order_acquire) & 1u)) {
42 T* ptr = (T*)m_ptr.load(AK::MemoryOrder::memory_order_acquire);
43 if (ptr && ptr->try_ref())
44 ref = adopt_lock_ref(*ptr);
45 }
46 m_consumers.fetch_sub(1u << 1, AK::MemoryOrder::memory_order_release);
47 }
48
49 return ref;
50 }
51
52 template<typename T>
53 T* unsafe_ptr() const
54 {
55 if (m_consumers.load(AK::MemoryOrder::memory_order_relaxed) & 1u)
56 return nullptr;
57 // NOTE: This may return a non-null pointer even if revocation
58 // has been triggered as there is a possible race! But it's "unsafe"
59 // anyway because we return a raw pointer without ensuring a
60 // reference...
61 return (T*)m_ptr.load(AK::MemoryOrder::memory_order_acquire);
62 }
63
64 bool is_null() const
65 {
66 return unsafe_ptr<void>() == nullptr;
67 }
68
69 void revoke()
70 {
71 auto current_consumers = m_consumers.fetch_or(1u, AK::MemoryOrder::memory_order_relaxed);
72 VERIFY(!(current_consumers & 1u));
73 // We flagged revocation, now wait until everyone trying to obtain
74 // a strong reference is done
75 while (current_consumers > 0) {
76 Kernel::Processor::wait_check();
77 current_consumers = m_consumers.load(AK::MemoryOrder::memory_order_acquire) & ~1u;
78 }
79 // No one is trying to use it (anymore)
80 m_ptr.store(nullptr, AK::MemoryOrder::memory_order_release);
81 }
82
83private:
84 template<typename T>
85 explicit LockWeakLink(T& weakable)
86 : m_ptr(&weakable)
87 {
88 }
89 mutable Atomic<void*> m_ptr;
90 mutable Atomic<unsigned> m_consumers; // LSB indicates revocation in progress
91};
92
93template<typename T>
94class LockWeakable {
95private:
96 class Link;
97
98public:
99 template<typename U = T>
100 ErrorOr<LockWeakPtr<U>> try_make_weak_ptr() const;
101
102protected:
103 LockWeakable() = default;
104
105 ~LockWeakable()
106 {
107 m_being_destroyed.store(true, AK::MemoryOrder::memory_order_release);
108 revoke_weak_ptrs();
109 }
110
111 void revoke_weak_ptrs()
112 {
113 if (auto link = move(m_link))
114 link->revoke();
115 }
116
117private:
118 mutable LockRefPtr<LockWeakLink> m_link;
119 Atomic<bool> m_being_destroyed { false };
120};
121
122}
123
124using AK::LockWeakable;