The Pedigree Project 0.1
WaitQueue.h
1/*
2 * Copyright (c) 2026, Pedigree Developers
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted.
6 */
7
8#ifndef PEDIGREE_KERNEL_PROCESS_WAITQUEUE_H
9#define PEDIGREE_KERNEL_PROCESS_WAITQUEUE_H
10#include "pedigree/kernel/Spinlock.h"
11#include "pedigree/kernel/compiler.h"
12#include "pedigree/kernel/processor/types.h"
13
14#include <config.h>
15
16class Mutex;
18class Thread;
19
28class EXPORTED_PUBLIC WaitQueue {
29 private:
30 struct Waiter;
31
32 public:
33 using StackDiscardCleanup = void (*)(void*);
34 using AbandonCallback = StackDiscardCleanup; // Source compatibility.
35
36 enum class WakeReason {
37 Waiting,
38 Signalled,
39 Event,
40 Unwinding,
41 Terminating,
42 Spurious,
43 };
44
45 struct Channel {
50 Channel(const void* owner = nullptr, uintptr_t value = 0) : owner(owner), value(value) {}
51
52 bool operator==(const Channel& other) const {
53 return owner == other.owner && value == other.value;
54 }
55
56 const void* owner;
57 uintptr_t value;
58 };
59
60 class EXPORTED_PUBLIC Guard {
61 public:
62 Guard(Guard&& other) noexcept;
63 ~Guard();
64
70 void prepareToWait();
71
75 MUST_USE_RESULT WakeReason wait(const Channel& channel = Channel(), size_t debugState = 0,
76 uintptr_t debugAddress = 0,
77 StackDiscardCleanup onStackDiscard = nullptr,
78 void* stackDiscardContext = nullptr);
79
86 MUST_USE_RESULT WakeReason waitForCompletion(const Channel& channel = Channel(),
87 size_t debugState = 0, uintptr_t debugAddress = 0);
88
94 MUST_USE_RESULT WakeReason waitAndUnlock(Mutex& mutex, const Channel& channel = Channel(),
95 size_t debugState = 0, uintptr_t debugAddress = 0,
96 StackDiscardCleanup onStackDiscard = nullptr,
97 void* stackDiscardContext = nullptr);
98
105 MUST_USE_RESULT WakeReason waitAndUnlockForCompletion(Mutex& mutex,
106 const Channel& channel = Channel(),
107 size_t debugState = 0,
108 uintptr_t debugAddress = 0);
109
110 bool wakeOne(WakeReason reason = WakeReason::Signalled, const Channel& channel = Channel());
111 size_t wakeAll(WakeReason reason = WakeReason::Signalled, const Channel& channel = Channel());
112
114 size_t wakeAndRequeue(const Channel& source, size_t wakeCount, const Channel& destination,
115 size_t requeueCount);
116
117 private:
118 friend class WaitQueue;
119 friend class PerProcessorScheduler;
120
121 explicit Guard(WaitQueue& queue);
122 NOT_COPYABLE_OR_ASSIGNABLE(Guard);
123
125 MUST_USE_RESULT WakeReason waitWithoutEventDispatch(const Channel& channel, size_t debugState,
126 uintptr_t debugAddress);
127 void queueSchedulerNotification(Waiter* waiter);
128 void release();
129
130 WaitQueue* m_Queue;
131 bool m_OwnsLock;
132 Waiter* m_pFirstReady;
133 Waiter* m_pLastReady;
134 };
135
136 WaitQueue();
137 ~WaitQueue();
138
139 MUST_USE_RESULT Guard acquire() {
140 return Guard(*this);
141 }
142
143 bool wakeOne(WakeReason reason = WakeReason::Signalled, const Channel& channel = Channel());
144 size_t wakeAll(WakeReason reason = WakeReason::Signalled, const Channel& channel = Channel());
145
151 size_t wakeAllIfWaiting(WakeReason reason = WakeReason::Signalled,
152 const Channel& channel = Channel());
153
154 size_t waiterCount();
155
156#if PEDIGREE_AFFINITY_TESTS
157 using ReadyPublicationHook = void (*)(Thread*);
158 static void setReadyPublicationHookForTest(Thread* target, ReadyPublicationHook hook);
159#endif
160
161#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
162 using BeforeBlockHook = void (*)(WaitQueue* queue, Thread* thread, const Channel& channel,
163 size_t debugState);
164
165 static void setBeforeBlockHook(BeforeBlockHook hook);
166#endif
167
168 private:
169 friend class Thread;
170
171 struct Waiter {
172 WaitQueue* loadQueue() const {
173 return __atomic_load_n(&queue, __ATOMIC_ACQUIRE);
174 }
175
176 void storeQueue(WaitQueue* value) {
177 __atomic_store_n(&queue, value, __ATOMIC_RELEASE);
178 }
179
180 WakeReason loadReason() const {
181 return static_cast<WakeReason>(__atomic_load_n(&reason, __ATOMIC_ACQUIRE));
182 }
183
184 void storeReason(WakeReason value) {
185 __atomic_store_n(&reason, static_cast<size_t>(value), __ATOMIC_RELEASE);
186 }
187
188 bool isQueued() const {
189 return __atomic_load_n(&queued, __ATOMIC_ACQUIRE);
190 }
191
192 void setQueued(bool value) {
193 __atomic_store_n(&queued, value, __ATOMIC_RELEASE);
194 }
195
196 Thread* thread = nullptr;
197 PerProcessorScheduler* scheduler = nullptr;
198 WaitQueue* queue = nullptr;
199 Channel channel;
200 size_t channelGeneration = 0;
201
202 void storeChannel(const Channel& value) {
203 __atomic_add_fetch(&channelGeneration, static_cast<size_t>(1), __ATOMIC_ACQ_REL);
204 __atomic_store_n(&channel.owner, value.owner, __ATOMIC_RELAXED);
205 __atomic_store_n(&channel.value, value.value, __ATOMIC_RELAXED);
206 __atomic_add_fetch(&channelGeneration, static_cast<size_t>(1), __ATOMIC_RELEASE);
207 }
208
209 bool snapshotChannel(Channel& value) const {
210 const size_t generation = __atomic_load_n(&channelGeneration, __ATOMIC_ACQUIRE);
211 if (generation & 1) {
212 return false;
213 }
214 value.owner = __atomic_load_n(&channel.owner, __ATOMIC_RELAXED);
215 value.value = __atomic_load_n(&channel.value, __ATOMIC_RELAXED);
216 __atomic_thread_fence(__ATOMIC_ACQUIRE);
217 return __atomic_load_n(&channelGeneration, __ATOMIC_RELAXED) == generation;
218 }
219 size_t stateLevel = 0;
220 size_t reason = static_cast<size_t>(WakeReason::Waiting);
221 bool queued = false;
222
223 // Legacy callback fields stay inert; lexical scopes own stack cleanup.
224 StackDiscardCleanup legacyOnAbandon = nullptr;
225 void* legacyAbandonContext = nullptr;
226 Waiter* previous = nullptr;
227 Waiter* next = nullptr;
228 Waiter* notificationNext = nullptr;
229 };
230
231 WakeReason wait(Guard& guard, Mutex* mutex, const Channel& channel, size_t debugState,
232 uintptr_t debugAddress, bool deferTerminal, bool dispatchEvents);
233 bool wakeOneLocked(Guard& guard, WakeReason reason, const Channel& channel);
234 size_t wakeAllLocked(Guard& guard, WakeReason reason, const Channel& channel);
235 size_t wakeAndRequeueLocked(Guard& guard, const Channel& source, size_t wakeCount,
236 const Channel& destination, size_t requeueCount);
237 bool completeWaiter(Guard& guard, Waiter* waiter, WakeReason reason);
238 static void publishReady(Waiter* waiter);
239 void removeWaiterLocked(Waiter* waiter);
240 void clearWaitIntentIfEmpty();
241 void cancel(Waiter* waiter, WakeReason reason);
242
243 Spinlock m_Lock;
244 Waiter* m_pFirstWaiter;
245 Waiter* m_pLastWaiter;
246 size_t m_WaiterCount;
247 bool m_WaitIntent;
248
249#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
250 static BeforeBlockHook m_BeforeBlockHook;
251#endif
252};
253
254#endif
Definition Event.h:49
Definition Mutex.h:56
bool operator==(const Iterator< originalT, Struct, FunctionPrev, FunctionNext, T1 > &x1, const Iterator< originalT, Struct, FunctionPrev, FunctionNext, T2 > &x2)
Definition Iterator.h:256
Channel(const void *owner=nullptr, uintptr_t value=0)
Definition WaitQueue.h:50