-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathqueue_spinlock.h
76 lines (59 loc) · 1.81 KB
/
queue_spinlock.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
//
// Created by konstantin on 28.07.24.
//
#pragma once
#include <atomic>
namespace NSync {
class QueueSpinLock final {
public:
class Guard final {
public:
explicit Guard(QueueSpinLock& host) : host(host) { host.Acquire(this); }
~Guard() {
if (is_owner.load(std::memory_order_acquire)) Release();
}
void Release() {
host.Release(this);
is_owner.store(false, std::memory_order_release);
}
void SetOwner() { is_owner.store(true, std::memory_order_release); }
void SetNext(Guard* guard) { next.store(guard, std::memory_order_release); }
bool IsOwner() const {
return is_owner.load(std::memory_order_acquire);
}
bool HasNext() const { return next.load(std::memory_order_acquire) != nullptr; }
void SetNextOwner() { next.load()->SetOwner(); }
private:
QueueSpinLock& host;
std::atomic<Guard*> next{};
std::atomic<bool> is_owner{};
};
private:
void Acquire(Guard* guard) {
auto ancestor = tail_.exchange(guard/*, std::memory_order_acquire*/);
if (ancestor == nullptr) {
guard->SetOwner();
return;
}
ancestor->SetNext(guard);
while (!guard->IsOwner()) {
}
}
void Release(Guard* guard) {
if (guard->HasNext()) {
guard->SetNextOwner();
return;
}
Guard* old_guard = guard;
while (!tail_.compare_exchange_weak(old_guard, nullptr/*,
std::memory_order_release*/)) {
if (guard->HasNext()) {
guard->SetNextOwner();
return;
}
old_guard = guard;
}
}
std::atomic<Guard*> tail_{};
};
} // namespace NSync