join 1.0
lightweight network framework library
Loading...
Searching...
No Matches
futex.hpp
Go to the documentation of this file.
1
25#ifndef JOIN_CORE_FUTEX_HPP
26#define JOIN_CORE_FUTEX_HPP
27
28// libjoin.
29#include <join/thread.hpp>
30#include <join/mutex.hpp>
31
32// Linux.
33#include <sys/syscall.h>
34#include <linux/futex.h>
35#include <unistd.h>
36#include <cerrno>
37
38// C++.
39#include <cstdlib>
40#include <atomic>
41
42#if defined(__SANITIZE_THREAD__)
43#define JOIN_TSAN 1
44#elif defined(__has_feature)
45#if __has_feature(thread_sanitizer)
46#define JOIN_TSAN 1
47#endif
48#endif
49
50#ifdef JOIN_TSAN
51#include <sanitizer/tsan_interface.h>
52#define JOIN_TSAN_ACQUIRE(addr) __tsan_acquire (addr)
53#define JOIN_TSAN_RELEASE(addr) __tsan_release (addr)
54#else
55#define JOIN_TSAN_ACQUIRE(addr)
56#define JOIN_TSAN_RELEASE(addr)
57#endif
58
59namespace join
60{
64 template <class LockPolicy>
66 {
67 public:
71 BasicFutex () noexcept
72 : _futex (0)
73 {
74 }
75
80 BasicFutex (const BasicFutex& other) = delete;
81
87 BasicFutex& operator= (const BasicFutex& other) = delete;
88
93 BasicFutex (BasicFutex&& other) = delete;
94
100 BasicFutex& operator= (BasicFutex&& other) = delete;
101
105 ~BasicFutex () = default;
106
110 void lock () noexcept
111 {
112 LockPolicy::lock (_futex, FUTEX_PRIVATE_FLAG);
113 }
114
119 bool tryLock () noexcept
120 {
121 return LockPolicy::tryLock (_futex);
122 }
123
127 void unlock () noexcept
128 {
129 LockPolicy::unlock (_futex, FUTEX_PRIVATE_FLAG);
130 }
131
136 std::atomic_uint32_t* handle () noexcept
137 {
138 return &_futex;
139 }
140
141 private:
143 alignas (4) std::atomic_uint32_t _futex;
144 };
145
149 template <class LockPolicy>
151 {
152 public:
157 : _futex (0)
158 {
159 }
160
165 BasicSharedFutex (const BasicSharedFutex& other) = delete;
166
173
179
186
190 ~BasicSharedFutex () = default;
191
195 void lock () noexcept
196 {
197 LockPolicy::lock (_futex, 0);
198 }
199
204 bool tryLock () noexcept
205 {
206 return LockPolicy::tryLock (_futex);
207 }
208
212 void unlock () noexcept
213 {
214 LockPolicy::unlock (_futex, 0);
215 }
216
221 std::atomic_uint32_t* handle () noexcept
222 {
223 return &_futex;
224 }
225
226 private:
228 alignas (4) std::atomic_uint32_t _futex;
229 };
230
235 {
236 public:
242 static void lock (std::atomic_uint32_t& futex, int flags) noexcept
243 {
244 uint32_t expected = 0;
245 if (futex.compare_exchange_strong (expected, 1, std::memory_order_acquire, std::memory_order_relaxed))
246 {
247 return;
248 }
249
250 do
251 {
252 if (expected == 2 ||
253 futex.compare_exchange_strong (expected, 2, std::memory_order_relaxed, std::memory_order_relaxed))
254 {
255 ::syscall (SYS_futex, reinterpret_cast<uint32_t*> (&futex), FUTEX_WAIT | flags, 2, nullptr, nullptr,
256 0);
257 }
258 expected = 0;
259 }
260 while (!futex.compare_exchange_strong (expected, 2, std::memory_order_acquire, std::memory_order_relaxed));
261 }
262
268 static bool tryLock (std::atomic_uint32_t& futex) noexcept
269 {
270 uint32_t expected = 0;
271 return futex.compare_exchange_strong (expected, 1, std::memory_order_acquire, std::memory_order_relaxed);
272 }
273
279 static void unlock (std::atomic_uint32_t& futex, int flags) noexcept
280 {
281 if (futex.exchange (0, std::memory_order_release) == 2)
282 {
283 ::syscall (SYS_futex, reinterpret_cast<uint32_t*> (&futex), FUTEX_WAKE | flags, 1, nullptr, nullptr, 0);
284 }
285 }
286 };
287
292 {
293 public:
299 static void lock (std::atomic_uint32_t& futex, int flags) noexcept
300 {
301 uint32_t expected = 0;
302 if (futex.compare_exchange_strong (expected, static_cast<uint32_t> (Thread::currentId ()),
303 std::memory_order_acquire, std::memory_order_relaxed))
304 {
305 return;
306 }
307
308 while (::syscall (SYS_futex, reinterpret_cast<uint32_t*> (&futex), FUTEX_LOCK_PI | flags, 0, nullptr,
309 nullptr, 0) == -1)
310 {
311 // LCOV_EXCL_START
312 if (errno != EAGAIN)
313 {
314 std::abort ();
315 }
316 // LCOV_EXCL_STOP
317 }
318
319 JOIN_TSAN_ACQUIRE (&futex);
320 }
321
327 static bool tryLock (std::atomic_uint32_t& futex) noexcept
328 {
329 uint32_t expected = 0;
330 return futex.compare_exchange_strong (expected, static_cast<uint32_t> (Thread::currentId ()),
331 std::memory_order_acquire, std::memory_order_relaxed);
332 }
333
339 static void unlock (std::atomic_uint32_t& futex, int flags) noexcept
340 {
341 uint32_t expected = static_cast<uint32_t> (Thread::currentId ());
342 if (futex.compare_exchange_strong (expected, 0, std::memory_order_release, std::memory_order_relaxed))
343 {
344 return;
345 }
346
347 JOIN_TSAN_RELEASE (&futex);
348 ::syscall (SYS_futex, reinterpret_cast<uint32_t*> (&futex), FUTEX_UNLOCK_PI | flags, 0, nullptr, nullptr,
349 0);
350 }
351 };
352
355
358}
359
360#endif
Futex-based mutex for intra-process locking.
Definition futex.hpp:66
BasicFutex(BasicFutex &&other)=delete
move constructor.
BasicFutex() noexcept
default constructor.
Definition futex.hpp:71
void lock() noexcept
lock the futex, blocking until it becomes available.
Definition futex.hpp:110
void unlock() noexcept
unlock the futex.
Definition futex.hpp:127
~BasicFutex()=default
destructor.
BasicFutex & operator=(const BasicFutex &other)=delete
copy assignment.
BasicFutex(const BasicFutex &other)=delete
copy constructor.
bool tryLock() noexcept
try to lock the futex without blocking.
Definition futex.hpp:119
std::atomic_uint32_t * handle() noexcept
get a pointer to the underlying futex word.
Definition futex.hpp:136
Futex-based mutex suitable for inter-process locking via shared memory.
Definition futex.hpp:151
bool tryLock() noexcept
try to lock the futex without blocking.
Definition futex.hpp:204
BasicSharedFutex(const BasicSharedFutex &other)=delete
copy constructor.
BasicSharedFutex(BasicSharedFutex &&other)=delete
move constructor.
BasicSharedFutex & operator=(const BasicSharedFutex &other)=delete
copy assignment.
std::atomic_uint32_t * handle() noexcept
get a pointer to the underlying futex word.
Definition futex.hpp:221
void lock() noexcept
lock the futex, blocking until it becomes available.
Definition futex.hpp:195
void unlock() noexcept
unlock the futex.
Definition futex.hpp:212
~BasicSharedFutex()=default
destructor.
BasicSharedFutex() noexcept
default constructor.
Definition futex.hpp:156
priority inheritance policy.
Definition futex.hpp:292
static void lock(std::atomic_uint32_t &futex, int flags) noexcept
lock the futex.
Definition futex.hpp:299
static bool tryLock(std::atomic_uint32_t &futex) noexcept
try to lock the futex.
Definition futex.hpp:327
static void unlock(std::atomic_uint32_t &futex, int flags) noexcept
unlock the futex.
Definition futex.hpp:339
no priority inheritance policy.
Definition futex.hpp:235
static void lock(std::atomic_uint32_t &futex, int flags) noexcept
lock the futex.
Definition futex.hpp:242
static bool tryLock(std::atomic_uint32_t &futex) noexcept
try to lock the futex.
Definition futex.hpp:268
static void unlock(std::atomic_uint32_t &futex, int flags) noexcept
unlock the futex.
Definition futex.hpp:279
static pid_t currentId() noexcept
get calling thread id.
Definition thread.hpp:346
#define JOIN_TSAN_RELEASE(addr)
Definition futex.hpp:56
#define JOIN_TSAN_ACQUIRE(addr)
Definition futex.hpp:55
Definition acceptor.hpp:32