-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFutex.h
More file actions
106 lines (83 loc) · 2.81 KB
/
Futex.h
File metadata and controls
106 lines (83 loc) · 2.81 KB
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#pragma once
#include <linux/futex.h>
#include <spdlog/fmt/bundled/ostream.h>
#include <spdlog/spdlog.h>
#include <syscall.h>
#include <unistd.h>
#include <atomic>
#include <iostream>
#include "RobustList.h"
namespace libfutex {
inline static thread_local const uint32_t tid = static_cast<uint32_t>(gettid());
class Futex {
/**
* Pointer to the next futex in the list of futexes owned by the same thread.
* Must be the first field in the class.
*/
std::atomic<Futex*> next;
/**
* Pointer to the previous futex in the list of futexes owned by the same
* thread. Used to remove the futex from the list when it is destroyed.
*/
std::atomic<Futex*> prev;
/**
* Futex value.
* Reference:
* https://docs.kernel.org/locking/robust-futexes.html
* https://docs.kernel.org/locking/robust-futex-ABI.html
*/
std::atomic<uint32_t> val;
public:
Futex() = default;
explicit Futex(uint32_t val) : val(val) {
static_assert(offsetof(Futex, val) == FUTEX_OFFSET);
}
template <typename Fn>
void lock(Fn&& fn) {
// Set pending futex to the current one so that the kernel knows this futex
// might be locked by the thread.
rlist.head.list_op_pending = (robust_list*)this;
fn(val);
// Add the current futex to the robust list.
this->prev = (Futex*)&rlist.head.list;
this->next = (Futex*)rlist.head.list.next;
auto old = rlist.head.list.next;
rlist.head.list.next = (robust_list*)this;
if (old != &rlist.head.list) ((Futex*)old)->prev = this;
// Reset pending futex to nullptr when the lock is acquired.
rlist.head.list_op_pending = nullptr;
}
template <typename Fn>
void unlock(Fn&& fn) {
rlist.head.list_op_pending = (robust_list*)this;
// Remove the current futex from the robust list.
this->next.load()->next = this->prev.load();
this->prev.load()->next = this->next.load();
fn(val);
rlist.head.list_op_pending = nullptr;
}
[[nodiscard]] uint32_t get_val() const { return val.load(); }
/**
* A thread-local RobustList instance. The constructor is called when the
* thread starts to register the list to the kernel.
*/
inline static thread_local RobustList rlist;
friend std::ostream& operator<<(std::ostream& os, const Futex& f) {
uint32_t v = f.val.load();
uint32_t val_rest = v & static_cast<uint32_t>(~FUTEX_TID_MASK);
uint32_t val_tid = v & FUTEX_TID_MASK;
void* addr = (void*)&f.val;
void* p = f.prev.load();
void* n = f.next.load();
if (val_rest) {
os << fmt::format(
"Futex{{val = {} | {:#x}, &val = {}, prev = {}, next = {}}}", val_tid,
val_rest, addr, p, n);
} else {
os << fmt::format("Futex{{val = {}, &val = {}, prev = {}, next = {}}}",
val_tid, addr, p, n);
}
return os;
}
};
} // namespace libfutex