8#include "pedigree/kernel/Log.h"
9#include "pedigree/kernel/Metrics.h"
10#include "pedigree/kernel/process/Mutex.h"
11#include "pedigree/kernel/process/PerProcessorScheduler.h"
12#include "pedigree/kernel/process/Thread.h"
13#include "pedigree/kernel/process/WaitQueue.h"
14#include "pedigree/kernel/processor/Processor.h"
15#include "pedigree/kernel/processor/ProcessorInformation.h"
16#include "pedigree/kernel/utilities/Iterator.h"
17#include "pedigree/kernel/utilities/assert.h"
19#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
20WaitQueue::BeforeBlockHook WaitQueue::m_BeforeBlockHook =
nullptr;
24#if PEDIGREE_AFFINITY_TESTS
25Thread* g_ReadyPublicationTarget =
nullptr;
26WaitQueue::ReadyPublicationHook g_ReadyPublicationHook =
nullptr;
30 return WaitQueue::WakeReason::Unwinding;
33 return WaitQueue::WakeReason::Terminating;
35 FATAL(
"Unknown terminal WaitQueue unwind state.");
36 return WaitQueue::WakeReason::Spurious;
41 : m_Queue(&queue), m_OwnsLock(false), m_pFirstReady(nullptr), m_pLastReady(nullptr) {
46 m_Queue->m_Lock.acquire();
50WaitQueue::Guard::Guard(Guard&& other) noexcept
51 : m_Queue(other.m_Queue),
52 m_OwnsLock(other.m_OwnsLock),
53 m_pFirstReady(other.m_pFirstReady),
54 m_pLastReady(other.m_pLastReady) {
55 other.m_Queue =
nullptr;
56 other.m_OwnsLock =
false;
57 other.m_pFirstReady =
nullptr;
58 other.m_pLastReady =
nullptr;
61WaitQueue::Guard::~Guard() {
65void WaitQueue::Guard::release() {
66 if (m_Queue && m_OwnsLock) {
67 m_Queue->clearWaitIntentIfEmpty();
68 m_Queue->m_Lock.release();
74 while (m_pFirstReady) {
75 Waiter*
waiter = m_pFirstReady;
76 m_pFirstReady =
waiter->notificationNext;
77 waiter->notificationNext =
nullptr;
78 WaitQueue::publishReady(
waiter);
80 m_pLastReady =
nullptr;
89 __atomic_store_n(&m_Queue->m_WaitIntent,
true, __ATOMIC_SEQ_CST);
93void WaitQueue::Guard::queueSchedulerNotification(
Waiter*
waiter) {
95 assert(!
waiter->notificationNext);
97 m_pLastReady->notificationNext =
waiter;
105 uintptr_t debugAddress,
106 StackDiscardCleanup onStackDiscard,
107 void* stackDiscardContext) {
110 return WakeReason::Spurious;
114 return m_Queue->wait(*
this,
nullptr, channel, debugState, debugAddress,
false,
true);
118 uintptr_t debugAddress) {
121 return WakeReason::Spurious;
124 return m_Queue->wait(*
this,
nullptr, channel, debugState, debugAddress,
true,
true);
129 uintptr_t debugAddress) {
132 return WakeReason::Spurious;
135 return m_Queue->wait(*
this,
nullptr, channel, debugState, debugAddress,
false,
false);
139 size_t debugState, uintptr_t debugAddress,
140 StackDiscardCleanup onStackDiscard,
141 void* stackDiscardContext) {
144 return WakeReason::Spurious;
148 return m_Queue->wait(*
this, &mutex, channel, debugState, debugAddress,
false,
true);
154 uintptr_t debugAddress) {
157 return WakeReason::Spurious;
160 return m_Queue->wait(*
this, &mutex, channel, debugState, debugAddress,
true,
true);
163bool WaitQueue::Guard::wakeOne(WakeReason reason,
const Channel& channel) {
169 assert(reason != WakeReason::Waiting);
170 return m_Queue->wakeOneLocked(*
this, reason, channel);
173size_t WaitQueue::Guard::wakeAll(WakeReason reason,
const Channel& channel) {
179 assert(reason != WakeReason::Waiting);
180 return m_Queue->wakeAllLocked(*
this, reason, channel);
184 const Channel& destination,
size_t requeueCount) {
190 return m_Queue->wakeAndRequeueLocked(*
this, source, wakeCount, destination, requeueCount);
193WaitQueue::WaitQueue()
195 m_pFirstWaiter(nullptr),
196 m_pLastWaiter(nullptr),
198 m_WaitIntent(false) {}
200WaitQueue::~WaitQueue() {
202 FATAL(
"Destroying a WaitQueue with live waiters.");
206WaitQueue::WakeReason WaitQueue::wait(Guard& guard,
Mutex* mutex,
const Channel& channel,
207 size_t debugState, uintptr_t debugAddress,
bool deferTerminal,
208 bool dispatchEvents) {
209 if (mutex && !mutex->isOwnedByCurrentThread()) {
211 "WaitQueue::waitAndUnlock requires current-thread mutex "
222 FATAL(
"Thread attempted to enter two wait queues at once.");
224 thread->clearTerminalWaitCancelledBeforeBlockUnlocked(stateLevel);
229 return terminalWakeReason(unwindState);
232 waiter.scheduler = thread->m_pScheduler;
234 waiter.storeChannel(channel);
235 waiter.stateLevel = stateLevel;
236 waiter.storeReason(WakeReason::Waiting);
238 waiter.notificationNext =
nullptr;
239 waiter.previous = m_pLastWaiter;
244 m_pLastWaiter->next = &
waiter;
250 Metrics::increment(Metrics::WaitQueueWait);
267#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
268 if (m_BeforeBlockHook) {
269 m_BeforeBlockHook(
this, thread, channel, debugState);
277 thread->clearTerminalWaitCancelledBeforeBlockUnlocked(stateLevel);
278 if (
waiter.loadQueue() ==
this) {
280 waiter.storeQueue(
nullptr);
282 WakeReason reason =
waiter.loadReason();
283 waiter.scheduler =
nullptr;
286 removeWaiterLocked(&
waiter);
289 if (reason == WakeReason::Waiting) {
290 reason = WakeReason::Spurious;
295 reason = terminalWakeReason(terminalState);
303 if (dispatchEvents && (terminalState ==
Thread::Continue || deferTerminal)) {
313 reason = terminalWakeReason(terminalState);
322 const bool acquired = mutex->acquireForCompletion();
324 FATAL(
"WaitQueue could not reacquire its caller mutex");
329 reason = terminalWakeReason(terminalState);
335bool WaitQueue::wakeOne(WakeReason reason,
const Channel& channel) {
337 return guard.wakeOne(reason, channel);
340size_t WaitQueue::wakeAll(WakeReason reason,
const Channel& channel) {
342 return guard.wakeAll(reason, channel);
349 if (!__atomic_load_n(&m_WaitIntent, __ATOMIC_SEQ_CST)) {
352 return wakeAll(reason, channel);
355void WaitQueue::clearWaitIntentIfEmpty() {
358 if (!m_WaiterCount && __atomic_load_n(&m_WaitIntent, __ATOMIC_RELAXED)) {
359 __atomic_store_n(&m_WaitIntent,
false, __ATOMIC_SEQ_CST);
363bool WaitQueue::wakeOneLocked(Guard& guard, WakeReason reason,
const Channel& channel) {
364 assert(reason == WakeReason::Signalled || reason == WakeReason::Event ||
365 reason == WakeReason::Spurious);
367 if (!(
waiter->channel == channel)) {
371 if (completeWaiter(guard,
waiter, reason)) {
379size_t WaitQueue::wakeAllLocked(Guard& guard, WakeReason reason,
const Channel& channel) {
380 assert(reason == WakeReason::Signalled || reason == WakeReason::Event ||
381 reason == WakeReason::Spurious);
384 if (!(
waiter->channel == channel)) {
388 if (completeWaiter(guard,
waiter, reason)) {
396size_t WaitQueue::wakeAndRequeueLocked(Guard& guard,
const Channel& source,
size_t wakeCount,
397 const Channel& destination,
size_t requeueCount) {
401 if (!(
waiter->channel == source) ||
waiter->loadReason() != WakeReason::Waiting) {
405 if (woken < wakeCount) {
406 if (completeWaiter(guard,
waiter, WakeReason::Signalled)) {
412 if (requeued < requeueCount) {
416 if (thread->m_StateLevels[
waiter->stateLevel].
m_Waiter.loadQueue() ==
this &&
417 waiter->loadReason() == WakeReason::Waiting &&
waiter->channel == source) {
420 waiter->storeChannel(destination);
426 Metrics::increment(Metrics::WaitQueueRequeue);
431 return woken + requeued;
434bool WaitQueue::completeWaiter(Guard& guard, Waiter*
waiter, WakeReason reason) {
436 bool becameReady =
false;
437 bool completed =
false;
440 if (thread->m_StateLevels[
waiter->stateLevel].
m_Waiter.loadQueue() ==
this &&
441 waiter->loadReason() == WakeReason::Waiting) {
442 waiter->storeReason(reason);
444 Metrics::increment(Metrics::WaitQueueWake);
445 if (thread->
m_Status == Thread::Sleeping) {
447 __atomic_store_n(&thread->m_ReadyPublicationPending,
true, __ATOMIC_RELEASE);
450 Metrics::increment(Metrics::WaitQueueEarlyWake);
456 guard.queueSchedulerNotification(
waiter);
461void WaitQueue::publishReady(Waiter*
waiter) {
465#if PEDIGREE_AFFINITY_TESTS
466 const auto hook = __atomic_load_n(&g_ReadyPublicationHook, __ATOMIC_ACQUIRE);
467 if (hook && thread == __atomic_load_n(&g_ReadyPublicationTarget, __ATOMIC_ACQUIRE))
470 thread->publishReadyNotification();
473#if PEDIGREE_AFFINITY_TESTS
474void WaitQueue::setReadyPublicationHookForTest(
Thread* target, ReadyPublicationHook hook) {
475 __atomic_store_n(&g_ReadyPublicationTarget, target, __ATOMIC_RELEASE);
476 __atomic_store_n(&g_ReadyPublicationHook, hook, __ATOMIC_RELEASE);
480void WaitQueue::removeWaiterLocked(Waiter*
waiter) {
481 if (!
waiter->isQueued()) {
488 assert(m_pFirstWaiter ==
waiter);
489 m_pFirstWaiter =
waiter->next;
495 assert(m_pLastWaiter ==
waiter);
496 m_pLastWaiter =
waiter->previous;
499 assert(m_WaiterCount);
501 Metrics::increment(Metrics::WaitQueueUnqueue);
502 clearWaitIntentIfEmpty();
503 waiter->previous =
nullptr;
508void WaitQueue::cancel(Waiter*
waiter, WakeReason reason) {
510 bool makeReady =
false;
513 if (!guard.m_OwnsLock) {
517 if (
waiter->loadQueue() !=
this) {
522 removeWaiterLocked(
waiter);
523 Metrics::increment(Metrics::WaitQueueCancel);
524 waiter->storeReason(reason);
525 makeReady = thread->
m_Status == Thread::Sleeping;
527 if (reason == WakeReason::Terminating) {
528 waiter->storeQueue(
nullptr);
529 if (!makeReady && thread->
m_Status == Thread::Running) {
540 bool becameReady =
false;
543 if (thread->
m_Status == Thread::Sleeping) {
545 __atomic_store_n(&thread->m_ReadyPublicationPending,
true, __ATOMIC_RELEASE);
556size_t WaitQueue::waiterCount() {
558 if (!guard.m_OwnsLock) {
561 return m_WaiterCount;
564#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
565void WaitQueue::setBeforeBlockHook(BeforeBlockHook hook) {
566 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