8#include "pedigree/kernel/Log.h"
9#include "pedigree/kernel/process/Mutex.h"
10#include "pedigree/kernel/process/PerProcessorScheduler.h"
11#include "pedigree/kernel/process/Thread.h"
12#include "pedigree/kernel/process/WaitQueue.h"
13#include "pedigree/kernel/processor/Processor.h"
14#include "pedigree/kernel/processor/ProcessorInformation.h"
15#include "pedigree/kernel/utilities/Iterator.h"
16#include "pedigree/kernel/utilities/assert.h"
18#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
19WaitQueue::BeforeBlockHook WaitQueue::m_BeforeBlockHook =
nullptr;
23#if PEDIGREE_AFFINITY_TESTS
24Thread* g_ReadyPublicationTarget =
nullptr;
25WaitQueue::ReadyPublicationHook g_ReadyPublicationHook =
nullptr;
29 return WaitQueue::WakeReason::Unwinding;
32 return WaitQueue::WakeReason::Terminating;
34 FATAL(
"Unknown terminal WaitQueue unwind state.");
35 return WaitQueue::WakeReason::Spurious;
40 : m_Queue(&queue), m_OwnsLock(false), m_pFirstReady(nullptr), m_pLastReady(nullptr) {
45 m_Queue->m_Lock.acquire();
49WaitQueue::Guard::Guard(Guard&& other) noexcept
50 : m_Queue(other.m_Queue),
51 m_OwnsLock(other.m_OwnsLock),
52 m_pFirstReady(other.m_pFirstReady),
53 m_pLastReady(other.m_pLastReady) {
54 other.m_Queue =
nullptr;
55 other.m_OwnsLock =
false;
56 other.m_pFirstReady =
nullptr;
57 other.m_pLastReady =
nullptr;
60WaitQueue::Guard::~Guard() {
64void WaitQueue::Guard::release() {
65 if (m_Queue && m_OwnsLock) {
66 m_Queue->clearWaitIntentIfEmpty();
67 m_Queue->m_Lock.release();
73 while (m_pFirstReady) {
74 Waiter*
waiter = m_pFirstReady;
75 m_pFirstReady =
waiter->notificationNext;
76 waiter->notificationNext =
nullptr;
77 WaitQueue::publishReady(
waiter);
79 m_pLastReady =
nullptr;
88 __atomic_store_n(&m_Queue->m_WaitIntent,
true, __ATOMIC_SEQ_CST);
92void WaitQueue::Guard::queueSchedulerNotification(
Waiter*
waiter) {
94 assert(!
waiter->notificationNext);
96 m_pLastReady->notificationNext =
waiter;
104 uintptr_t debugAddress,
105 StackDiscardCleanup onStackDiscard,
106 void* stackDiscardContext) {
109 return WakeReason::Spurious;
113 return m_Queue->wait(*
this,
nullptr, channel, debugState, debugAddress,
false,
true);
117 uintptr_t debugAddress) {
120 return WakeReason::Spurious;
123 return m_Queue->wait(*
this,
nullptr, channel, debugState, debugAddress,
true,
true);
128 uintptr_t debugAddress) {
131 return WakeReason::Spurious;
134 return m_Queue->wait(*
this,
nullptr, channel, debugState, debugAddress,
false,
false);
138 size_t debugState, uintptr_t debugAddress,
139 StackDiscardCleanup onStackDiscard,
140 void* stackDiscardContext) {
143 return WakeReason::Spurious;
147 return m_Queue->wait(*
this, &mutex, channel, debugState, debugAddress,
false,
true);
153 uintptr_t debugAddress) {
156 return WakeReason::Spurious;
159 return m_Queue->wait(*
this, &mutex, channel, debugState, debugAddress,
true,
true);
162bool WaitQueue::Guard::wakeOne(WakeReason reason,
const Channel& channel) {
168 assert(reason != WakeReason::Waiting);
169 return m_Queue->wakeOneLocked(*
this, reason, channel);
172size_t WaitQueue::Guard::wakeAll(WakeReason reason,
const Channel& channel) {
178 assert(reason != WakeReason::Waiting);
179 return m_Queue->wakeAllLocked(*
this, reason, channel);
183 const Channel& destination,
size_t requeueCount) {
189 return m_Queue->wakeAndRequeueLocked(*
this, source, wakeCount, destination, requeueCount);
192WaitQueue::WaitQueue()
194 m_pFirstWaiter(nullptr),
195 m_pLastWaiter(nullptr),
197 m_WaitIntent(false) {}
199WaitQueue::~WaitQueue() {
201 FATAL(
"Destroying a WaitQueue with live waiters.");
205WaitQueue::WakeReason WaitQueue::wait(Guard& guard,
Mutex* mutex,
const Channel& channel,
206 size_t debugState, uintptr_t debugAddress,
bool deferTerminal,
207 bool dispatchEvents) {
208 if (mutex && !mutex->isOwnedByCurrentThread()) {
210 "WaitQueue::waitAndUnlock requires current-thread mutex "
221 FATAL(
"Thread attempted to enter two wait queues at once.");
223 thread->clearTerminalWaitCancelledBeforeBlockUnlocked(stateLevel);
228 return terminalWakeReason(unwindState);
231 waiter.scheduler = thread->m_pScheduler;
233 waiter.storeChannel(channel);
234 waiter.stateLevel = stateLevel;
235 waiter.storeReason(WakeReason::Waiting);
237 waiter.notificationNext =
nullptr;
238 waiter.previous = m_pLastWaiter;
243 m_pLastWaiter->next = &
waiter;
265#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
266 if (m_BeforeBlockHook) {
267 m_BeforeBlockHook(
this, thread, channel, debugState);
275 thread->clearTerminalWaitCancelledBeforeBlockUnlocked(stateLevel);
276 if (
waiter.loadQueue() ==
this) {
278 waiter.storeQueue(
nullptr);
280 WakeReason reason =
waiter.loadReason();
281 waiter.scheduler =
nullptr;
284 removeWaiterLocked(&
waiter);
287 if (reason == WakeReason::Waiting) {
288 reason = WakeReason::Spurious;
293 reason = terminalWakeReason(terminalState);
301 if (dispatchEvents && (terminalState ==
Thread::Continue || deferTerminal)) {
311 reason = terminalWakeReason(terminalState);
320 const bool acquired = mutex->acquireForCompletion();
322 FATAL(
"WaitQueue could not reacquire its caller mutex");
327 reason = terminalWakeReason(terminalState);
333bool WaitQueue::wakeOne(WakeReason reason,
const Channel& channel) {
335 return guard.wakeOne(reason, channel);
338size_t WaitQueue::wakeAll(WakeReason reason,
const Channel& channel) {
340 return guard.wakeAll(reason, channel);
347 if (!__atomic_load_n(&m_WaitIntent, __ATOMIC_SEQ_CST)) {
350 return wakeAll(reason, channel);
353void WaitQueue::clearWaitIntentIfEmpty() {
356 if (!m_WaiterCount && __atomic_load_n(&m_WaitIntent, __ATOMIC_RELAXED)) {
357 __atomic_store_n(&m_WaitIntent,
false, __ATOMIC_SEQ_CST);
361bool WaitQueue::wakeOneLocked(Guard& guard, WakeReason reason,
const Channel& channel) {
362 assert(reason == WakeReason::Signalled || reason == WakeReason::Event ||
363 reason == WakeReason::Spurious);
365 if (!(
waiter->channel == channel)) {
369 if (completeWaiter(guard,
waiter, reason)) {
377size_t WaitQueue::wakeAllLocked(Guard& guard, WakeReason reason,
const Channel& channel) {
378 assert(reason == WakeReason::Signalled || reason == WakeReason::Event ||
379 reason == WakeReason::Spurious);
382 if (!(
waiter->channel == channel)) {
386 if (completeWaiter(guard,
waiter, reason)) {
394size_t WaitQueue::wakeAndRequeueLocked(Guard& guard,
const Channel& source,
size_t wakeCount,
395 const Channel& destination,
size_t requeueCount) {
399 if (!(
waiter->channel == source) ||
waiter->loadReason() != WakeReason::Waiting) {
403 if (woken < wakeCount) {
404 if (completeWaiter(guard,
waiter, WakeReason::Signalled)) {
410 if (requeued < requeueCount) {
414 if (thread->m_StateLevels[
waiter->stateLevel].
m_Waiter.loadQueue() ==
this &&
415 waiter->loadReason() == WakeReason::Waiting &&
waiter->channel == source) {
418 waiter->storeChannel(destination);
428 return woken + requeued;
431bool WaitQueue::completeWaiter(Guard& guard, Waiter*
waiter, WakeReason reason) {
433 bool becameReady =
false;
434 bool completed =
false;
437 if (thread->m_StateLevels[
waiter->stateLevel].
m_Waiter.loadQueue() ==
this &&
438 waiter->loadReason() == WakeReason::Waiting) {
439 waiter->storeReason(reason);
441 if (thread->
m_Status == Thread::Sleeping) {
443 __atomic_store_n(&thread->m_ReadyPublicationPending,
true, __ATOMIC_RELEASE);
450 guard.queueSchedulerNotification(
waiter);
455void WaitQueue::publishReady(Waiter*
waiter) {
459#if PEDIGREE_AFFINITY_TESTS
460 const auto hook = __atomic_load_n(&g_ReadyPublicationHook, __ATOMIC_ACQUIRE);
461 if (hook && thread == __atomic_load_n(&g_ReadyPublicationTarget, __ATOMIC_ACQUIRE))
464 thread->publishReadyNotification();
467#if PEDIGREE_AFFINITY_TESTS
468void WaitQueue::setReadyPublicationHookForTest(
Thread* target, ReadyPublicationHook hook) {
469 __atomic_store_n(&g_ReadyPublicationTarget, target, __ATOMIC_RELEASE);
470 __atomic_store_n(&g_ReadyPublicationHook, hook, __ATOMIC_RELEASE);
474void WaitQueue::removeWaiterLocked(Waiter*
waiter) {
475 if (!
waiter->isQueued()) {
482 assert(m_pFirstWaiter ==
waiter);
483 m_pFirstWaiter =
waiter->next;
489 assert(m_pLastWaiter ==
waiter);
490 m_pLastWaiter =
waiter->previous;
493 assert(m_WaiterCount);
495 clearWaitIntentIfEmpty();
496 waiter->previous =
nullptr;
501void WaitQueue::cancel(Waiter*
waiter, WakeReason reason) {
503 bool makeReady =
false;
506 if (!guard.m_OwnsLock) {
510 if (
waiter->loadQueue() !=
this) {
515 removeWaiterLocked(
waiter);
516 waiter->storeReason(reason);
517 makeReady = thread->
m_Status == Thread::Sleeping;
519 if (reason == WakeReason::Terminating) {
520 waiter->storeQueue(
nullptr);
521 if (!makeReady && thread->
m_Status == Thread::Running) {
532 bool becameReady =
false;
535 if (thread->
m_Status == Thread::Sleeping) {
537 __atomic_store_n(&thread->m_ReadyPublicationPending,
true, __ATOMIC_RELEASE);
548size_t WaitQueue::waiterCount() {
550 if (!guard.m_OwnsLock) {
553 return m_WaiterCount;
556#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
557void WaitQueue::setBeforeBlockHook(BeforeBlockHook hook) {
558 m_BeforeBlockHook = hook;
static ProcessorInformation & information()
static bool guardDeviceHardIrqOperation(DeviceHardIrqOperation operation)
bool acquire(bool recurse=false, bool safe=true)
@ Continue
No unwind necessary, carry on as normal.
@ TerminateThread
Exit only this thread during Process exit.
@ Exit
Exit the owning process at the next safe boundary.
void setDebugState(DebugState state, uintptr_t address)
UnwindType getUnwindState()
void markTerminalWaitCancelledBeforeBlockUnlocked(size_t level)
size_t getStateLevel() const
MUST_USE_RESULT WakeReason wait(const Channel &channel=Channel(), size_t debugState=0, uintptr_t debugAddress=0, StackDiscardCleanup onStackDiscard=nullptr, void *stackDiscardContext=nullptr)
MUST_USE_RESULT WakeReason waitForCompletion(const Channel &channel=Channel(), size_t debugState=0, uintptr_t debugAddress=0)
size_t wakeAndRequeue(const Channel &source, size_t wakeCount, const Channel &destination, size_t requeueCount)
MUST_USE_RESULT WakeReason waitWithoutEventDispatch(const Channel &channel, size_t debugState, uintptr_t debugAddress)
MUST_USE_RESULT WakeReason waitAndUnlock(Mutex &mutex, const Channel &channel=Channel(), size_t debugState=0, uintptr_t debugAddress=0, StackDiscardCleanup onStackDiscard=nullptr, void *stackDiscardContext=nullptr)
MUST_USE_RESULT WakeReason waitAndUnlockForCompletion(Mutex &mutex, const Channel &channel=Channel(), size_t debugState=0, uintptr_t debugAddress=0)
size_t wakeAllIfWaiting(WakeReason reason=WakeReason::Signalled, const Channel &channel=Channel())
WaitQueue::Waiter m_Waiter
bool m_bDispatchingWaitEvent