10#if !PEDIGREE_HOSTED_CORE_SMOKE
11#include "modules/system/usb/UsbPnP.h"
13#include "pedigree/kernel/Atomic.h"
14#include "pedigree/kernel/Log.h"
15#include "pedigree/kernel/process/ConditionVariable.h"
16#include "pedigree/kernel/process/Event.h"
17#include "pedigree/kernel/process/Mutex.h"
18#include "pedigree/kernel/process/PerProcessorScheduler.h"
19#include "pedigree/kernel/process/Process.h"
20#include "pedigree/kernel/process/Scheduler.h"
21#include "pedigree/kernel/process/Semaphore.h"
22#include "pedigree/kernel/process/SignalEvent.h"
23#include "pedigree/kernel/process/TerminationDeferral.h"
24#include "pedigree/kernel/process/Thread.h"
25#include "pedigree/kernel/process/Uninterruptible.h"
26#include "pedigree/kernel/process/WaitQueue.h"
27#include "pedigree/kernel/processor/Processor.h"
28#include "pedigree/kernel/time/Time.h"
31bool runHostedRequestQueueRegressions();
32bool runHostedAtaPioRegressions();
33bool runHostedBufferRegressions();
34bool runHostedElfValidationRegressions();
35bool runHostedLifetimeLeaseRegressions();
36bool runHostedMutexRegressions();
37bool runHostedSpinlockRegressions();
38bool runHostedPageFaultRegressions();
39bool runHostedPayloadPageRegressions();
40bool runHostedPrimitiveRegressions(
Thread* thread);
41bool runHostedRingBufferRegressions();
42bool runHostedProcessExitRegressions();
43bool runHostedPs2MousePacketRegressions();
44bool runHostedSignalInterruptionRegressions(
Thread* thread);
45bool runHostedTimerRegressions(
Thread* thread);
46#if PEDIGREE_HOSTED_CORE_SMOKE
47bool runHostedStoragePageRegressions();
48bool runHostedCacheDiscardRegressions();
49bool runHostedCacheTimerRegressions();
50bool runHostedCacheDirectWritebackRegressions();
52#if !PEDIGREE_HOSTED_CORE_SMOKE
53bool runHostedSchedulerRegressions();
54bool runHostedCacheRegressions();
55bool runHostedInputRegressions();
56bool runHostedInterruptManagerRegressions();
57EXPORTED_PUBLIC
bool runHostedFatSectorRegressions();
58EXPORTED_PUBLIC
bool runHostedRawFsContractRegressions();
59EXPORTED_PUBLIC
bool runHostedUnixDatagramRegressions();
60bool runHostedLogRegressions();
61bool runNetworkFilterConcurrencyRegressions();
62bool runVfsCallbackLifetimeRegressions();
63bool runHostedPipeRegressions();
64bool runHostedPs2ControllerRegressions();
65bool runHostedPs2MouseRegressions();
66bool runHostedSyscallRegressions();
67bool runHostedUsbCallbackDeliveryRegressions();
68bool runHostedUsbHcdPortChangeRegressions();
69bool runHostedUsbTransferLifecycleRegressions();
70EXPORTED_PUBLIC
bool runHostedUsbSyncOwnershipRegression();
71EXPORTED_PUBLIC
bool runHostedUsbInterruptOwnershipRegression();
72EXPORTED_PUBLIC
bool runHostedUsbContainerOwnershipRegression();
73EXPORTED_PUBLIC
bool runHostedUsbEndpointHaltRegressions();
74EXPORTED_PUBLIC
bool runHostedUsbBotRegressions();
75EXPORTED_PUBLIC
bool runHostedHidReportRegressions();
76EXPORTED_PUBLIC
bool runHostedScsiWriteRegressions();
84Thread* g_ImmediateWaiter =
nullptr;
85Thread* g_ImmediateExpectedWaiter =
nullptr;
87struct TerminalCancelBeforeBlockContext {
90 firstChannel(&queue, 0x5445524d),
91 secondChannel(&queue, 0x52455553),
110TerminalCancelBeforeBlockContext* g_TerminalCancelBeforeBlockContext =
nullptr;
112struct NestedTerminalShutdownContext {
113 NestedTerminalShutdownContext()
115 channel(&queue, 0x4e455354),
141NestedTerminalShutdownContext* g_NestedTerminalShutdownContext =
nullptr;
143struct SemaphoreHookContext {
145 : semaphore(semaphore),
146 expectedWaiter(expectedWaiter),
158SemaphoreHookContext* g_SemaphoreContext =
nullptr;
160class StackDestructionCanary {
162 explicit StackDestructionCanary(
Atomic<size_t>* destructions) : m_Destructions(destructions) {}
164 ~StackDestructionCanary() {
165 *m_Destructions += 1;
172struct TerminalCancelContext {
173 TerminalCancelContext()
174 :
waiter(nullptr), entered(0), resumed(0), terminalResult(0), destructed(0) {}
184class PublishedWaitOwner;
186struct PublishedWaitDiscardContext {
187 PublishedWaitDiscardContext()
196 waitersAtDestruction(0),
198 stackDestructed(0) {}
200 PublishedWaitOwner* owner;
213class PublishedWaitOwner {
215 explicit PublishedWaitOwner(PublishedWaitDiscardContext* context) : m_Context(context) {}
217 ~PublishedWaitOwner() {
218 m_Context->waitersAtDestruction += queue.waiterCount();
219 m_Context->ownerDestructed += 1;
225 PublishedWaitDiscardContext* m_Context;
228constexpr uintptr_t PublishedWaitDiscardChannel = 0x44495343;
229PublishedWaitDiscardContext* g_PublishedWaitDiscardContext =
nullptr;
231void destroyPublishedWaitOwner(
void* parameter) {
232 PublishedWaitDiscardContext* context =
reinterpret_cast<PublishedWaitDiscardContext*
>(parameter);
233 context->cleanupCalls += 1;
234 PublishedWaitOwner* owner = context->owner;
235 context->owner =
nullptr;
239struct ConditionVariableHookContext {
241 : condition(condition),
243 expectedWaiter(expectedWaiter),
258ConditionVariableHookContext* g_ConditionVariableContext =
nullptr;
260struct CompletionConditionContext {
261 CompletionConditionContext()
268 terminalMutexHeld(0),
269 signalPreserved(0) {}
283struct ContendedConditionContext {
289 ContendedConditionContext(
Thread*
waiter, Trigger trigger)
293 conditionWaitPublished(0),
295 reacquireWaitPublished(0),
309struct TerminalConditionContext {
310 TerminalConditionContext()
315 mutexHeldOnReturn(0),
330struct UnlikelyLockHookContext {
336 UnlikelyLockHookContext(
UnlikelyLock* lock, HeldMode heldMode)
344 signalPreserved(0) {}
356UnlikelyLockHookContext* g_UnlikelyLockContext =
nullptr;
358struct UnlikelyLockFairnessContext {
359 explicit UnlikelyLockFairnessContext(
UnlikelyLock* lock)
360 : lock(lock), admissionSequence(0), writerOrder(0), writerReturned(0), readersEntered(0) {}
369bool waitForDebugState(
Thread* thread,
size_t debugState) {
370 const Time::Timestamp deadline = Time::getTicks() + (2 * Time::Multiplier::Second);
371 while (Time::getTicks() < deadline) {
373 uintptr_t debugAddress = 0;
383void contendedConditionSignalHandler(
size_t) {
384 g_ContendedSignalHandlerCalls += 1;
387int holdConditionMutex(
void* parameter) {
388 ContendedConditionContext* context =
reinterpret_cast<ContendedConditionContext*
>(parameter);
389 if (!context->mutex.acquireForCompletion()) {
392 context->holderAcquired += 1;
394 if (waitForDebugState(context->waiter, Thread::CondWait)) {
395 context->conditionWaitPublished += 1;
398 if (context->trigger == ContendedConditionContext::Signal) {
400 reinterpret_cast<uintptr_t
>(&contendedConditionSignalHandler), 10, ~0UL, 0,
true,
true);
401 if (context->waiter->sendEvent(event)) {
402 context->triggerSent += 1;
408 const Time::Timestamp holdTime = context->trigger == ContendedConditionContext::Signal
409 ? 50 * Time::Multiplier::Millisecond
410 : 200 * Time::Multiplier::Millisecond;
411 Time::delay(holdTime);
412 if (waitForDebugState(context->waiter, Thread::SemWait)) {
413 context->reacquireWaitPublished += 1;
416 context->mutex.release();
417 context->holderReleased += 1;
421int waitForTerminalCondition(
void* parameter) {
422 TerminalConditionContext* context =
reinterpret_cast<TerminalConditionContext*
>(parameter);
423 StackDestructionCanary stackCanary(&context->destructed);
424 if (!context->mutex.acquireForCompletion()) {
427 context->entered += 1;
429 ConditionVariable::Error error = ConditionVariable::NoError;
430 const bool waited = context->condition.wait(context->mutex, error);
431 context->terminalResult =
432 !waited && error == ConditionVariable::TerminationDeferred ?
static_cast<size_t>(1) : 0;
433 context->mutexHeldOnReturn = context->mutex.isOwnedByCurrentThread() ? 1 : 0;
434 context->waitReturned += 1;
436 if (context->mutexHeldOnReturn) {
437 context->mutex.release();
439 return context->terminalResult ? 0 : 1;
442struct ProcessSuspendContext {
449 ProcessSuspendContext(
Process* process, Mode mode)
469ProcessSuspendContext* g_ProcessSuspendContext =
nullptr;
471struct ProcessStopReturnGateContext {
472 ProcessStopReturnGateContext()
473 : release(0), entered(0), returned(0), crossings(0), terminalReturns(0), failures(0) {}
483struct ProcessStopOwnerContext {
484 explicit ProcessStopOwnerContext(
Process* process) : process(process), entered(0), returned(0) {}
493void stoppedUserReturnHandler(
size_t) {
494 g_StoppedUserHandlerCalls += 1;
497class StoppedUserReturnEvent :
public Event {
499 StoppedUserReturnEvent()
500 :
Event(reinterpret_cast<uintptr_t>(&stoppedUserReturnHandler), false, ~0UL,
501 HandlerPrivilege::
User) {}
512void stopCurrentProcessFromEvent(
size_t) {
517class StopCurrentProcessEvent :
public Event {
519 StopCurrentProcessEvent()
520 :
Event(reinterpret_cast<uintptr_t>(&stopCurrentProcessFromEvent), false, ~0UL,
521 HandlerPrivilege::Kernel) {}
532struct PrequeuedStopOwnerContext {
533 explicit PrequeuedStopOwnerContext(
Process* process)
551struct JoinPropagationContext {
552 explicit JoinPropagationContext(
Semaphore* gate)
558 joinerDestructed(0) {}
568struct JoinPublicationContext {
569 JoinPublicationContext()
577 targetReapableInHook(0),
593JoinPublicationContext* g_JoinPublicationContext =
nullptr;
597 if (thread != g_ImmediateExpectedWaiter) {
600 g_ImmediateHookCalls += 1;
601 g_ImmediateWaiter = thread;
602 if (queue != g_ImmediateQueue || !(channel == g_ImmediateChannel) ||
603 debugState != Thread::EventWait) {
604 g_ImmediateHookFailures += 1;
612 if (!waitVisible || wait.queue != queue || wait.channelOwner != channel.owner ||
613 wait.channelValue != channel.value || wait.reason != WaitQueue::WakeReason::Waiting ||
614 wait.stateLevel != thread->
getStateLevel() || !wait.queued) {
615 g_ImmediateHookFailures += 1;
619 if (!queue->wakeOne(WaitQueue::WakeReason::Signalled, channel)) {
620 g_ImmediateHookFailures += 1;
626 PublishedWaitDiscardContext* context = g_PublishedWaitDiscardContext;
627 if (!context || thread != context->waiter) {
631 context->hookCalls += 1;
632 PublishedWaitOwner* owner = context->owner;
634 if (context->hookCalls != 1 || !owner || queue != &owner->queue || channel.owner != owner ||
635 channel.value != PublishedWaitDiscardChannel || debugState != Thread::EventWait ||
637 wait.reason != WaitQueue::WakeReason::Waiting || queue->waiterCount() != 1) {
638 context->hookFailures += 1;
640 context->published += 1;
644 PerProcessorScheduler::StackDiscardReason::HostedRegression);
649 TerminalCancelBeforeBlockContext* context = g_TerminalCancelBeforeBlockContext;
650 if (!context || thread != context->waiter) {
654 context->hookCalls += 1;
656 if (queue != &context->queue || debugState != Thread::EventWait ||
658 wait.reason != WaitQueue::WakeReason::Waiting) {
659 context->hookFailures += 1;
663 if (context->phase == 0) {
664 if (!(channel == context->firstChannel)) {
665 context->hookFailures += 1;
670 context->cancellations += 1;
675 if (context->phase != 1 || !(channel == context->secondChannel) ||
676 !context->queue.wakeOne(WaitQueue::WakeReason::Signalled, channel)) {
677 context->hookFailures += 1;
681 context->wakeups += 1;
685void nestedTerminalShutdownBeforeBlockHook(
WaitQueue* queue,
Thread* thread,
687 NestedTerminalShutdownContext* context = g_NestedTerminalShutdownContext;
688 if (!context || thread != context->waiter) {
692 context->hookCalls += 1;
694 if (context->hookCalls != 1 || queue != &context->queue || !(channel == context->channel) ||
695 debugState != Thread::EventWait || !thread->
getWaitDebugInfo(wait) || wait.queue != queue ||
696 !wait.queued || wait.reason != WaitQueue::WakeReason::Waiting) {
697 context->hookFailures += 1;
701 SchedulerState* outerState = thread->
pushState();
703 context->hookFailures += 1;
706 context->pushed += 1;
712 context->shutdowns += 1;
714 context->popped += 1;
719 SemaphoreHookContext* context = g_SemaphoreContext;
724 if (thread != context->expectedWaiter) {
728 context->hookCalls += 1;
729 context->waiter = thread;
730 if (!queue || channel.owner != context->semaphore || channel.value ||
731 debugState != Thread::SemWait) {
732 context->hookFailures += 1;
735 context->semaphore->release();
738void semaphoreReleaseBeforeIntentHook(
Semaphore* semaphore) {
739 SemaphoreHookContext* context = g_SemaphoreContext;
740 if (!context || semaphore != context->semaphore ||
744 context->hookCalls += 1;
746 context->hookFailures += 1;
753 SemaphoreHookContext* context = g_SemaphoreContext;
754 if (!context || thread != context->expectedWaiter || channel.owner != context->semaphore) {
757 const size_t phase = context->hookCalls;
758 context->hookCalls += 1;
759 context->waiter = thread;
761 if (phase >= 4 || !queue || channel.value || debugState != Thread::SemWait ||
763 wait.reason != WaitQueue::WakeReason::Waiting || queue->waiterCount() != 1 ||
764 context->semaphore->getValue() !=
static_cast<ssize_t
>(phase % 2)) {
765 context->hookFailures += 1;
767 context->semaphore->release(phase >= 4 ? 3 : (phase % 2 ? 2 : 1));
770int waitForTerminalCancellation(
void* parameter) {
771 TerminalCancelContext* context =
reinterpret_cast<TerminalCancelContext*
>(parameter);
772 StackDestructionCanary stackCanary(&context->destructed);
774 context->entered += 1;
775 auto guard = context->queue.acquire();
776 const WaitQueue::WakeReason reason =
778 reinterpret_cast<uintptr_t
>(__builtin_return_address(0)));
780 context->terminalResult = reason == WaitQueue::WakeReason::Terminating ? 1 : 0;
781 context->resumed += 1;
782 return context->terminalResult ? 0 : 1;
785int abandonPublishedWait(
void* parameter) {
786 PublishedWaitDiscardContext* context =
reinterpret_cast<PublishedWaitDiscardContext*
>(parameter);
787 PublishedWaitOwner* owner =
new PublishedWaitOwner(context);
788 context->owner = owner;
791 StackDestructionCanary stackCanary(&context->stackDestructed);
792 context->entered += 1;
794 auto guard = owner->queue.acquire();
796 reinterpret_cast<uintptr_t
>(__builtin_return_address(0)));
799 context->returned += 1;
800 discardScope.disarm();
801 destroyPublishedWaitOwner(context);
807 ConditionVariableHookContext* context = g_ConditionVariableContext;
812 if (thread != context->expectedWaiter) {
816 context->hookCalls += 1;
817 context->waiter = thread;
818 const bool mutexAcquired = context->mutex->tryAcquire();
819 if (!queue || channel.owner || channel.value || debugState != Thread::CondWait ||
821 context->hookFailures += 1;
823 context->acquiredMutex += 1;
828 context->condition->signal();
829 context->mutex->release();
834 UnlikelyLockHookContext* context = g_UnlikelyLockContext;
835 if (!context || thread != context->thread) {
839 context->hookCalls += 1;
840 if (!queue || channel.owner || channel.value || debugState != Thread::CondWait) {
841 context->hookFailures += 1;
845 if (context->heldMode == UnlikelyLockHookContext::WriterHeld) {
846 context->lock->release();
848 context->lock->leave();
852int waitForTerminalCompletionCondition(
void* parameter) {
853 CompletionConditionContext* context =
reinterpret_cast<CompletionConditionContext*
>(parameter);
855 if (!context->gate.acquireForCompletion()) {
859 if (!context->mutex.acquireForCompletion()) {
862 context->entered += 1;
863 Thread* thread = context->waiter;
865 ConditionVariable::Error terminalError = ConditionVariable::NoError;
866 const bool terminalWait = context->condition.wait(context->mutex, terminalError);
867 context->terminalReported =
868 !terminalWait && terminalError == ConditionVariable::TerminationDeferred ? 1 : 0;
869 context->terminalMutexHeld = context->mutex.isOwnedByCurrentThread() ? 1 : 0;
870 if (!context->terminalMutexHeld) {
874 thread->setInterruptionReason(Thread::InterruptedBySignal);
875 while (!context->predicate) {
876 context->condition.waitForCompletion(context->mutex);
878 context->signalPreserved = thread->getInterruptionReason() == Thread::InterruptedBySignal ? 1 : 0;
879 thread->clearInterruption();
881 context->mutex.release();
882 context->returned += 1;
886int enterUnlikelyLock(
void* parameter) {
887 UnlikelyLockHookContext* context =
reinterpret_cast<UnlikelyLockHookContext*
>(parameter);
888 Thread* thread = context->thread;
889 thread->setInterruptionReason(Thread::InterruptedBySignal);
890 context->lock->enter();
891 context->acquired += 1;
892 context->signalPreserved = thread->getInterruptionReason() == Thread::InterruptedBySignal ? 1 : 0;
893 thread->clearInterruption();
894 context->lock->leave();
895 context->returned += 1;
899int acquireTerminalUnlikelyLock(
void* parameter) {
900 UnlikelyLockHookContext* context =
reinterpret_cast<UnlikelyLockHookContext*
>(parameter);
901 Thread* thread = context->thread;
903 context->lock->acquire();
904 context->acquired += 1;
905 context->lock->release();
906 context->returned += 1;
910int acquireFairnessWriter(
void* parameter) {
911 UnlikelyLockFairnessContext* context =
reinterpret_cast<UnlikelyLockFairnessContext*
>(parameter);
912 context->lock->acquire();
913 context->writerOrder = context->admissionSequence += 1;
914 context->lock->release();
915 context->writerReturned += 1;
919int enterFairnessReader(
void* parameter) {
920 UnlikelyLockFairnessContext* context =
reinterpret_cast<UnlikelyLockFairnessContext*
>(parameter);
921 context->lock->enter();
922 context->admissionSequence += 1;
923 context->readersEntered += 1;
924 context->lock->leave();
930 ProcessSuspendContext* context = g_ProcessSuspendContext;
931 if (!context || debugState != Thread::ProcessWait) {
935 context->hookCalls += 1;
936 context->waiter = thread;
937 if (!queue || channel.owner != context->process ||
938 channel.value !=
static_cast<uintptr_t
>(Thread::ProcessWait)) {
939 context->hookFailures += 1;
942 switch (context->mode) {
943 case ProcessSuspendContext::ObserveBlock:
944 if (!context->phase.compareAndSwap(0, 1)) {
945 context->hookFailures += 1;
948 case ProcessSuspendContext::ResumeBeforeBlock:
949 context->process->resume();
950 context->resumes += 1;
952 case ProcessSuspendContext::EventThenResume:
953 if (context->hookCalls == 1) {
954 if (queue->wakeOne(WaitQueue::WakeReason::Event, channel)) {
955 context->eventWakes += 1;
957 context->hookFailures += 1;
959 }
else if (context->hookCalls == 2) {
960 context->process->resume();
961 context->resumes += 1;
963 context->hookFailures += 1;
969int immediateThreadExit(
void*) {
970 g_ImmediateThreadExits += 1;
974int blockedJoinTarget(
void* parameter) {
975 JoinPropagationContext* context =
reinterpret_cast<JoinPropagationContext*
>(parameter);
976 context->targetEntered += 1;
977 return context->gate->acquireForCompletion() ? 0 : 1;
980int joinTarget(
void* parameter) {
981 JoinPropagationContext* context =
reinterpret_cast<JoinPropagationContext*
>(parameter);
982 StackDestructionCanary stackCanary(&context->joinerDestructed);
983 if (context->target->join()) {
984 context->joinSucceeded += 1;
986 context->joinReturned += 1;
990int blockedJoinPublicationTarget(
void* parameter) {
991 JoinPublicationContext* context =
reinterpret_cast<JoinPublicationContext*
>(parameter);
992 context->targetEntered += 1;
993 return context->targetGate.acquireForCompletion() ? 0 : 1;
996int joinPublicationTarget(
void* parameter) {
997 JoinPublicationContext* context =
reinterpret_cast<JoinPublicationContext*
>(parameter);
998 if (context->target->join()) {
999 context->joinSucceeded += 1;
1001 context->joinReturned += 1;
1006 JoinPublicationContext* context = __atomic_load_n(&g_JoinPublicationContext, __ATOMIC_ACQUIRE);
1007 if (!context || thread != context->joiner || debugState != Thread::Joining) {
1011 context->hookCalls += 1;
1014 wait.reason != WaitQueue::WakeReason::Waiting) {
1015 context->hookFailures += 1;
1020 if (!context->target->detach()) {
1021 context->detachRejected += 1;
1023 context->hookFailures += 1;
1028 context->targetGate.release();
1029 constexpr size_t Attempts = 10000;
1030 for (
size_t attempt = 0; attempt < Attempts; ++attempt) {
1031 if (context->target->isReapableForHostedTest()) {
1032 context->targetReapableInHook += 1;
1037 context->hookFailures += 1;
1040int resumeBlockedProcess(
void* parameter) {
1041 ProcessSuspendContext* context =
reinterpret_cast<ProcessSuspendContext*
>(parameter);
1042 while (context->phase < 1) {
1047 context->waiter->getLock().acquire();
1048 const bool sleeping = context->waiter->getStatus() == Thread::Sleeping;
1049 context->waiter->getLock().release();
1056 context->process->resume();
1057 context->resumes += 1;
1058 if (!context->phase.compareAndSwap(1, 2)) {
1059 context->hookFailures += 1;
1065int waitAtProcessStopReturnGate(
void* parameter) {
1066 ProcessStopReturnGateContext* context =
1067 reinterpret_cast<ProcessStopReturnGateContext*
>(parameter);
1069 context->entered += 1;
1070 if (!context->release.acquire()) {
1071 context->failures += 1;
1072 context->returned += 1;
1077 context->terminalReturns += 1;
1079 context->crossings += 1;
1081 context->returned += 1;
1085int waitAtProcessStopReturnGateWithDeferredEvents(
void* parameter) {
1086 ProcessStopReturnGateContext* context =
1087 reinterpret_cast<ProcessStopReturnGateContext*
>(parameter);
1089 context->entered += 1;
1092 if (!context->release.acquire()) {
1093 context->failures += 1;
1094 context->returned += 1;
1100 context->terminalReturns += 1;
1102 context->crossings += 1;
1104 context->returned += 1;
1108void terminateStoppedReturnGatePeer(
size_t) {
1112int stopOwnerWithPrequeuedTerminalEvent(
void* parameter) {
1113 PrequeuedStopOwnerContext* context =
reinterpret_cast<PrequeuedStopOwnerContext*
>(parameter);
1115 StopCurrentProcessEvent stopEvent;
1116 SignalEvent terminalEvent(
reinterpret_cast<uintptr_t
>(&terminateStoppedReturnGatePeer), 9, ~0UL,
1117 0,
true,
false, Event::HandlerPrivilege::Kernel,
1118 SignalEvent::DeliveryDisposition::DefaultAction);
1120 context->entered += 1;
1121 bool stopQueued =
false;
1122 bool terminalQueued =
false;
1125 stopQueued = current->
sendEvent(&stopEvent);
1126 terminalQueued = stopQueued && current->
sendEvent(&terminalEvent);
1127 if (!stopQueued || !terminalQueued) {
1131 if (terminalQueued) {
1137 if (!stopQueued || !terminalQueued) {
1138 context->failures += 1;
1139 context->returned += 1;
1142 context->eventsQueued += 1;
1146 context->terminalReturns += 1;
1148 context->failures += 1;
1150 context->returned += 1;
1154int activeDirectTransitionWithPrequeuedTerminalEvent(
void* parameter) {
1155 PrequeuedStopOwnerContext* context =
reinterpret_cast<PrequeuedStopOwnerContext*
>(parameter);
1157 SignalEvent terminalEvent(
reinterpret_cast<uintptr_t
>(&terminateStoppedReturnGatePeer), 9, ~0UL,
1158 0,
true,
false, Event::HandlerPrivilege::Kernel,
1159 SignalEvent::DeliveryDisposition::DefaultAction);
1161 context->entered += 1;
1162 bool terminalQueued =
false;
1165 terminalQueued = current->
sendEvent(&terminalEvent);
1167 if (!terminalQueued) {
1168 context->failures += 1;
1169 context->returned += 1;
1172 context->eventsQueued += 1;
1175 PerProcessorScheduler::ProcessStopGateMode::DirectUserTransition);
1177 context->process->getState() == Process::Active) {
1178 context->terminalReturns += 1;
1180 context->failures += 1;
1182 context->returned += 1;
1186int ownProcessStop(
void* parameter) {
1187 ProcessStopOwnerContext* context =
reinterpret_cast<ProcessStopOwnerContext*
>(parameter);
1188 context->entered += 1;
1189 context->process->suspend();
1190 context->returned += 1;
1194struct BlockingContext {
1196 : channel(this, 0x57414954),
1212BlockingContext* g_BlockingContext =
nullptr;
1214struct RequeueWaitContext {
1215 RequeueWaitContext()
1216 : source(&queue, 0x46555458),
1217 destination(&queue, 0x4d555445),
1232int waitForRequeue(
void* parameter) {
1233 RequeueWaitContext* context =
reinterpret_cast<RequeueWaitContext*
>(parameter);
1234 context->entered += 1;
1236 auto guard = context->queue.acquire();
1237 const WaitQueue::WakeReason reason = guard.wait(
1238 context->source, Thread::FutexWait,
reinterpret_cast<uintptr_t
>(__builtin_return_address(0)));
1239 if (reason == WaitQueue::WakeReason::Signalled) {
1240 context->signalled += 1;
1241 }
else if (reason == WaitQueue::WakeReason::Terminating) {
1242 context->terminated += 1;
1244 context->returned += 1;
1249 size_t debugState) {
1250 BlockingContext* context = __atomic_load_n(&g_BlockingContext, __ATOMIC_ACQUIRE);
1255 if (queue != &context->queue || !(channel == context->channel) ||
1256 debugState != Thread::EventWait || thread != context->waiter) {
1260 context->hookCalls += 1;
1261 if (!context->phase.compareAndSwap(0, 1)) {
1262 context->hookFailures += 1;
1266int blockingWaker(
void* parameter) {
1267 BlockingContext* context =
reinterpret_cast<BlockingContext*
>(parameter);
1268 while (context->phase < 1) {
1274 context->waiter->getLock().acquire();
1275 const bool sleeping = context->waiter->getStatus() == Thread::Sleeping;
1276 context->waiter->getLock().release();
1283 if (!context->phase.compareAndSwap(1, 2)) {
1284 context->hookFailures += 1;
1288 if (context->queue.wakeOne(WaitQueue::WakeReason::Signalled, context->channel)) {
1289 context->workerWoke += 1;
1291 context->hookFailures += 1;
1294 if (!context->phase.compareAndSwap(2, 3)) {
1295 context->hookFailures += 1;
1301bool check(
bool condition,
const char* test,
const char* detail) {
1306 ERROR(
"HOSTED-WAIT-TEST: FAIL " << test <<
": " << detail);
1310bool wakeBeforeBlock() {
1314 g_ImmediateQueue = &queue;
1315 g_ImmediateChannel = channel;
1317 WaitQueue::setBeforeBlockHook(immediateWakeHook);
1319 auto guard = queue.acquire();
1320 const WaitQueue::WakeReason reason = guard.
wait(
1321 channel, Thread::EventWait,
reinterpret_cast<uintptr_t
>(__builtin_return_address(0)));
1323 WaitQueue::setBeforeBlockHook(
nullptr);
1324 g_ImmediateQueue =
nullptr;
1325 g_ImmediateExpectedWaiter =
nullptr;
1328 passed &= check(reason == WaitQueue::WakeReason::Signalled,
"wake-before-block",
1329 "wait returned the wrong wake reason");
1330 passed &= check(g_ImmediateHookCalls == 1,
"wake-before-block",
1331 "the pre-block hook did not run exactly once");
1332 passed &= check(g_ImmediateHookFailures == 0,
"wake-before-block",
1333 "the published waiter was not visible to the hook");
1335 check(queue.waiterCount() == 0,
"wake-before-block",
"the completed waiter remained queued");
1336 passed &= check(g_ImmediateWaiter && g_ImmediateWaiter->
getStatus() == Thread::Running,
1337 "wake-before-block",
"the scheduler committed a stale Sleeping transition");
1340 NOTICE(
"HOSTED-WAIT-TEST: PASS wake-before-block");
1345bool semaphoreReleaseBeforeBlock() {
1348 g_SemaphoreContext = &context;
1349 WaitQueue::setBeforeBlockHook(semaphoreReleaseHook);
1351 const bool acquired = semaphore.
acquire();
1353 WaitQueue::setBeforeBlockHook(
nullptr);
1354 g_SemaphoreContext =
nullptr;
1357 passed &= check(acquired,
"semaphore-pre-block",
"acquire did not consume the hook release");
1358 passed &= check(context.hookCalls == 1,
"semaphore-pre-block",
1359 "the pre-block hook did not run exactly once");
1360 passed &= check(context.hookFailures == 0,
"semaphore-pre-block",
1361 "the semaphore waiter was not published in the expected state");
1362 passed &= check(context.waiter && context.waiter->getStatus() == Thread::Running,
1363 "semaphore-pre-block",
"the scheduler committed Sleeping after the release");
1364 passed &= check(semaphore.
getValue() == 0,
"semaphore-pre-block",
1365 "the released semaphore unit was not consumed");
1368 NOTICE(
"HOSTED-WAIT-TEST: PASS semaphore-pre-block");
1373bool semaphoreReleaseBeforeIntent() {
1376 g_SemaphoreContext = &context;
1377 Semaphore::setBeforeWaitHook(semaphoreReleaseBeforeIntentHook);
1380 const bool first = semaphore.
acquire();
1381 const bool second = semaphore.
acquire();
1383 Semaphore::setBeforeWaitHook(
nullptr);
1384 g_SemaphoreContext =
nullptr;
1385 const bool passed = check(first && second && context.hookCalls == 2 &&
1386 context.hookFailures == 0 && semaphore.
getValue() == 0,
1387 "semaphore-pre-intent",
1388 "the final predicate missed a release or retained abandoned intent");
1390 NOTICE(
"HOSTED-WAIT-TEST: PASS semaphore-pre-intent");
1395bool semaphorePartialReleaseBeforeBlock() {
1398 g_SemaphoreContext = &context;
1399 WaitQueue::setBeforeBlockHook(semaphorePartialReleaseHook);
1401 bool acquired =
true;
1402 bool exactCounts =
true;
1403 for (
size_t cycle = 0; cycle < 2 && acquired; ++cycle) {
1404 acquired = semaphore.
acquire(3);
1405 exactCounts &= context.hookCalls == 2 * (cycle + 1) && semaphore.
getValue() == 0;
1408 WaitQueue::setBeforeBlockHook(
nullptr);
1409 g_SemaphoreContext =
nullptr;
1411 check(acquired && exactCounts && context.hookCalls == 4 && context.hookFailures == 0 &&
1412 context.waiter && context.waiter->getStatus() == Thread::Running,
1413 "semaphore-partial-release-rearm",
1414 "partial releases lost tokens, completed early, or failed to rearm the queue");
1416 NOTICE(
"HOSTED-WAIT-TEST: PASS semaphore-partial-release-rearm");
1421bool terminalCancellationReturns() {
1422 TerminalCancelContext context;
1424 &context,
nullptr,
false,
true);
1425 waiter->setName(
"hosted terminal cancellation return");
1428 const bool queued = waitForDebugState(
waiter, Thread::EventWait);
1430 const bool joined =
waiter->joinForCompletion();
1432 const bool passed = check(
1433 context.entered == 1 && queued && joined && context.terminalResult == 1 &&
1434 context.resumed == 1 && context.destructed == 1,
1435 "terminal-cancel-return",
"terminal cancellation did not return through the target's stack");
1437 NOTICE(
"HOSTED-WAIT-TEST: PASS terminal-cancel-return");
1442bool publishedWaitDiscardCleanup() {
1443 PublishedWaitDiscardContext context;
1444 const auto reason = PerProcessorScheduler::StackDiscardReason::HostedRegression;
1448 &context,
nullptr,
false,
true,
true);
1449 waiter->setName(
"hosted published wait stack discard");
1452 g_PublishedWaitDiscardContext = &context;
1453 WaitQueue::setBeforeBlockHook(discardPublishedWaitHook);
1454 const bool started =
waiter->start();
1455 const bool joined = started &&
waiter->joinForCompletion();
1456 WaitQueue::setBeforeBlockHook(
nullptr);
1457 g_PublishedWaitDiscardContext =
nullptr;
1459 PublishedWaitOwner* residue = context.owner;
1460 context.owner =
nullptr;
1466 check(started && joined && context.entered == 1 && context.hookCalls == 1 &&
1467 context.hookFailures == 0 && context.published == 1 && context.cleanupCalls == 1 &&
1468 context.ownerDestructed == 1 && context.waitersAtDestruction == 0 &&
1469 context.returned == 0 && context.stackDestructed == 0 &&
1471 "waitqueue-published-stack-discard",
1472 "stack discard did not unlink the published waiter before owner cleanup");
1474 NOTICE(
"HOSTED-WAIT-TEST: PASS waitqueue-published-stack-discard");
1479bool terminalCancelBeforeBlock() {
1481 TerminalCancelBeforeBlockContext context(
waiter);
1482 g_TerminalCancelBeforeBlockContext = &context;
1483 WaitQueue::setBeforeBlockHook(terminalCancelBeforeBlockHook);
1485 auto firstGuard = context.queue.acquire();
1486 const WaitQueue::WakeReason cancelled =
1487 firstGuard.wait(context.firstChannel, Thread::EventWait,
1488 reinterpret_cast<uintptr_t
>(__builtin_return_address(0)));
1493 auto secondGuard = context.queue.acquire();
1494 const WaitQueue::WakeReason signalled =
1495 secondGuard.wait(context.secondChannel, Thread::EventWait,
1496 reinterpret_cast<uintptr_t
>(__builtin_return_address(0)));
1498 WaitQueue::setBeforeBlockHook(
nullptr);
1499 g_TerminalCancelBeforeBlockContext =
nullptr;
1502 passed &= check(cancelled == WaitQueue::WakeReason::Terminating,
"terminal-cancel-before-block",
1503 "pre-block terminal cancellation did not return its terminal reason");
1504 passed &= check(signalled == WaitQueue::WakeReason::Signalled,
"terminal-cancel-before-block",
1505 "a stale terminal handoff aborted a fresh wait");
1506 passed &= check(context.phase == 2 && context.hookCalls == 2 && context.hookFailures == 0 &&
1507 context.cancellations == 1 && context.wakeups == 1,
1508 "terminal-cancel-before-block",
1509 "the deterministic pre-block interleaving did not complete");
1510 passed &= check(context.queue.waiterCount() == 0 &&
waiter->getStatus() == Thread::Running,
1511 "terminal-cancel-before-block",
1512 "terminal return left a linked waiter or stale Sleeping state");
1515 NOTICE(
"HOSTED-WAIT-TEST: PASS terminal-cancel-before-block");
1520int nestedTerminalShutdownWait(
void* parameter) {
1521 NestedTerminalShutdownContext* context =
1522 reinterpret_cast<NestedTerminalShutdownContext*
>(parameter);
1524 context->entered += 1;
1526 auto guard = context->queue.acquire();
1527 const WaitQueue::WakeReason reason =
1528 guard.wait(context->channel, Thread::EventWait,
1529 reinterpret_cast<uintptr_t
>(__builtin_return_address(0)));
1530 context->terminalResult = reason == WaitQueue::WakeReason::Terminating;
1533 context->waiterUnlinked = !context->waiter->getWaitDebugInfo(wait);
1534 context->finished += 1;
1535 return context->terminalResult ? 0 : 1;
1538bool nestedTerminalShutdownBeforeBlock() {
1539 NestedTerminalShutdownContext context;
1540 g_NestedTerminalShutdownContext = &context;
1541 WaitQueue::setBeforeBlockHook(nestedTerminalShutdownBeforeBlockHook);
1544 &context,
nullptr,
false,
true);
1545 waiter->setName(
"hosted nested terminal pre-block waiter");
1547 const bool joined =
waiter->joinForCompletion();
1548 WaitQueue::setBeforeBlockHook(
nullptr);
1549 g_NestedTerminalShutdownContext =
nullptr;
1552 check(context.entered == 1 && context.hookCalls == 1 && context.hookFailures == 0 &&
1553 context.pushed == 1 && context.shutdowns == 1 && context.popped == 1 &&
1554 context.finished == 1 && context.terminalResult == 1 &&
1555 context.waiterUnlinked == 1 && context.queue.waiterCount() == 0 && joined,
1556 "nested-terminal-cancel-before-block",
1557 "shutdown cancelled a lower-state waiter without returning it from "
1558 "blockCurrent cleanly");
1561 "HOSTED-WAIT-TEST: PASS "
1562 "nested-terminal-cancel-before-block");
1567bool conditionVariableSignalBeforeBlock() {
1570 ConditionVariableHookContext context(&condition, &mutex,
1573 const bool initiallyAcquired = mutex.acquire();
1574 g_ConditionVariableContext = &context;
1575 WaitQueue::setBeforeBlockHook(conditionVariableSignalHook);
1577 ConditionVariable::Error waitError = ConditionVariable::NoError;
1578 const bool waitSucceeded = condition.
wait(mutex, waitError);
1580 WaitQueue::setBeforeBlockHook(
nullptr);
1581 g_ConditionVariableContext =
nullptr;
1585 const bool mutexWasUnlocked = mutex.tryAcquire();
1589 passed &= check(initiallyAcquired,
"condition-variable-pre-block",
1590 "the caller could not acquire its mutex");
1591 passed &= check(waitSucceeded && waitError == ConditionVariable::NoError,
1592 "condition-variable-pre-block",
"wait did not report a successful signal");
1593 passed &= check(context.hookCalls == 1,
"condition-variable-pre-block",
1594 "the pre-block hook did not run exactly once");
1595 passed &= check(context.hookFailures == 0,
"condition-variable-pre-block",
1596 "the signal window did not release the caller mutex");
1597 passed &= check(context.acquiredMutex == 1,
"condition-variable-pre-block",
1598 "the hook could not acquire the released caller mutex");
1599 passed &= check(!mutexWasUnlocked,
"condition-variable-pre-block",
1600 "wait returned without reacquiring the caller mutex");
1602 check(context.waiter && context.waiter->getStatus() == Thread::Running,
1603 "condition-variable-pre-block",
"the scheduler committed Sleeping after the signal");
1606 NOTICE(
"HOSTED-WAIT-TEST: PASS condition-variable-pre-block");
1611bool conditionVariableCompletionBarrier() {
1612 CompletionConditionContext context;
1615 &context,
nullptr,
false,
true,
true);
1616 waiter->setName(
"hosted terminal-pending CV completion waiter");
1618 const bool started =
waiter->start();
1620 const bool gateQueued = [&
waiter]() {
1621 for (
size_t i = 0; i < 10000; ++i) {
1623 uintptr_t debugAddress = 0;
1624 if (
waiter->getWaitDebugInfo(wait) && wait.queued &&
1625 waiter->getDebugState(debugAddress) == Thread::SemWait) {
1634 context.gate.release();
1636 const bool conditionQueued = [&
waiter]() {
1637 for (
size_t i = 0; i < 10000; ++i) {
1639 uintptr_t debugAddress = 0;
1640 if (
waiter->getWaitDebugInfo(wait) && wait.queued &&
1641 waiter->getDebugState(debugAddress) == Thread::CondWait) {
1649 const bool mutexAcquired = context.mutex.acquireForCompletion();
1650 context.predicate = 1;
1651 context.condition.signal();
1652 if (mutexAcquired) {
1653 context.mutex.release();
1655 const bool joined =
waiter->join();
1658 check(started && gateQueued && conditionQueued && mutexAcquired && joined &&
1659 context.entered == 1 && context.returned == 1 && context.terminalReported == 1 &&
1660 context.terminalMutexHeld == 1 && context.signalPreserved == 1,
1661 "condition-variable-completion-barrier",
1662 "terminal state escaped the barrier, mutex ownership was lost, or "
1663 "its signal marker was discarded");
1666 "HOSTED-WAIT-TEST: PASS "
1667 "condition-variable-completion-barrier");
1672bool conditionVariableContendedReacquire(
Thread*
waiter,
1673 ContendedConditionContext::Trigger trigger) {
1674 const char* test = trigger == ContendedConditionContext::Signal
1675 ?
"condition-variable-contended-signal-reacquire"
1676 :
"condition-variable-contended-timeout-reacquire";
1677 ContendedConditionContext context(
waiter, trigger);
1679 const bool initiallyAcquired = context.mutex.acquire();
1680 g_ContendedSignalHandlerCalls = 0;
1682 &context,
nullptr,
false,
true);
1683 holder->setName(
"hosted contended condition mutex holder");
1685 ConditionVariable::Error error = ConditionVariable::NoError;
1686 Time::Timestamp timeout = trigger == ContendedConditionContext::Timeout
1687 ? 100 * Time::Multiplier::Millisecond
1689 const bool waited = context.condition.wait(context.mutex, timeout, error);
1690 const bool mutexHeld = context.mutex.isOwnedByCurrentThread();
1692 context.mutex.release();
1696 const bool expectedError = trigger == ContendedConditionContext::Signal
1697 ? error == ConditionVariable::Interrupted
1698 : error == ConditionVariable::TimedOut;
1700 passed &= check(initiallyAcquired && !waited && expectedError, test,
1701 "the wait did not report its interruption after mutex reacquisition");
1702 passed &= check(mutexHeld, test,
"the interrupted wait returned without owning its mutex");
1703 passed &= check(joined && context.holderAcquired == 1 && context.conditionWaitPublished == 1 &&
1704 context.reacquireWaitPublished == 1 && context.holderReleased == 1,
1705 test,
"the contender did not hold the mutex across interruption");
1706 if (trigger == ContendedConditionContext::Signal) {
1707 passed &= check(context.triggerSent == 1 && g_ContendedSignalHandlerCalls == 1, test,
1708 "the published signal was not delivered exactly once");
1710 passed &= check(context.triggerSent == 0 && timeout == 0, test,
1711 "the timeout did not expire while the mutex was contended");
1715 if (trigger == ContendedConditionContext::Signal) {
1717 "HOSTED-WAIT-TEST: PASS "
1718 "condition-variable-contended-signal-reacquire");
1721 "HOSTED-WAIT-TEST: PASS "
1722 "condition-variable-contended-timeout-reacquire");
1728bool conditionVariableTerminalReacquire() {
1729 TerminalConditionContext context;
1731 &context,
nullptr,
false,
true,
true);
1732 waiter->setName(
"hosted terminal condition mutex waiter");
1734 const bool started =
waiter->start();
1736 const bool conditionQueued = waitForDebugState(
waiter, Thread::CondWait);
1737 const bool mutexAcquired = conditionQueued && context.mutex.acquireForCompletion();
1738 context.condition.signal();
1739 const bool reacquireQueued = waitForDebugState(
waiter, Thread::SemWait);
1741 if (mutexAcquired) {
1742 context.mutex.release();
1745 const bool joined =
waiter->joinForCompletion();
1746 const bool mutexRecoverable = context.mutex.tryAcquire();
1747 if (mutexRecoverable) {
1748 context.mutex.release();
1751 const bool passed = check(
1752 started && context.entered == 1 && conditionQueued && mutexAcquired && reacquireQueued &&
1753 joined && context.waitReturned == 1 && context.terminalResult == 1 &&
1754 context.mutexHeldOnReturn == 1 && context.destructed == 1 && mutexRecoverable,
1755 "condition-variable-terminal-reacquire",
1756 "terminal propagation did not return with mutex ownership and unwind the stack");
1759 "HOSTED-WAIT-TEST: PASS "
1760 "condition-variable-terminal-reacquire");
1765bool unlikelyLockWriterPreference() {
1767 UnlikelyLockFairnessContext context(&lock);
1771 &context,
nullptr,
false,
true);
1772 writer->setName(
"hosted queued UnlikelyLock writer");
1773 const bool writerQueued = waitForDebugState(writer, Thread::CondWait);
1776 &context,
nullptr,
false,
true);
1777 readerA->setName(
"hosted later UnlikelyLock reader A");
1779 &context,
nullptr,
false,
true);
1780 readerB->setName(
"hosted later UnlikelyLock reader B");
1781 const bool readerAQueued = waitForDebugState(readerA, Thread::CondWait);
1782 const bool readerBQueued = waitForDebugState(readerB, Thread::CondWait);
1789 return check(writerQueued && readerAQueued && readerBQueued && writerJoined && readerAJoined &&
1790 readerBJoined && context.writerOrder == 1 && context.writerReturned == 1 &&
1791 context.readersEntered == 2 && context.admissionSequence == 3,
1792 "unlikely-lock-admission",
"later readers bypassed a queued writer");
1795bool unlikelyLockAdmission() {
1800 UnlikelyLockHookContext
reader(&lock, UnlikelyLockHookContext::WriterHeld);
1801 g_UnlikelyLockContext = &
reader;
1802 WaitQueue::setBeforeBlockHook(unlikelyLockAdmissionHook);
1804 &
reader,
nullptr,
false,
true,
true);
1805 readerThread->setName(
"hosted UnlikelyLock reader");
1806 reader.thread = readerThread;
1807 const bool readerStarted = readerThread->
start();
1808 for (
size_t i = 0; i < 10000 &&
reader.hookCalls == 0; ++i) {
1812 WaitQueue::setBeforeBlockHook(
nullptr);
1813 g_UnlikelyLockContext =
nullptr;
1815 const bool readerJoined = readerThread->
join();
1816 WaitQueue::setBeforeBlockHook(
nullptr);
1817 g_UnlikelyLockContext =
nullptr;
1820 check(readerStarted && readerJoined &&
reader.hookCalls == 1 &&
reader.hookFailures == 0 &&
1822 "unlikely-lock-admission",
"reader admission lost a publication wake or signal marker");
1825 UnlikelyLockHookContext writer(&lock, UnlikelyLockHookContext::ReaderHeld);
1826 g_UnlikelyLockContext = &writer;
1827 WaitQueue::setBeforeBlockHook(unlikelyLockAdmissionHook);
1830 nullptr,
false,
true,
true);
1831 writerThread->setName(
"hosted terminal-pending UnlikelyLock writer");
1832 writer.thread = writerThread;
1833 const bool writerStarted = writerThread->
start();
1834 for (
size_t i = 0; i < 10000 && writer.hookCalls == 0; ++i) {
1837 if (writer.hookCalls) {
1838 WaitQueue::setBeforeBlockHook(
nullptr);
1839 g_UnlikelyLockContext =
nullptr;
1841 const bool writerJoined = writerThread->
join();
1842 WaitQueue::setBeforeBlockHook(
nullptr);
1843 g_UnlikelyLockContext =
nullptr;
1846 check(writerStarted && writerJoined && writer.hookCalls == 1 && writer.hookFailures == 0 &&
1847 writer.acquired == 1 && writer.returned == 1,
1848 "unlikely-lock-admission",
"writer admission abandoned ownership for terminal state");
1850 passed &= unlikelyLockWriterPreference();
1853 NOTICE(
"HOSTED-WAIT-TEST: PASS unlikely-lock-admission");
1858bool ordinaryBlockAndWake() {
1862 nullptr,
false,
true);
1863 waker->setName(
"hosted wait regression waker");
1865 __atomic_store_n(&g_BlockingContext, &context, __ATOMIC_RELEASE);
1866 WaitQueue::setBeforeBlockHook(blockingHook);
1867 auto guard = context.queue.acquire();
1868 const WaitQueue::WakeReason reason = guard.wait(
1869 context.channel, Thread::EventWait,
reinterpret_cast<uintptr_t
>(__builtin_return_address(0)));
1870 WaitQueue::setBeforeBlockHook(
nullptr);
1871 __atomic_store_n(&g_BlockingContext,
nullptr, __ATOMIC_RELEASE);
1873 const bool joined = waker->
join();
1876 passed &= check(reason == WaitQueue::WakeReason::Signalled,
"ordinary-block-wake",
1877 "wait returned the wrong wake reason");
1878 passed &= check(context.hookCalls == 1,
"ordinary-block-wake",
1879 "the publication hook did not run exactly once");
1880 passed &= check(context.hookFailures == 0,
"ordinary-block-wake",
1881 "the waker did not observe the expected wait state");
1882 passed &= check(context.workerWoke == 1,
"ordinary-block-wake",
1883 "the worker did not wake one published waiter");
1884 passed &= check(context.phase == 3,
"ordinary-block-wake",
1885 "the worker did not complete the blocking sequence");
1886 passed &= check(joined,
"ordinary-block-wake",
"the waker could not be joined");
1887 passed &= check(context.queue.waiterCount() == 0,
"ordinary-block-wake",
1888 "the completed waiter remained queued");
1891 NOTICE(
"HOSTED-WAIT-TEST: PASS ordinary-block-wake");
1896bool waitQueueWakeAndRequeue() {
1897 constexpr const char* Test =
"waitqueue-wake-and-requeue";
1898 RequeueWaitContext context;
1901 bool enrolled =
true;
1902 for (
size_t i = 0; i < 3; ++i) {
1904 nullptr,
false,
true);
1905 waiters[i]->setName(
"hosted WaitQueue requeue waiter");
1908 enrolled &= waitForDebugState(
waiter, Thread::FutexWait);
1912 bool sourceReachedMovedWaiter =
true;
1913 size_t destinationWoken = 0;
1915 auto guard = context.queue.acquire();
1916 moved = guard.wakeAndRequeue(context.source, 1, context.destination, 2);
1917 sourceReachedMovedWaiter = guard.wakeOne(WaitQueue::WakeReason::Signalled, context.source);
1918 destinationWoken = guard.wakeAll(WaitQueue::WakeReason::Signalled, context.destination);
1921 if (!enrolled || moved != 3 || sourceReachedMovedWaiter || destinationWoken != 2) {
1929 joined &=
waiter->joinForCompletion();
1933 passed &= check(enrolled, Test,
"the source waiters did not all publish");
1934 passed &= check(moved == 3, Test,
"wake-and-requeue did not select all three waiters");
1935 passed &= check(!sourceReachedMovedWaiter, Test,
1936 "the source channel reached a completed or requeued waiter");
1937 passed &= check(destinationWoken == 2, Test,
1938 "the destination did not wake exactly the two requeued waiters");
1939 passed &= check(joined && context.entered == 3 && context.returned == 3 &&
1940 context.signalled == 3 && context.terminated == 0,
1941 Test,
"the requeued waiters did not all return as signalled");
1943 check(context.queue.waiterCount() == 0, Test,
"completed requeued waiters remained linked");
1945 RequeueWaitContext cancellation;
1947 &cancellation,
nullptr,
false,
true);
1948 cancelled->setName(
"hosted WaitQueue cancelled requeue waiter");
1949 const bool cancellationEnrolled = waitForDebugState(cancelled, Thread::FutexWait);
1950 size_t cancellationMoved = 0;
1951 if (cancellationEnrolled) {
1952 auto guard = cancellation.queue.acquire();
1953 cancellationMoved = guard.wakeAndRequeue(cancellation.source, 0, cancellation.destination, 1);
1958 wait.queue == &cancellation.queue && wait.queued &&
1959 wait.reason == WaitQueue::WakeReason::Waiting &&
1960 wait.channelOwner == cancellation.destination.owner &&
1961 wait.channelValue == cancellation.destination.value;
1965 passed &= check(cancellationEnrolled && cancellationMoved == 1 && destinationPublished, Test,
1966 "the cancellation waiter was not requeued to the destination");
1967 passed &= check(cancellationJoined && cancellation.returned == 1 && cancellation.signalled == 0 &&
1968 cancellation.terminated == 1 && cancellation.queue.waiterCount() == 0,
1969 Test,
"cancelling a requeued waiter did not unlink it safely");
1972 NOTICE(
"HOSTED-WAIT-TEST: PASS waitqueue-wake-and-requeue");
1977bool processSuspendResume() {
1985 auto consumeContinued = [process]() {
1989 transition.kind == Process::ChildTransitionKind::Continued &&
1990 !transition.stopSignal;
1992 return stopWasNotSelected && continuedSelected && consumedExactlyOnce;
1995 ProcessSuspendContext blocking(process, ProcessSuspendContext::ObserveBlock);
1996 Thread* resumer =
new Thread(process, resumeBlockedProcess, &blocking,
nullptr,
false,
true);
1997 resumer->setName(
"hosted process resume regression");
1999 g_ProcessSuspendContext = &blocking;
2000 WaitQueue::setBeforeBlockHook(processSuspendHook);
2002 WaitQueue::setBeforeBlockHook(
nullptr);
2003 g_ProcessSuspendContext =
nullptr;
2006 check(resumer->
join(),
"process-suspend-resume",
"the ordinary resumer could not be joined");
2008 check(blocking.hookCalls == 1 && blocking.hookFailures == 0 && blocking.phase == 2 &&
2009 blocking.resumes == 1,
2010 "process-suspend-resume",
"the ordinary suspend did not block and resume exactly once");
2011 const bool blockingTransition = consumeContinued();
2013 check(process->getState() == Process::Active && blockingTransition,
"process-suspend-resume",
2014 "ordinary resume did not replace and exclusively report the pending stop");
2016 ProcessSuspendContext immediate(process, ProcessSuspendContext::ResumeBeforeBlock);
2017 g_ProcessSuspendContext = &immediate;
2018 WaitQueue::setBeforeBlockHook(processSuspendHook);
2020 WaitQueue::setBeforeBlockHook(
nullptr);
2021 g_ProcessSuspendContext =
nullptr;
2023 passed &= check(immediate.hookCalls == 1 && immediate.hookFailures == 0 && immediate.resumes == 1,
2024 "process-suspend-resume",
"resume in the publication window was not retained");
2025 const bool immediateTransition = consumeContinued();
2027 check(process->getState() == Process::Active && immediateTransition,
"process-suspend-resume",
2028 "pre-block resume did not replace and exclusively report the pending stop");
2030 ProcessSuspendContext eventThenResume(process, ProcessSuspendContext::EventThenResume);
2031 g_ProcessSuspendContext = &eventThenResume;
2032 WaitQueue::setBeforeBlockHook(processSuspendHook);
2034 WaitQueue::setBeforeBlockHook(
nullptr);
2035 g_ProcessSuspendContext =
nullptr;
2038 check(eventThenResume.hookCalls == 2 && eventThenResume.hookFailures == 0 &&
2039 eventThenResume.eventWakes == 1 && eventThenResume.resumes == 1,
2040 "process-suspend-resume",
"an event wake bypassed the Suspended predicate recheck");
2041 const bool eventTransition = consumeContinued();
2043 check(process->getState() == Process::Active && eventTransition,
"process-suspend-resume",
2044 "event/recheck resume did not replace and exclusively report the pending stop");
2047 NOTICE(
"HOSTED-WAIT-TEST: PASS process-suspend-resume");
2052bool processStopGatesPeerReturns() {
2053 constexpr const char* Test =
"process-stop-gates-peer-returns";
2057 ProcessStopReturnGateContext gateContext;
2059 new Thread(process, waitAtProcessStopReturnGate, &gateContext,
nullptr,
false,
true,
true);
2060 first->setName(
"hosted stopped return-gate peer one");
2062 new Thread(process, waitAtProcessStopReturnGate, &gateContext,
nullptr,
false,
true,
true);
2063 second->setName(
"hosted stopped return-gate peer two");
2065 ProcessStopOwnerContext ownerContext(process);
2066 Thread* owner =
new Thread(process, ownProcessStop, &ownerContext,
nullptr,
false,
true,
true);
2067 owner->setName(
"hosted process-stop owner");
2069 const bool firstStarted = first->
start();
2070 const bool secondStarted = second->
start();
2071 const bool peersWaiting = firstStarted && secondStarted &&
2072 waitForDebugState(first, Thread::SemWait) &&
2073 waitForDebugState(second, Thread::SemWait) && gateContext.entered == 2;
2074 const bool ownerStarted = peersWaiting && owner->start();
2075 const Time::Timestamp stopDeadline = Time::getTicks() + (2 * Time::Multiplier::Second);
2076 while (ownerStarted && !process->isSuspended() && !ownerContext.returned &&
2077 Time::getTicks() < stopDeadline) {
2080 const bool suspended = process->isSuspended();
2081 const bool ownerWaiting = suspended && waitForDebugState(owner, Thread::ProcessWait);
2083 gateContext.release.release(2);
2084 const bool firstGated = suspended && waitForDebugState(first, Thread::ProcessWait);
2085 const bool secondGated = suspended && waitForDebugState(second, Thread::ProcessWait);
2086 for (
size_t attempt = 0; attempt < 32; ++attempt) {
2089 const bool bothHeld = firstGated && secondGated && !gateContext.returned &&
2090 !gateContext.crossings && !gateContext.terminalReturns;
2092 bool resumed =
false;
2093 if (process->isSuspended()) {
2095 resumed = process->getState() == Process::Active;
2096 }
else if (ownerStarted && !ownerContext.returned) {
2100 const bool ownerJoined = ownerStarted && owner->joinForCompletion();
2102 if (process->isSuspended()) {
2107 if (!firstStarted) {
2110 if (!secondStarted) {
2113 if (!ownerStarted) {
2117 const bool passed = check(
2118 firstStarted && secondStarted && peersWaiting && ownerStarted && suspended && ownerWaiting &&
2119 bothHeld && resumed && firstJoined && secondJoined && ownerJoined &&
2120 gateContext.returned == 2 && gateContext.crossings == 2 && !gateContext.terminalReturns &&
2121 !gateContext.failures && ownerContext.entered == 1 && ownerContext.returned == 1,
2123 "a running process peer crossed its user-return gate while stopped or failed to resume");
2127 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
2132bool stoppedProcessDefersUserReturnEvent() {
2133 constexpr const char* Test =
"stopped-process-defers-user-return-event";
2137 ProcessStopReturnGateContext gateContext;
2139 new Thread(process, waitAtProcessStopReturnGate, &gateContext,
nullptr,
false,
true,
true);
2140 peer->setName(
"hosted stopped user-event return-gate peer");
2141 ProcessStopOwnerContext ownerContext(process);
2142 Thread* owner =
new Thread(process, ownProcessStop, &ownerContext,
nullptr,
false,
true,
true);
2143 owner->setName(
"hosted user-event process-stop owner");
2145 g_StoppedUserHandlerCalls = 0;
2146 const bool peerStarted = peer->
start();
2147 const bool peerWaiting =
2148 peerStarted && waitForDebugState(peer, Thread::SemWait) && gateContext.entered == 1;
2149 const bool ownerStarted = peerWaiting && owner->start();
2150 const Time::Timestamp stopDeadline = Time::getTicks() + (2 * Time::Multiplier::Second);
2151 while (ownerStarted && !process->isSuspended() && !ownerContext.returned &&
2152 Time::getTicks() < stopDeadline) {
2155 const bool suspended = process->isSuspended();
2156 const bool ownerWaiting = suspended && waitForDebugState(owner, Thread::ProcessWait);
2158 StoppedUserReturnEvent userEvent;
2159 const bool eventQueued = ownerWaiting && peer->
sendEvent(&userEvent);
2160 const bool remainedOnOriginalWait =
2161 eventQueued && waitForDebugState(peer, Thread::SemWait) && peer->
hasEvent(&userEvent);
2162 gateContext.release.release();
2163 const bool peerGated = remainedOnOriginalWait && waitForDebugState(peer, Thread::ProcessWait);
2164 for (
size_t attempt = 0; attempt < 32; ++attempt) {
2167 const bool heldWhileStopped = peerGated && process->isSuspended() && !gateContext.returned &&
2168 peer->
hasEvent(&userEvent) && !g_StoppedUserHandlerCalls;
2170 bool resumed =
false;
2171 if (process->isSuspended()) {
2173 resumed = process->getState() == Process::Active;
2174 }
else if (ownerStarted && !ownerContext.returned) {
2178 const bool ownerJoined = ownerStarted && owner->joinForCompletion();
2182 if (!ownerStarted) {
2186 const bool passed = check(
2187 peerStarted && peerWaiting && ownerStarted && suspended && ownerWaiting && eventQueued &&
2188 remainedOnOriginalWait && heldWhileStopped && resumed && peerJoined && ownerJoined &&
2189 gateContext.returned == 1 && gateContext.crossings == 1 && !gateContext.terminalReturns &&
2190 !gateContext.failures && !g_StoppedUserHandlerCalls && ownerContext.entered == 1 &&
2191 ownerContext.returned == 1,
2192 Test,
"a stopped process entered or consumed a userspace event before resume");
2196 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
2201bool terminalUnwindEscapesProcessStopGate() {
2202 constexpr const char* Test =
"terminal-unwind-escapes-process-stop-gate";
2206 ProcessStopReturnGateContext gateContext;
2208 new Thread(process, waitAtProcessStopReturnGate, &gateContext,
nullptr,
false,
true,
true);
2209 peer->setName(
"hosted terminal stopped return-gate peer");
2210 ProcessStopOwnerContext ownerContext(process);
2211 Thread* owner =
new Thread(process, ownProcessStop, &ownerContext,
nullptr,
false,
true,
true);
2212 owner->setName(
"hosted terminal process-stop owner");
2214 const bool peerStarted = peer->
start();
2215 const bool peerWaiting =
2216 peerStarted && waitForDebugState(peer, Thread::SemWait) && gateContext.entered == 1;
2217 const bool ownerStarted = peerWaiting && owner->start();
2218 const Time::Timestamp stopDeadline = Time::getTicks() + (2 * Time::Multiplier::Second);
2219 while (ownerStarted && !process->isSuspended() && !ownerContext.returned &&
2220 Time::getTicks() < stopDeadline) {
2223 const bool suspended = process->isSuspended();
2224 const bool ownerWaiting = suspended && waitForDebugState(owner, Thread::ProcessWait);
2226 gateContext.release.release();
2227 const bool peerGated = suspended && waitForDebugState(peer, Thread::ProcessWait);
2231 const Time::Timestamp terminalDeadline = Time::getTicks() + (2 * Time::Multiplier::Second);
2232 while (peerGated && !gateContext.returned && Time::getTicks() < terminalDeadline) {
2235 const bool returnedWhileStopped =
2236 peerGated && gateContext.returned == 1 && process->isSuspended();
2237 const bool terminalWon = returnedWhileStopped && gateContext.terminalReturns == 1 &&
2238 !gateContext.crossings && !gateContext.failures;
2240 if (!gateContext.returned && peerStarted) {
2243 if (process->isSuspended()) {
2245 }
else if (ownerStarted && !ownerContext.returned) {
2250 const bool ownerJoined = ownerStarted && owner->joinForCompletion();
2254 if (!ownerStarted) {
2257 if (process->isSuspended()) {
2262 check(peerStarted && peerWaiting && ownerStarted && suspended && ownerWaiting && peerGated &&
2263 returnedWhileStopped && terminalWon && peerJoined && ownerJoined &&
2264 gateContext.entered == 1 && gateContext.returned == 1 &&
2265 ownerContext.entered == 1 && ownerContext.returned == 1,
2266 Test,
"terminal unwind did not escape a stopped process gate before resume");
2270 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
2275bool prequeuedTerminalEventEscapesProcessStopGate() {
2276 constexpr const char* Test =
"prequeued-terminal-event-before-resume";
2280 ProcessStopReturnGateContext gateContext;
2281 Thread* peer =
new Thread(process, waitAtProcessStopReturnGateWithDeferredEvents, &gateContext,
2282 nullptr,
false,
true,
true);
2283 peer->setName(
"hosted prequeued-terminal stopped return-gate peer");
2284 ProcessStopOwnerContext ownerContext(process);
2285 Thread* owner =
new Thread(process, ownProcessStop, &ownerContext,
nullptr,
false,
true,
true);
2286 owner->setName(
"hosted prequeued-terminal process-stop owner");
2288 const bool peerStarted = peer->
start();
2289 const bool peerWaiting =
2290 peerStarted && waitForDebugState(peer, Thread::SemWait) && gateContext.entered == 1;
2291 const bool ownerStarted = peerWaiting && owner->start();
2292 const Time::Timestamp stopDeadline = Time::getTicks() + (2 * Time::Multiplier::Second);
2293 while (ownerStarted && !process->isSuspended() && !ownerContext.returned &&
2294 Time::getTicks() < stopDeadline) {
2297 const bool suspended = process->isSuspended();
2298 const bool ownerWaiting = suspended && waitForDebugState(owner, Thread::ProcessWait);
2300 SignalEvent terminalEvent(
reinterpret_cast<uintptr_t
>(&terminateStoppedReturnGatePeer), 9, ~0UL,
2301 0,
true,
false, Event::HandlerPrivilege::Kernel,
2302 SignalEvent::DeliveryDisposition::DefaultAction);
2303 const bool terminalQueued = ownerWaiting && peer->
sendEvent(&terminalEvent);
2304 const bool remainedPrequeued =
2305 terminalQueued && waitForDebugState(peer, Thread::SemWait) && !gateContext.returned;
2307 gateContext.release.release();
2308 const Time::Timestamp terminalDeadline = Time::getTicks() + (2 * Time::Multiplier::Second);
2309 while (remainedPrequeued && !gateContext.returned && Time::getTicks() < terminalDeadline) {
2312 const bool returnedWhileStopped = remainedPrequeued && gateContext.returned == 1 &&
2313 process->isSuspended() && !ownerContext.returned;
2314 const bool terminalWon = returnedWhileStopped && gateContext.terminalReturns == 1 &&
2315 !gateContext.crossings && !gateContext.failures;
2317 if (!gateContext.returned && peerStarted) {
2321 if (process->isSuspended()) {
2323 }
else if (ownerStarted && !ownerContext.returned) {
2326 const bool ownerJoined = ownerStarted && owner->joinForCompletion();
2330 if (!ownerStarted) {
2335 check(peerStarted && peerWaiting && ownerStarted && suspended && ownerWaiting &&
2336 terminalQueued && remainedPrequeued && terminalWon && peerJoined && ownerJoined &&
2337 gateContext.entered == 1 && gateContext.returned == 1 &&
2338 ownerContext.entered == 1 && ownerContext.returned == 1,
2339 Test,
"a terminal event queued before gate enrollment was lost behind process stop");
2343 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
2348bool prequeuedTerminalEventEscapesStopOwnerGate() {
2349 constexpr const char* Test =
"prequeued-terminal-event-before-stop-owner-wait";
2353 PrequeuedStopOwnerContext context(process);
2354 Thread* owner =
new Thread(process, stopOwnerWithPrequeuedTerminalEvent, &context,
nullptr,
false,
2356 owner->setName(
"hosted prequeued-terminal process-stop owner");
2358 const bool started = owner->start();
2359 const Time::Timestamp terminalDeadline = Time::getTicks() + (2 * Time::Multiplier::Second);
2360 while (started && !context.returned && Time::getTicks() < terminalDeadline) {
2363 const bool returnedWhileStopped = started && context.returned == 1 && process->isSuspended();
2364 const bool terminalWon = returnedWhileStopped && context.entered == 1 &&
2365 context.eventsQueued == 1 && context.terminalReturns == 1 &&
2368 if (!context.returned && started) {
2371 const bool joined = started && owner->joinForCompletion();
2372 if (process->isSuspended()) {
2380 check(started && terminalWon && joined, Test,
2381 "a terminal event queued before the stop owner enrolled was lost behind process stop");
2385 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
2390bool activeDirectTransitionDrainsKernelEvent() {
2391 constexpr const char* Test =
"active-direct-transition-drains-kernel-event";
2395 PrequeuedStopOwnerContext context(process);
2396 Thread* thread =
new Thread(process, activeDirectTransitionWithPrequeuedTerminalEvent, &context,
2397 nullptr,
false,
true,
true);
2398 thread->setName(
"hosted active direct-transition terminal gate");
2400 const bool started = thread->
start();
2401 const Time::Timestamp terminalDeadline = Time::getTicks() + (2 * Time::Multiplier::Second);
2402 while (started && !context.returned && Time::getTicks() < terminalDeadline) {
2405 const bool terminalWon = started && context.returned == 1 && context.entered == 1 &&
2406 context.eventsQueued == 1 && context.terminalReturns == 1 &&
2407 !context.failures && process->getState() == Process::Active;
2409 if (!context.returned && started) {
2418 check(terminalWon && joined, Test,
2419 "an active direct userspace transition bypassed a queued kernel terminal event");
2423 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
2428bool prequeuedEventDispatch() {
2429 Thread* thread = g_ImmediateWaiter;
2430 if (!check(thread !=
nullptr,
"prequeued-event",
2431 "the suite did not capture its current thread")) {
2435 const bool passed = check(thread->runHostedPrequeuedEventRegression(),
"prequeued-event",
2436 "a ready event was not dispatched exactly once without blocking");
2439 NOTICE(
"HOSTED-WAIT-TEST: PASS prequeued-event");
2444bool stateLevelPublication() {
2445 Thread* thread = g_ImmediateWaiter;
2446 if (!check(thread !=
nullptr,
"state-level-publication",
2447 "the suite did not capture its current thread")) {
2452 check(thread->runHostedStatePublicationRegression(),
"state-level-publication",
2453 "an event sender observed a partially published state level");
2456 NOTICE(
"HOSTED-WAIT-TEST: PASS state-level-publication");
2461bool stateCleanupOrder() {
2462 Thread* thread = g_ImmediateWaiter;
2463 if (!check(thread !=
nullptr,
"state-cleanup-order",
2464 "the suite did not capture its current thread")) {
2468 const bool passed = check(thread->runHostedStateCleanupRegression(),
"state-cleanup-order",
2469 "cleanup was not LIFO, checkpointed, or level-scoped");
2471 NOTICE(
"HOSTED-WAIT-TEST: PASS state-cleanup-order");
2476bool execStackOwnership() {
2477 Thread* thread = g_ImmediateWaiter;
2478 if (!check(thread !=
nullptr,
"exec-stack-ownership",
2479 "the suite did not capture its current thread")) {
2484 check(thread->runHostedExecStackOwnershipRegression(),
"exec-stack-ownership",
2485 "exec did not discard stale nested stack metadata or adopt the replacement at base");
2487 NOTICE(
"HOSTED-WAIT-TEST: PASS exec-stack-ownership");
2492bool activeEventDeliveryLease() {
2493 Thread* thread = g_ImmediateWaiter;
2494 if (!check(thread !=
nullptr,
"event-delivery-lease",
2495 "the suite did not capture its current thread")) {
2499 const bool passed = check(thread->runHostedEventDeliveryLeaseRegression(),
"event-delivery-lease",
2500 "a dequeued Event was not retained through scheduler dispatch");
2503 NOTICE(
"HOSTED-WAIT-TEST: PASS event-delivery-lease");
2508bool eventQueueShutdown() {
2509 Thread* thread = g_ImmediateWaiter;
2510 if (!check(thread !=
nullptr,
"event-shutdown-drain",
2511 "the suite did not capture its current thread")) {
2515 const bool passed = check(thread->runHostedEventShutdownRegression(),
"event-shutdown-drain",
2516 "thread shutdown did not reject or drain queued event deliveries");
2519 NOTICE(
"HOSTED-WAIT-TEST: PASS event-shutdown-drain");
2524bool immediateExitJoinLifecycle() {
2525 constexpr size_t JoinIterations = 4;
2528 for (
size_t iteration = 0; iteration < JoinIterations; ++iteration) {
2530 nullptr,
nullptr,
false,
true);
2531 thread->setName(
"hosted immediate-exit join regression");
2533 while (g_ImmediateThreadExits < (iteration + 1)) {
2539 passed &= check(thread->
join(),
"thread-join-lifecycle",
2540 "an immediate-exit thread could not be joined");
2543 passed &= check(g_ImmediateThreadExits == JoinIterations,
"thread-join-lifecycle",
2544 "the repeated immediate-exit entries did not all run");
2547 NOTICE(
"HOSTED-WAIT-TEST: PASS thread-join-lifecycle");
2552bool joinPublicationAndDetachExclusion() {
2554 JoinPublicationContext context;
2558 new Thread(process, blockedJoinPublicationTarget, &context,
nullptr,
false,
true);
2559 context.target->setName(
"hosted join publication target");
2561 constexpr size_t Attempts = 10000;
2562 bool targetBlocked =
false;
2563 for (
size_t attempt = 0; attempt < Attempts; ++attempt) {
2564 if (context.targetEntered == 1 && waitForDebugState(context.target, Thread::SemWait)) {
2565 targetBlocked =
true;
2570 passed &= check(targetBlocked,
"thread-join-pre-block-wake",
2571 "the target did not publish its completion gate");
2572 if (!targetBlocked) {
2573 context.targetGate.release();
2574 context.target->join();
2578 context.joiner =
new Thread(process, joinPublicationTarget, &context,
nullptr,
false,
true,
true);
2579 context.joiner->setName(
"hosted exact-window joiner");
2581 __atomic_store_n(&g_JoinPublicationContext, &context, __ATOMIC_RELEASE);
2582 WaitQueue::setBeforeBlockHook(joinPublicationHook);
2583 const bool joinerStarted = context.joiner->start();
2585 bool joinerCompleted =
false;
2586 for (
size_t attempt = 0; attempt < Attempts; ++attempt) {
2587 if (context.joinReturned == 1) {
2588 joinerCompleted =
true;
2593 WaitQueue::setBeforeBlockHook(
nullptr);
2594 __atomic_store_n(&g_JoinPublicationContext,
static_cast<JoinPublicationContext*
>(
nullptr),
2597 const bool joinerJoined = context.joiner->join();
2598 passed &= check(joinerStarted && joinerCompleted && joinerJoined && context.hookCalls == 1 &&
2599 context.hookFailures == 0 && context.targetReapableInHook == 1 &&
2600 context.joinSucceeded == 1,
2601 "thread-join-pre-block-wake",
2602 "a target wake in join's published-before-sleep window was lost");
2603 passed &= check(context.detachRejected == 1,
"thread-join-detach-exclusion",
2604 "detach stole a target after an exclusive join claim was published");
2607 NOTICE(
"HOSTED-WAIT-TEST: PASS thread-join-pre-block-wake");
2608 NOTICE(
"HOSTED-WAIT-TEST: PASS thread-join-detach-exclusion");
2613bool terminalJoinPropagation() {
2616 JoinPropagationContext context(&targetGate);
2619 context.target =
new Thread(process, blockedJoinTarget, &context,
nullptr,
false,
true);
2620 context.target->setName(
"hosted join-propagation target");
2622 while (context.targetEntered != 1) {
2626 Thread* interruptedJoiner =
new Thread(process, joinTarget, &context,
nullptr,
false,
true);
2627 interruptedJoiner->setName(
"hosted terminal join-propagation waiter");
2629 bool enrolled =
false;
2630 constexpr size_t EnrolmentAttempts = 10000;
2631 for (
size_t attempt = 0; attempt < EnrolmentAttempts; ++attempt) {
2633 uintptr_t debugAddress = 0;
2634 if (interruptedJoiner->
getWaitDebugInfo(info) && info.queue && info.queued &&
2635 interruptedJoiner->
getDebugState(debugAddress) == Thread::Joining &&
2636 interruptedJoiner->
getStatus() == Thread::Sleeping) {
2642 passed &= check(enrolled,
"join-terminal-propagation",
2643 "the first joiner did not publish its join wait");
2646 passed &= check(interruptedJoiner->
joinForCompletion(),
"join-terminal-propagation",
2647 "the terminated joiner did not become reapable");
2649 context.joinReturned == 1 && context.joinSucceeded == 0 && context.joinerDestructed == 1,
2650 "join-terminal-propagation",
"the interrupted join did not return and unwind its stack");
2652 Thread* replacementJoiner =
new Thread(process, joinTarget, &context,
nullptr,
false,
true);
2653 replacementJoiner->setName(
"hosted replacement join waiter");
2655 bool replacementEnrolled =
false;
2656 for (
size_t attempt = 0; attempt < EnrolmentAttempts; ++attempt) {
2658 if (replacementJoiner->
getWaitDebugInfo(info) && info.queue && info.queued &&
2659 replacementJoiner->
getStatus() == Thread::Sleeping) {
2660 replacementEnrolled =
true;
2665 passed &= check(replacementEnrolled,
"join-terminal-propagation",
2666 "the interrupted exclusive join claim blocked a replacement joiner");
2668 targetGate.release();
2669 passed &= check(replacementJoiner->
join(),
"join-terminal-propagation",
2670 "the replacement joiner did not become reapable");
2672 context.joinReturned == 2 && context.joinSucceeded == 1 && context.joinerDestructed == 2,
2673 "join-terminal-propagation",
"the replacement join did not consume the released claim");
2676 NOTICE(
"HOSTED-WAIT-TEST: PASS join-terminal-propagation");
2683bool runHostedWaitRegressions() {
2684 NOTICE(
"HOSTED-WAIT-TEST: BEGIN");
2687 runHostedSpinlockRegressions() && runHostedElfValidationRegressions() &&
2688#if !PEDIGREE_HOSTED_CORE_SMOKE
2689 runHostedPs2ControllerRegressions() &&
2691 wakeBeforeBlock() && semaphoreReleaseBeforeBlock() && semaphoreReleaseBeforeIntent() &&
2692 semaphorePartialReleaseBeforeBlock() && terminalCancellationReturns() &&
2693 publishedWaitDiscardCleanup() && terminalCancelBeforeBlock() &&
2694 nestedTerminalShutdownBeforeBlock() && conditionVariableSignalBeforeBlock() &&
2695 runHostedRingBufferRegressions() && runHostedBufferRegressions() &&
2696 runHostedAtaPioRegressions() &&
2698 .getBootstrapProcessorScheduler()
2699 ->runHostedNewThreadWorkerRegressions() &&
2700#if !PEDIGREE_HOSTED_CORE_SMOKE
2701 runHostedSchedulerRegressions() &&
2703 runHostedRequestQueueRegressions() &&
2704#if PEDIGREE_HOSTED_CORE_SMOKE
2705 runHostedStoragePageRegressions() && runHostedCacheDiscardRegressions() &&
2706 runHostedCacheTimerRegressions() && runHostedCacheDirectWritebackRegressions() &&
2708#if !PEDIGREE_HOSTED_CORE_SMOKE
2709 runHostedUsbCallbackDeliveryRegressions() && runHostedUsbHcdPortChangeRegressions() &&
2710 runHostedUsbTransferLifecycleRegressions() && runHostedMutexRegressions() &&
2711 runHostedInterruptManagerRegressions() && runHostedInputRegressions() &&
2712 runNetworkFilterConcurrencyRegressions() && runVfsCallbackLifetimeRegressions() &&
2713 runHostedCacheRegressions() && runHostedFatSectorRegressions() &&
2714 runHostedRawFsContractRegressions() && runHostedUnixDatagramRegressions() &&
2716 runHostedLifetimeLeaseRegressions() &&
2717#if !PEDIGREE_HOSTED_CORE_SMOKE
2718 runHostedLogRegressions() &&
2720 runHostedProcessExitRegressions() && runHostedPs2MousePacketRegressions() &&
2721#if !PEDIGREE_HOSTED_CORE_SMOKE
2722 runHostedPs2MouseRegressions() &&
2724 conditionVariableContendedReacquire(g_ImmediateWaiter, ContendedConditionContext::Signal) &&
2725 conditionVariableContendedReacquire(g_ImmediateWaiter, ContendedConditionContext::Timeout) &&
2726 conditionVariableTerminalReacquire() && conditionVariableCompletionBarrier() &&
2727 unlikelyLockAdmission() && runHostedPageFaultRegressions() &&
2728 runHostedPayloadPageRegressions() &&
2729#if !PEDIGREE_HOSTED_CORE_SMOKE
2730 runHostedPipeRegressions() && runHostedUsbSyncOwnershipRegression() &&
2731 runHostedUsbInterruptOwnershipRegression() && runHostedUsbContainerOwnershipRegression() &&
2732 runHostedUsbEndpointHaltRegressions() && runHostedUsbBotRegressions() &&
2733 runHostedHidReportRegressions() && runHostedScsiWriteRegressions() &&
2734 UsbPnP::runHostedRegistrationRegression() &&
2736 runHostedTimerRegressions(g_ImmediateWaiter) &&
2737 runHostedPrimitiveRegressions(g_ImmediateWaiter) &&
2738 runHostedSignalInterruptionRegressions(g_ImmediateWaiter) &&
2739#if !PEDIGREE_HOSTED_CORE_SMOKE
2740 runHostedSyscallRegressions() &&
2742 ordinaryBlockAndWake() && waitQueueWakeAndRequeue() && processSuspendResume() &&
2743 processStopGatesPeerReturns() && stoppedProcessDefersUserReturnEvent() &&
2744 terminalUnwindEscapesProcessStopGate() && prequeuedTerminalEventEscapesProcessStopGate() &&
2745 prequeuedTerminalEventEscapesStopOwnerGate() && activeDirectTransitionDrainsKernelEvent() &&
2746 immediateExitJoinLifecycle() && joinPublicationAndDetachExclusion() &&
2747 terminalJoinPropagation() && prequeuedEventDispatch() && stateLevelPublication() &&
2748 stateCleanupOrder() && execStackOwnership() && activeEventDeliveryLease() &&
2749 eventQueueShutdown();
2751 NOTICE(
"HOSTED-WAIT-TEST: PASS all");
2753 ERROR(
"HOSTED-WAIT-TEST: FAIL suite");
MUST_USE_RESULT bool wait(Mutex &mutex, Time::Timestamp &timeout, Error &error, WaitQueue::StackDiscardCleanup onStackDiscard=nullptr, void *stackDiscardContext=nullptr)
virtual size_t getNumber()=0
virtual size_t serialize(uint8_t *pBuffer)=0
void checkEventState(uintptr_t userStack)
void abandonCurrentThreadStack(StackDiscardReason reason, Spinlock *pLock=0) NORETURN
MUST_USE_RESULT bool serviceProcessStopAtUserReturn(ProcessStopGateMode mode=ProcessStopGateMode::StopOnly)
static size_t stackDiscardCount()
bool takePendingChildTransition(bool includeStopped, bool includeContinued, ChildTransition &transition)
void suspend(int stopSignal=0)
static ProcessorInformation & information()
static Scheduler & instance()
bool acquire(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
bool acquire(bool recurse=false, bool safe=true)
void setUnwindState(UnwindType ut)
@ Continue
No unwind necessary, carry on as normal.
@ TerminateThread
Exit only this thread during Process exit.
bool getWaitDebugInfo(WaitDebugInfo &info)
bool hasEvent(Event *pEvent)
UnwindType getUnwindState()
void cullEvent(Event *pEvent)
DebugState getDebugState(uintptr_t &address)
Process * getParent() const
void popState(bool clean=true)
class PerProcessorScheduler * getScheduler() const
SchedulerState * pushState()
bool sendEvent(Event *pEvent)
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)