Serenity Operating System
1/*
2 * Copyright (c) 2021, the SerenityOS developers.
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#pragma once
8
9#include <AK/Atomic.h>
10#include <Kernel/Arch/Processor.h>
11
12namespace Kernel {
13
14template<typename AtomicRefCountType>
15class AtomicEdgeAction {
16public:
17 template<typename FirstRefAction>
18 bool ref(FirstRefAction first_ref_action)
19 {
20 AtomicRefCountType expected = 0;
21 AtomicRefCountType desired = (1 << 1) | 1;
22 // Least significant bit indicates we're busy protecting/unprotecting
23 for (;;) {
24 if (m_atomic_ref_count.compare_exchange_strong(expected, desired, AK::memory_order_relaxed))
25 break;
26
27 Processor::wait_check();
28
29 expected &= ~1;
30 desired = expected + (1 << 1);
31 VERIFY(desired > expected);
32 if (expected == 0)
33 desired |= 1;
34 }
35
36 atomic_thread_fence(AK::memory_order_acquire);
37
38 if (expected == 0) {
39 first_ref_action();
40
41 // drop the busy flag
42 m_atomic_ref_count.store(desired & ~1, AK::memory_order_release);
43 return true;
44 }
45 return false;
46 }
47
48 template<typename LastRefAction>
49 bool unref(LastRefAction last_ref_action)
50 {
51 AtomicRefCountType expected = 1 << 1;
52 AtomicRefCountType desired = (1 << 1) | 1;
53 // Least significant bit indicates we're busy protecting/unprotecting
54 for (;;) {
55 if (m_atomic_ref_count.compare_exchange_strong(expected, desired, AK::memory_order_relaxed))
56 break;
57
58 Processor::wait_check();
59
60 expected &= ~1;
61 VERIFY(expected != 0); // Someone should always have at least one reference
62
63 if (expected == 1 << 1) {
64 desired = (1 << 1) | 1;
65 } else {
66 desired = expected - (1 << 1);
67 VERIFY(desired < expected);
68 }
69 }
70
71 AK::atomic_thread_fence(AK::memory_order_release);
72
73 if (expected == 1 << 1) {
74 last_ref_action();
75
76 // drop the busy flag and release reference
77 m_atomic_ref_count.store(0, AK::memory_order_release);
78 return true;
79 }
80 return false;
81 }
82
83private:
84 Atomic<AtomicRefCountType> m_atomic_ref_count { 0 };
85};
86
87}