Gaia-ECS v0.9.3
A simple and powerful entity component system
Loading...
Searching...
No Matches
futex.h
1#pragma once
2#include "gaia/config/config.h"
3#include "gaia/config/profiler.h"
4
5#include <atomic>
6#include <mutex>
7
8#include "gaia/core/utility.h"
9#include "gaia/mt/event.h"
10
11namespace gaia {
12 namespace mt {
13 namespace detail {
14 inline static constexpr uint32_t WaitMaskAll = 0x7FFFFFFF;
15 inline static constexpr uint32_t WaitMaskAny = ~0u;
16
18 FutexWaitNode* pNext = nullptr;
19 const std::atomic_uint32_t* pFutexValue = nullptr;
20 uint32_t waitMask = WaitMaskAny;
21 Event evt;
22 };
23
24 struct FutexBucket {
25 GAIA_PROF_MUTEX(std::mutex, mtx);
26 FutexWaitNode* pFirst = nullptr;
27
28 // Since there shouldn't be that many threads waiting at any one time, this seems like a good
29 // number of hash table buckets. Making it prime number for better spread.
30 static constexpr uint32_t BUCKET_SIZE = 37;
31
32 static FutexBucket& get(const std::atomic_uint32_t* pFutexValue) {
33 static FutexBucket s_buckets[BUCKET_SIZE];
34 return s_buckets[(uintptr_t(pFutexValue) >> 2) % BUCKET_SIZE];
35 }
36 };
37
38 inline thread_local FutexWaitNode t_WaitNode;
39
40 } // namespace detail
41
53 struct Futex {
54 enum class Result {
56 Change,
58 WakeUp
59 };
60
65 static Result wait(const std::atomic_uint32_t* pFutexValue, uint32_t expected, uint32_t waitMask) {
66 GAIA_PROF_SCOPE(futex::wait);
67
68 GAIA_ASSERT(waitMask != 0);
69
70 auto& bucket = detail::FutexBucket::get(pFutexValue);
71 auto& node = detail::t_WaitNode;
72 node.pFutexValue = pFutexValue;
73 node.waitMask = waitMask;
74
75 {
76 auto& mtx = GAIA_PROF_EXTRACT_MUTEX(bucket.mtx);
77 core::lock_scope lock(mtx);
78 GAIA_PROF_LOCK_MARK(bucket.mtx);
79
80 const uint32_t futexValue = pFutexValue->load(std::memory_order_relaxed);
81 if (futexValue != expected)
82 return Result::Change;
83
84 node.pNext = bucket.pFirst;
85 bucket.pFirst = &node;
86 }
87
88 node.evt.wait();
89 return Result::WakeUp;
90 }
91
96 static uint32_t
97 wake(const std::atomic_uint32_t* pFutexValue, uint32_t wakeCount, uint32_t wakeMask = detail::WaitMaskAny) {
98 GAIA_PROF_SCOPE(futex::wake);
99
100 GAIA_ASSERT(wakeMask != 0);
101
102 auto& bucket = detail::FutexBucket::get(pFutexValue);
103 auto& mtx = GAIA_PROF_EXTRACT_MUTEX(bucket.mtx);
104 core::lock_scope lock(mtx);
105 GAIA_PROF_LOCK_MARK(bucket.mtx);
106
107 uint32_t numAwoken = 0;
108 auto** ppNode = &bucket.pFirst;
109 for (auto* pNode = *ppNode; numAwoken < wakeCount && pNode != nullptr; pNode = *ppNode) {
110 if (pNode->pFutexValue == pFutexValue && (pNode->waitMask & wakeMask) != 0) {
111 ++numAwoken;
112
113 // Unlink the node
114 *ppNode = pNode->pNext;
115 pNode->pNext = nullptr;
116
117 pNode->evt.set();
118 } else {
119 ppNode = &pNode->pNext;
120 }
121 }
122
123 return numAwoken;
124 }
125 };
126 } // namespace mt
127} // namespace gaia
Definition event.h:18
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9
Definition utility.h:123
An implementation of a simple futex (fast userspace mutex). Only wait and wake are implemented.
Definition futex.h:53
static Result wait(const std::atomic_uint32_t *pFutexValue, uint32_t expected, uint32_t waitMask)
Suspends the caller on the futex while its value remains expected.
Definition futex.h:65
static uint32_t wake(const std::atomic_uint32_t *pFutexValue, uint32_t wakeCount, uint32_t wakeMask=detail::WaitMaskAny)
Wakes up to wakeCount waiters whose waitMask matches wakeMask.
Definition futex.h:97
Result
Definition futex.h:54
@ WakeUp
Futex woken up as a result of wake()
@ Change
Futex value didn't match the expected one.
Definition futex.h:24
Definition futex.h:17