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/Ipc.h"
18#include "pedigree/kernel/process/Mutex.h"
19#include "pedigree/kernel/process/PerProcessorScheduler.h"
20#include "pedigree/kernel/process/Process.h"
21#include "pedigree/kernel/process/Scheduler.h"
22#include "pedigree/kernel/process/Semaphore.h"
23#include "pedigree/kernel/process/SignalEvent.h"
24#include "pedigree/kernel/process/TerminationDeferral.h"
25#include "pedigree/kernel/process/Thread.h"
26#include "pedigree/kernel/process/Uninterruptible.h"
27#include "pedigree/kernel/process/WaitQueue.h"
28#include "pedigree/kernel/processor/Processor.h"
29#include "pedigree/kernel/time/Time.h"
32bool runHostedRequestQueueRegressions();
33bool runHostedAtaPioRegressions();
34bool runHostedBufferRegressions();
35bool runHostedElfValidationRegressions();
36bool runHostedLifetimeLeaseRegressions();
37bool runHostedMutexRegressions();
38bool runHostedSpinlockRegressions();
39bool runHostedPageFaultRegressions();
40bool runHostedPayloadPageRegressions();
41bool runHostedPrimitiveRegressions(
Thread* thread);
42bool runHostedRingBufferRegressions();
43bool runHostedProcessExitRegressions();
44bool runHostedPs2MousePacketRegressions();
45bool runHostedSignalInterruptionRegressions(
Thread* thread);
46bool runHostedTimerRegressions(
Thread* thread);
47#if PEDIGREE_HOSTED_CORE_SMOKE
48bool runHostedStoragePageRegressions();
49bool runHostedCacheDiscardRegressions();
50bool runHostedCacheTimerRegressions();
52#if !PEDIGREE_HOSTED_CORE_SMOKE
53bool runHostedSchedulerRegressions();
54bool runHostedCacheRegressions();
55bool runHostedCdiIrqRegressions();
56bool runHostedInputRegressions();
57bool runHostedInterruptManagerRegressions();
58EXPORTED_PUBLIC
bool runHostedFatSectorRegressions();
59EXPORTED_PUBLIC
bool runHostedRawFsContractRegressions();
60EXPORTED_PUBLIC
bool runHostedUnixDatagramRegressions();
61bool runHostedLogRegressions();
62bool runNetworkFilterConcurrencyRegressions();
63bool runVfsCallbackLifetimeRegressions();
64bool runHostedPipeRegressions();
65bool runHostedPs2ControllerRegressions();
66bool runHostedPs2MouseRegressions();
67bool runHostedSyscallRegressions();
68bool runHostedUsbCallbackDeliveryRegressions();
69bool runHostedUsbHcdPortChangeRegressions();
70bool runHostedUsbTransferLifecycleRegressions();
71EXPORTED_PUBLIC
bool runHostedUsbSyncOwnershipRegression();
72EXPORTED_PUBLIC
bool runHostedUsbInterruptOwnershipRegression();
73EXPORTED_PUBLIC
bool runHostedUsbContainerOwnershipRegression();
74EXPORTED_PUBLIC
bool runHostedUsbEndpointHaltRegressions();
75EXPORTED_PUBLIC
bool runHostedUsbBotRegressions();
76EXPORTED_PUBLIC
bool runHostedHidReportRegressions();
77EXPORTED_PUBLIC
bool runHostedScsiWriteRegressions();
85Thread* g_ImmediateWaiter =
nullptr;
86Thread* g_ImmediateExpectedWaiter =
nullptr;
88struct TerminalCancelBeforeBlockContext {
91 firstChannel(&queue, 0x5445524d),
92 secondChannel(&queue, 0x52455553),
111TerminalCancelBeforeBlockContext* g_TerminalCancelBeforeBlockContext =
nullptr;
113struct NestedTerminalShutdownContext {
114 NestedTerminalShutdownContext()
116 channel(&queue, 0x4e455354),
142NestedTerminalShutdownContext* g_NestedTerminalShutdownContext =
nullptr;
144struct SemaphoreHookContext {
146 : semaphore(semaphore),
147 expectedWaiter(expectedWaiter),
159SemaphoreHookContext* g_SemaphoreContext =
nullptr;
161class StackDestructionCanary {
163 explicit StackDestructionCanary(
Atomic<size_t>* destructions) : m_Destructions(destructions) {}
165 ~StackDestructionCanary() {
166 *m_Destructions += 1;
173struct TerminalCancelContext {
174 TerminalCancelContext()
175 :
waiter(nullptr), entered(0), resumed(0), terminalResult(0), destructed(0) {}
185class PublishedWaitOwner;
187struct PublishedWaitDiscardContext {
188 PublishedWaitDiscardContext()
197 waitersAtDestruction(0),
199 stackDestructed(0) {}
201 PublishedWaitOwner* owner;
214class PublishedWaitOwner {
216 explicit PublishedWaitOwner(PublishedWaitDiscardContext* context) : m_Context(context) {}
218 ~PublishedWaitOwner() {
219 m_Context->waitersAtDestruction += queue.waiterCount();
220 m_Context->ownerDestructed += 1;
226 PublishedWaitDiscardContext* m_Context;
229constexpr uintptr_t PublishedWaitDiscardChannel = 0x44495343;
230PublishedWaitDiscardContext* g_PublishedWaitDiscardContext =
nullptr;
232void destroyPublishedWaitOwner(
void* parameter) {
233 PublishedWaitDiscardContext* context =
reinterpret_cast<PublishedWaitDiscardContext*
>(parameter);
234 context->cleanupCalls += 1;
235 PublishedWaitOwner* owner = context->owner;
236 context->owner =
nullptr;
240struct ConditionVariableHookContext {
242 : condition(condition),
244 expectedWaiter(expectedWaiter),
259ConditionVariableHookContext* g_ConditionVariableContext =
nullptr;
261struct CompletionConditionContext {
262 CompletionConditionContext()
269 terminalMutexHeld(0),
270 signalPreserved(0) {}
284struct ContendedConditionContext {
294 conditionWaitPublished(0),
296 reacquireWaitPublished(0),
310struct TerminalConditionContext {
311 TerminalConditionContext()
316 mutexHeldOnReturn(0),
331struct UnlikelyLockHookContext {
337 UnlikelyLockHookContext(
UnlikelyLock* lock, HeldMode heldMode)
345 signalPreserved(0) {}
357UnlikelyLockHookContext* g_UnlikelyLockContext =
nullptr;
359struct UnlikelyLockFairnessContext {
360 explicit UnlikelyLockFairnessContext(
UnlikelyLock* lock)
361 : lock(lock), admissionSequence(0), writerOrder(0), writerReturned(0), readersEntered(0) {}
370bool waitForDebugState(
Thread* thread,
size_t debugState) {
371 const Time::Timestamp deadline = Time::getTicks() + (2 * Time::Multiplier::Second);
372 while (Time::getTicks() < deadline) {
374 uintptr_t debugAddress = 0;
384void contendedConditionSignalHandler(
size_t) {
385 g_ContendedSignalHandlerCalls += 1;
388int holdConditionMutex(
void* parameter) {
389 ContendedConditionContext* context =
reinterpret_cast<ContendedConditionContext*
>(parameter);
390 if (!context->mutex.acquireForCompletion()) {
393 context->holderAcquired += 1;
395 if (waitForDebugState(context->waiter, Thread::CondWait)) {
396 context->conditionWaitPublished += 1;
399 if (context->trigger == ContendedConditionContext::Signal) {
401 reinterpret_cast<uintptr_t
>(&contendedConditionSignalHandler), 10, ~0UL, 0,
true,
true);
402 if (context->waiter->sendEvent(event)) {
403 context->triggerSent += 1;
409 const Time::Timestamp holdTime = context->trigger == ContendedConditionContext::Signal
410 ? 50 * Time::Multiplier::Millisecond
411 : 200 * Time::Multiplier::Millisecond;
412 Time::delay(holdTime);
413 if (waitForDebugState(context->waiter, Thread::SemWait)) {
414 context->reacquireWaitPublished += 1;
417 context->mutex.release();
418 context->holderReleased += 1;
422int waitForTerminalCondition(
void* parameter) {
423 TerminalConditionContext* context =
reinterpret_cast<TerminalConditionContext*
>(parameter);
424 StackDestructionCanary stackCanary(&context->destructed);
425 if (!context->mutex.acquireForCompletion()) {
428 context->entered += 1;
430 ConditionVariable::Error error = ConditionVariable::NoError;
431 const bool waited = context->condition.wait(context->mutex, error);
432 context->terminalResult =
433 !waited && error == ConditionVariable::TerminationDeferred ?
static_cast<size_t>(1) : 0;
434 context->mutexHeldOnReturn = context->mutex.isOwnedByCurrentThread() ? 1 : 0;
435 context->waitReturned += 1;
437 if (context->mutexHeldOnReturn) {
438 context->mutex.release();
440 return context->terminalResult ? 0 : 1;
443struct ProcessSuspendContext {
450 ProcessSuspendContext(
Process* process, Mode mode)
470ProcessSuspendContext* g_ProcessSuspendContext =
nullptr;
472struct ProcessStopReturnGateContext {
473 ProcessStopReturnGateContext()
474 : release(0), entered(0), returned(0), crossings(0), terminalReturns(0), failures(0) {}
484struct ProcessStopOwnerContext {
485 explicit ProcessStopOwnerContext(
Process* process) : process(process), entered(0), returned(0) {}
494void stoppedUserReturnHandler(
size_t) {
495 g_StoppedUserHandlerCalls += 1;
498class StoppedUserReturnEvent :
public Event {
500 StoppedUserReturnEvent()
501 :
Event(reinterpret_cast<uintptr_t>(&stoppedUserReturnHandler), false, ~0UL,
502 HandlerPrivilege::
User) {}
513void stopCurrentProcessFromEvent(
size_t) {
518class StopCurrentProcessEvent :
public Event {
520 StopCurrentProcessEvent()
521 :
Event(reinterpret_cast<uintptr_t>(&stopCurrentProcessFromEvent), false, ~0UL,
522 HandlerPrivilege::Kernel) {}
533struct PrequeuedStopOwnerContext {
534 explicit PrequeuedStopOwnerContext(
Process* process)
552struct JoinPropagationContext {
553 explicit JoinPropagationContext(
Semaphore* gate)
559 joinerDestructed(0) {}
569struct JoinPublicationContext {
570 JoinPublicationContext()
578 targetReapableInHook(0),
594JoinPublicationContext* g_JoinPublicationContext =
nullptr;
598 if (thread != g_ImmediateExpectedWaiter) {
601 g_ImmediateHookCalls += 1;
602 g_ImmediateWaiter = thread;
603 if (queue != g_ImmediateQueue || !(channel == g_ImmediateChannel) ||
604 debugState != Thread::EventWait) {
605 g_ImmediateHookFailures += 1;
613 if (!waitVisible || wait.queue != queue || wait.channelOwner != channel.owner ||
614 wait.channelValue != channel.value || wait.reason != WaitQueue::WakeReason::Waiting ||
615 wait.stateLevel != thread->
getStateLevel() || !wait.queued) {
616 g_ImmediateHookFailures += 1;
620 if (!queue->wakeOne(WaitQueue::WakeReason::Signalled, channel)) {
621 g_ImmediateHookFailures += 1;
627 PublishedWaitDiscardContext* context = g_PublishedWaitDiscardContext;
628 if (!context || thread != context->waiter) {
632 context->hookCalls += 1;
633 PublishedWaitOwner* owner = context->owner;
635 if (context->hookCalls != 1 || !owner || queue != &owner->queue || channel.owner != owner ||
636 channel.value != PublishedWaitDiscardChannel || debugState != Thread::EventWait ||
638 wait.reason != WaitQueue::WakeReason::Waiting || queue->waiterCount() != 1) {
639 context->hookFailures += 1;
641 context->published += 1;
645 PerProcessorScheduler::StackDiscardReason::HostedRegression);
650 TerminalCancelBeforeBlockContext* context = g_TerminalCancelBeforeBlockContext;
651 if (!context || thread != context->waiter) {
655 context->hookCalls += 1;
657 if (queue != &context->queue || debugState != Thread::EventWait ||
659 wait.reason != WaitQueue::WakeReason::Waiting) {
660 context->hookFailures += 1;
664 if (context->phase == 0) {
665 if (!(channel == context->firstChannel)) {
666 context->hookFailures += 1;
671 context->cancellations += 1;
676 if (context->phase != 1 || !(channel == context->secondChannel) ||
677 !context->queue.wakeOne(WaitQueue::WakeReason::Signalled, channel)) {
678 context->hookFailures += 1;
682 context->wakeups += 1;
686void nestedTerminalShutdownBeforeBlockHook(
WaitQueue* queue,
Thread* thread,
688 NestedTerminalShutdownContext* context = g_NestedTerminalShutdownContext;
689 if (!context || thread != context->waiter) {
693 context->hookCalls += 1;
695 if (context->hookCalls != 1 || queue != &context->queue || !(channel == context->channel) ||
696 debugState != Thread::EventWait || !thread->
getWaitDebugInfo(wait) || wait.queue != queue ||
697 !wait.queued || wait.reason != WaitQueue::WakeReason::Waiting) {
698 context->hookFailures += 1;
702 SchedulerState* outerState = thread->
pushState();
704 context->hookFailures += 1;
707 context->pushed += 1;
713 context->shutdowns += 1;
715 context->popped += 1;
720 SemaphoreHookContext* context = g_SemaphoreContext;
725 if (thread != context->expectedWaiter) {
729 context->hookCalls += 1;
730 context->waiter = thread;
731 if (!queue || channel.owner != context->semaphore || channel.value ||
732 debugState != Thread::SemWait) {
733 context->hookFailures += 1;
736 context->semaphore->release();
739void semaphoreReleaseBeforeIntentHook(
Semaphore* semaphore) {
740 SemaphoreHookContext* context = g_SemaphoreContext;
741 if (!context || semaphore != context->semaphore ||
745 context->hookCalls += 1;
747 context->hookFailures += 1;
754 SemaphoreHookContext* context = g_SemaphoreContext;
755 if (!context || thread != context->expectedWaiter || channel.owner != context->semaphore) {
758 const size_t phase = context->hookCalls;
759 context->hookCalls += 1;
760 context->waiter = thread;
762 if (phase >= 4 || !queue || channel.value || debugState != Thread::SemWait ||
764 wait.reason != WaitQueue::WakeReason::Waiting || queue->waiterCount() != 1 ||
765 context->semaphore->getValue() !=
static_cast<ssize_t
>(phase % 2)) {
766 context->hookFailures += 1;
768 context->semaphore->release(phase >= 4 ? 3 : (phase % 2 ? 2 : 1));
771int waitForTerminalCancellation(
void* parameter) {
772 TerminalCancelContext* context =
reinterpret_cast<TerminalCancelContext*
>(parameter);
773 StackDestructionCanary stackCanary(&context->destructed);
775 context->entered += 1;
776 auto guard = context->queue.acquire();
777 const WaitQueue::WakeReason reason =
779 reinterpret_cast<uintptr_t
>(__builtin_return_address(0)));
781 context->terminalResult = reason == WaitQueue::WakeReason::Terminating ? 1 : 0;
782 context->resumed += 1;
783 return context->terminalResult ? 0 : 1;
786int abandonPublishedWait(
void* parameter) {
787 PublishedWaitDiscardContext* context =
reinterpret_cast<PublishedWaitDiscardContext*
>(parameter);
788 PublishedWaitOwner* owner =
new PublishedWaitOwner(context);
789 context->owner = owner;
792 StackDestructionCanary stackCanary(&context->stackDestructed);
793 context->entered += 1;
795 auto guard = owner->queue.acquire();
797 reinterpret_cast<uintptr_t
>(__builtin_return_address(0)));
800 context->returned += 1;
801 discardScope.disarm();
802 destroyPublishedWaitOwner(context);
808 ConditionVariableHookContext* context = g_ConditionVariableContext;
813 if (thread != context->expectedWaiter) {
817 context->hookCalls += 1;
818 context->waiter = thread;
819 const bool mutexAcquired = context->mutex->tryAcquire();
820 if (!queue || channel.owner || channel.value || debugState != Thread::CondWait ||
822 context->hookFailures += 1;
824 context->acquiredMutex += 1;
829 context->condition->signal();
830 context->mutex->release();
835 UnlikelyLockHookContext* context = g_UnlikelyLockContext;
836 if (!context || thread != context->thread) {
840 context->hookCalls += 1;
841 if (!queue || channel.owner || channel.value || debugState != Thread::CondWait) {
842 context->hookFailures += 1;
846 if (context->heldMode == UnlikelyLockHookContext::WriterHeld) {
847 context->lock->release();
849 context->lock->leave();
853int waitForTerminalCompletionCondition(
void* parameter) {
854 CompletionConditionContext* context =
reinterpret_cast<CompletionConditionContext*
>(parameter);
856 if (!context->gate.acquireForCompletion()) {
860 if (!context->mutex.acquireForCompletion()) {
863 context->entered += 1;
864 Thread* thread = context->waiter;
866 ConditionVariable::Error terminalError = ConditionVariable::NoError;
867 const bool terminalWait = context->condition.wait(context->mutex, terminalError);
868 context->terminalReported =
869 !terminalWait && terminalError == ConditionVariable::TerminationDeferred ? 1 : 0;
870 context->terminalMutexHeld = context->mutex.isOwnedByCurrentThread() ? 1 : 0;
871 if (!context->terminalMutexHeld) {
875 thread->setInterruptionReason(Thread::InterruptedBySignal);
876 while (!context->predicate) {
877 context->condition.waitForCompletion(context->mutex);
879 context->signalPreserved = thread->getInterruptionReason() == Thread::InterruptedBySignal ? 1 : 0;
880 thread->clearInterruption();
882 context->mutex.release();
883 context->returned += 1;
887int enterUnlikelyLock(
void* parameter) {
888 UnlikelyLockHookContext* context =
reinterpret_cast<UnlikelyLockHookContext*
>(parameter);
889 Thread* thread = context->thread;
890 thread->setInterruptionReason(Thread::InterruptedBySignal);
891 context->lock->enter();
892 context->acquired += 1;
893 context->signalPreserved = thread->getInterruptionReason() == Thread::InterruptedBySignal ? 1 : 0;
894 thread->clearInterruption();
895 context->lock->leave();
896 context->returned += 1;
900int acquireTerminalUnlikelyLock(
void* parameter) {
901 UnlikelyLockHookContext* context =
reinterpret_cast<UnlikelyLockHookContext*
>(parameter);
902 Thread* thread = context->thread;
904 context->lock->acquire();
905 context->acquired += 1;
906 context->lock->release();
907 context->returned += 1;
911int acquireFairnessWriter(
void* parameter) {
912 UnlikelyLockFairnessContext* context =
reinterpret_cast<UnlikelyLockFairnessContext*
>(parameter);
913 context->lock->acquire();
914 context->writerOrder = context->admissionSequence += 1;
915 context->lock->release();
916 context->writerReturned += 1;
920int enterFairnessReader(
void* parameter) {
921 UnlikelyLockFairnessContext* context =
reinterpret_cast<UnlikelyLockFairnessContext*
>(parameter);
922 context->lock->enter();
923 context->admissionSequence += 1;
924 context->readersEntered += 1;
925 context->lock->leave();
931 ProcessSuspendContext* context = g_ProcessSuspendContext;
932 if (!context || debugState != Thread::ProcessWait) {
936 context->hookCalls += 1;
937 context->waiter = thread;
938 if (!queue || channel.owner != context->process ||
939 channel.value !=
static_cast<uintptr_t
>(Thread::ProcessWait)) {
940 context->hookFailures += 1;
943 switch (context->mode) {
944 case ProcessSuspendContext::ObserveBlock:
945 if (!context->phase.compareAndSwap(0, 1)) {
946 context->hookFailures += 1;
949 case ProcessSuspendContext::ResumeBeforeBlock:
950 context->process->resume();
951 context->resumes += 1;
953 case ProcessSuspendContext::EventThenResume:
954 if (context->hookCalls == 1) {
955 if (queue->wakeOne(WaitQueue::WakeReason::Event, channel)) {
956 context->eventWakes += 1;
958 context->hookFailures += 1;
960 }
else if (context->hookCalls == 2) {
961 context->process->resume();
962 context->resumes += 1;
964 context->hookFailures += 1;
970int immediateThreadExit(
void*) {
971 g_ImmediateThreadExits += 1;
975int blockedJoinTarget(
void* parameter) {
976 JoinPropagationContext* context =
reinterpret_cast<JoinPropagationContext*
>(parameter);
977 context->targetEntered += 1;
978 return context->gate->acquireForCompletion() ? 0 : 1;
981int joinTarget(
void* parameter) {
982 JoinPropagationContext* context =
reinterpret_cast<JoinPropagationContext*
>(parameter);
983 StackDestructionCanary stackCanary(&context->joinerDestructed);
984 if (context->target->join()) {
985 context->joinSucceeded += 1;
987 context->joinReturned += 1;
991int blockedJoinPublicationTarget(
void* parameter) {
992 JoinPublicationContext* context =
reinterpret_cast<JoinPublicationContext*
>(parameter);
993 context->targetEntered += 1;
994 return context->targetGate.acquireForCompletion() ? 0 : 1;
997int joinPublicationTarget(
void* parameter) {
998 JoinPublicationContext* context =
reinterpret_cast<JoinPublicationContext*
>(parameter);
999 if (context->target->join()) {
1000 context->joinSucceeded += 1;
1002 context->joinReturned += 1;
1007 JoinPublicationContext* context = __atomic_load_n(&g_JoinPublicationContext, __ATOMIC_ACQUIRE);
1008 if (!context || thread != context->joiner || debugState != Thread::Joining) {
1012 context->hookCalls += 1;
1015 wait.reason != WaitQueue::WakeReason::Waiting) {
1016 context->hookFailures += 1;
1021 if (!context->target->detach()) {
1022 context->detachRejected += 1;
1024 context->hookFailures += 1;
1029 context->targetGate.release();
1030 constexpr size_t Attempts = 10000;
1031 for (
size_t attempt = 0; attempt < Attempts; ++attempt) {
1032 if (context->target->isReapableForHostedTest()) {
1033 context->targetReapableInHook += 1;
1038 context->hookFailures += 1;
1041int resumeBlockedProcess(
void* parameter) {
1042 ProcessSuspendContext* context =
reinterpret_cast<ProcessSuspendContext*
>(parameter);
1043 while (context->phase < 1) {
1048 context->waiter->getLock().acquire();
1049 const bool sleeping = context->waiter->getStatus() == Thread::Sleeping;
1050 context->waiter->getLock().release();
1057 context->process->resume();
1058 context->resumes += 1;
1059 if (!context->phase.compareAndSwap(1, 2)) {
1060 context->hookFailures += 1;
1066int waitAtProcessStopReturnGate(
void* parameter) {
1067 ProcessStopReturnGateContext* context =
1068 reinterpret_cast<ProcessStopReturnGateContext*
>(parameter);
1070 context->entered += 1;
1071 if (!context->release.acquire()) {
1072 context->failures += 1;
1073 context->returned += 1;
1078 context->terminalReturns += 1;
1080 context->crossings += 1;
1082 context->returned += 1;
1086int waitAtProcessStopReturnGateWithDeferredEvents(
void* parameter) {
1087 ProcessStopReturnGateContext* context =
1088 reinterpret_cast<ProcessStopReturnGateContext*
>(parameter);
1090 context->entered += 1;
1093 if (!context->release.acquire()) {
1094 context->failures += 1;
1095 context->returned += 1;
1101 context->terminalReturns += 1;
1103 context->crossings += 1;
1105 context->returned += 1;
1109void terminateStoppedReturnGatePeer(
size_t) {
1113int stopOwnerWithPrequeuedTerminalEvent(
void* parameter) {
1114 PrequeuedStopOwnerContext* context =
reinterpret_cast<PrequeuedStopOwnerContext*
>(parameter);
1116 StopCurrentProcessEvent stopEvent;
1117 SignalEvent terminalEvent(
reinterpret_cast<uintptr_t
>(&terminateStoppedReturnGatePeer), 9, ~0UL,
1118 0,
true,
false, Event::HandlerPrivilege::Kernel,
1119 SignalEvent::DeliveryDisposition::DefaultAction);
1121 context->entered += 1;
1122 bool stopQueued =
false;
1123 bool terminalQueued =
false;
1126 stopQueued = current->
sendEvent(&stopEvent);
1127 terminalQueued = stopQueued && current->
sendEvent(&terminalEvent);
1128 if (!stopQueued || !terminalQueued) {
1132 if (terminalQueued) {
1138 if (!stopQueued || !terminalQueued) {
1139 context->failures += 1;
1140 context->returned += 1;
1143 context->eventsQueued += 1;
1147 context->terminalReturns += 1;
1149 context->failures += 1;
1151 context->returned += 1;
1155int activeDirectTransitionWithPrequeuedTerminalEvent(
void* parameter) {
1156 PrequeuedStopOwnerContext* context =
reinterpret_cast<PrequeuedStopOwnerContext*
>(parameter);
1158 SignalEvent terminalEvent(
reinterpret_cast<uintptr_t
>(&terminateStoppedReturnGatePeer), 9, ~0UL,
1159 0,
true,
false, Event::HandlerPrivilege::Kernel,
1160 SignalEvent::DeliveryDisposition::DefaultAction);
1162 context->entered += 1;
1163 bool terminalQueued =
false;
1166 terminalQueued = current->
sendEvent(&terminalEvent);
1168 if (!terminalQueued) {
1169 context->failures += 1;
1170 context->returned += 1;
1173 context->eventsQueued += 1;
1176 PerProcessorScheduler::ProcessStopGateMode::DirectUserTransition);
1178 context->process->getState() == Process::Active) {
1179 context->terminalReturns += 1;
1181 context->failures += 1;
1183 context->returned += 1;
1187int ownProcessStop(
void* parameter) {
1188 ProcessStopOwnerContext* context =
reinterpret_cast<ProcessStopOwnerContext*
>(parameter);
1189 context->entered += 1;
1190 context->process->suspend();
1191 context->returned += 1;
1195struct BlockingContext {
1197 : channel(this, 0x57414954),
1213BlockingContext* g_BlockingContext =
nullptr;
1215struct RequeueWaitContext {
1216 RequeueWaitContext()
1217 : source(&queue, 0x46555458),
1218 destination(&queue, 0x4d555445),
1233int waitForRequeue(
void* parameter) {
1234 RequeueWaitContext* context =
reinterpret_cast<RequeueWaitContext*
>(parameter);
1235 context->entered += 1;
1237 auto guard = context->queue.acquire();
1238 const WaitQueue::WakeReason reason = guard.wait(
1239 context->source, Thread::FutexWait,
reinterpret_cast<uintptr_t
>(__builtin_return_address(0)));
1240 if (reason == WaitQueue::WakeReason::Signalled) {
1241 context->signalled += 1;
1242 }
else if (reason == WaitQueue::WakeReason::Terminating) {
1243 context->terminated += 1;
1245 context->returned += 1;
1250 size_t debugState) {
1251 BlockingContext* context = __atomic_load_n(&g_BlockingContext, __ATOMIC_ACQUIRE);
1256 if (queue != &context->queue || !(channel == context->channel) ||
1257 debugState != Thread::EventWait || thread != context->waiter) {
1261 context->hookCalls += 1;
1262 if (!context->phase.compareAndSwap(0, 1)) {
1263 context->hookFailures += 1;
1267int blockingWaker(
void* parameter) {
1268 BlockingContext* context =
reinterpret_cast<BlockingContext*
>(parameter);
1269 while (context->phase < 1) {
1275 context->waiter->getLock().acquire();
1276 const bool sleeping = context->waiter->getStatus() == Thread::Sleeping;
1277 context->waiter->getLock().release();
1284 if (!context->phase.compareAndSwap(1, 2)) {
1285 context->hookFailures += 1;
1289 if (context->queue.wakeOne(WaitQueue::WakeReason::Signalled, context->channel)) {
1290 context->workerWoke += 1;
1292 context->hookFailures += 1;
1295 if (!context->phase.compareAndSwap(2, 3)) {
1296 context->hookFailures += 1;
1302bool check(
bool condition,
const char* test,
const char* detail) {
1307 ERROR(
"HOSTED-WAIT-TEST: FAIL " << test <<
": " << detail);
1311bool wakeBeforeBlock() {
1315 g_ImmediateQueue = &queue;
1316 g_ImmediateChannel = channel;
1318 WaitQueue::setBeforeBlockHook(immediateWakeHook);
1320 auto guard = queue.acquire();
1321 const WaitQueue::WakeReason reason = guard.
wait(
1322 channel, Thread::EventWait,
reinterpret_cast<uintptr_t
>(__builtin_return_address(0)));
1324 WaitQueue::setBeforeBlockHook(
nullptr);
1325 g_ImmediateQueue =
nullptr;
1326 g_ImmediateExpectedWaiter =
nullptr;
1329 passed &= check(reason == WaitQueue::WakeReason::Signalled,
"wake-before-block",
1330 "wait returned the wrong wake reason");
1331 passed &= check(g_ImmediateHookCalls == 1,
"wake-before-block",
1332 "the pre-block hook did not run exactly once");
1333 passed &= check(g_ImmediateHookFailures == 0,
"wake-before-block",
1334 "the published waiter was not visible to the hook");
1336 check(queue.waiterCount() == 0,
"wake-before-block",
"the completed waiter remained queued");
1337 passed &= check(g_ImmediateWaiter && g_ImmediateWaiter->
getStatus() == Thread::Running,
1338 "wake-before-block",
"the scheduler committed a stale Sleeping transition");
1341 NOTICE(
"HOSTED-WAIT-TEST: PASS wake-before-block");
1346bool semaphoreReleaseBeforeBlock() {
1349 g_SemaphoreContext = &context;
1350 WaitQueue::setBeforeBlockHook(semaphoreReleaseHook);
1352 const bool acquired = semaphore.
acquire();
1354 WaitQueue::setBeforeBlockHook(
nullptr);
1355 g_SemaphoreContext =
nullptr;
1358 passed &= check(acquired,
"semaphore-pre-block",
"acquire did not consume the hook release");
1359 passed &= check(context.hookCalls == 1,
"semaphore-pre-block",
1360 "the pre-block hook did not run exactly once");
1361 passed &= check(context.hookFailures == 0,
"semaphore-pre-block",
1362 "the semaphore waiter was not published in the expected state");
1363 passed &= check(context.waiter && context.waiter->getStatus() == Thread::Running,
1364 "semaphore-pre-block",
"the scheduler committed Sleeping after the release");
1365 passed &= check(semaphore.
getValue() == 0,
"semaphore-pre-block",
1366 "the released semaphore unit was not consumed");
1369 NOTICE(
"HOSTED-WAIT-TEST: PASS semaphore-pre-block");
1374bool semaphoreReleaseBeforeIntent() {
1377 g_SemaphoreContext = &context;
1378 Semaphore::setBeforeWaitHook(semaphoreReleaseBeforeIntentHook);
1381 const bool first = semaphore.
acquire();
1382 const bool second = semaphore.
acquire();
1384 Semaphore::setBeforeWaitHook(
nullptr);
1385 g_SemaphoreContext =
nullptr;
1386 const bool passed = check(first && second && context.hookCalls == 2 &&
1387 context.hookFailures == 0 && semaphore.
getValue() == 0,
1388 "semaphore-pre-intent",
1389 "the final predicate missed a release or retained abandoned intent");
1391 NOTICE(
"HOSTED-WAIT-TEST: PASS semaphore-pre-intent");
1396bool semaphorePartialReleaseBeforeBlock() {
1399 g_SemaphoreContext = &context;
1400 WaitQueue::setBeforeBlockHook(semaphorePartialReleaseHook);
1402 bool acquired =
true;
1403 bool exactCounts =
true;
1404 for (
size_t cycle = 0; cycle < 2 && acquired; ++cycle) {
1405 acquired = semaphore.
acquire(3);
1406 exactCounts &= context.hookCalls == 2 * (cycle + 1) && semaphore.
getValue() == 0;
1409 WaitQueue::setBeforeBlockHook(
nullptr);
1410 g_SemaphoreContext =
nullptr;
1412 check(acquired && exactCounts && context.hookCalls == 4 && context.hookFailures == 0 &&
1413 context.waiter && context.waiter->getStatus() == Thread::Running,
1414 "semaphore-partial-release-rearm",
1415 "partial releases lost tokens, completed early, or failed to rearm the queue");
1417 NOTICE(
"HOSTED-WAIT-TEST: PASS semaphore-partial-release-rearm");
1422bool terminalCancellationReturns() {
1423 TerminalCancelContext context;
1425 &context,
nullptr,
false,
true);
1426 waiter->setName(
"hosted terminal cancellation return");
1429 const bool queued = waitForDebugState(
waiter, Thread::EventWait);
1431 const bool joined =
waiter->joinForCompletion();
1433 const bool passed = check(
1434 context.entered == 1 && queued && joined && context.terminalResult == 1 &&
1435 context.resumed == 1 && context.destructed == 1,
1436 "terminal-cancel-return",
"terminal cancellation did not return through the target's stack");
1438 NOTICE(
"HOSTED-WAIT-TEST: PASS terminal-cancel-return");
1443bool publishedWaitDiscardCleanup() {
1444 PublishedWaitDiscardContext context;
1445 const auto reason = PerProcessorScheduler::StackDiscardReason::HostedRegression;
1449 &context,
nullptr,
false,
true,
true);
1450 waiter->setName(
"hosted published wait stack discard");
1453 g_PublishedWaitDiscardContext = &context;
1454 WaitQueue::setBeforeBlockHook(discardPublishedWaitHook);
1455 const bool started =
waiter->start();
1456 const bool joined = started &&
waiter->joinForCompletion();
1457 WaitQueue::setBeforeBlockHook(
nullptr);
1458 g_PublishedWaitDiscardContext =
nullptr;
1460 PublishedWaitOwner* residue = context.owner;
1461 context.owner =
nullptr;
1467 check(started && joined && context.entered == 1 && context.hookCalls == 1 &&
1468 context.hookFailures == 0 && context.published == 1 && context.cleanupCalls == 1 &&
1469 context.ownerDestructed == 1 && context.waitersAtDestruction == 0 &&
1470 context.returned == 0 && context.stackDestructed == 0 &&
1472 "waitqueue-published-stack-discard",
1473 "stack discard did not unlink the published waiter before owner cleanup");
1475 NOTICE(
"HOSTED-WAIT-TEST: PASS waitqueue-published-stack-discard");
1480bool terminalCancelBeforeBlock() {
1482 TerminalCancelBeforeBlockContext context(
waiter);
1483 g_TerminalCancelBeforeBlockContext = &context;
1484 WaitQueue::setBeforeBlockHook(terminalCancelBeforeBlockHook);
1486 auto firstGuard = context.queue.acquire();
1487 const WaitQueue::WakeReason cancelled =
1488 firstGuard.wait(context.firstChannel, Thread::EventWait,
1489 reinterpret_cast<uintptr_t
>(__builtin_return_address(0)));
1494 auto secondGuard = context.queue.acquire();
1495 const WaitQueue::WakeReason signalled =
1496 secondGuard.wait(context.secondChannel, Thread::EventWait,
1497 reinterpret_cast<uintptr_t
>(__builtin_return_address(0)));
1499 WaitQueue::setBeforeBlockHook(
nullptr);
1500 g_TerminalCancelBeforeBlockContext =
nullptr;
1503 passed &= check(cancelled == WaitQueue::WakeReason::Terminating,
"terminal-cancel-before-block",
1504 "pre-block terminal cancellation did not return its terminal reason");
1505 passed &= check(signalled == WaitQueue::WakeReason::Signalled,
"terminal-cancel-before-block",
1506 "a stale terminal handoff aborted a fresh wait");
1507 passed &= check(context.phase == 2 && context.hookCalls == 2 && context.hookFailures == 0 &&
1508 context.cancellations == 1 && context.wakeups == 1,
1509 "terminal-cancel-before-block",
1510 "the deterministic pre-block interleaving did not complete");
1511 passed &= check(context.queue.waiterCount() == 0 &&
waiter->getStatus() == Thread::Running,
1512 "terminal-cancel-before-block",
1513 "terminal return left a linked waiter or stale Sleeping state");
1516 NOTICE(
"HOSTED-WAIT-TEST: PASS terminal-cancel-before-block");
1521int nestedTerminalShutdownWait(
void* parameter) {
1522 NestedTerminalShutdownContext* context =
1523 reinterpret_cast<NestedTerminalShutdownContext*
>(parameter);
1525 context->entered += 1;
1527 auto guard = context->queue.acquire();
1528 const WaitQueue::WakeReason reason =
1529 guard.wait(context->channel, Thread::EventWait,
1530 reinterpret_cast<uintptr_t
>(__builtin_return_address(0)));
1531 context->terminalResult = reason == WaitQueue::WakeReason::Terminating;
1534 context->waiterUnlinked = !context->waiter->getWaitDebugInfo(wait);
1535 context->finished += 1;
1536 return context->terminalResult ? 0 : 1;
1539bool nestedTerminalShutdownBeforeBlock() {
1540 NestedTerminalShutdownContext context;
1541 g_NestedTerminalShutdownContext = &context;
1542 WaitQueue::setBeforeBlockHook(nestedTerminalShutdownBeforeBlockHook);
1545 &context,
nullptr,
false,
true);
1546 waiter->setName(
"hosted nested terminal pre-block waiter");
1548 const bool joined =
waiter->joinForCompletion();
1549 WaitQueue::setBeforeBlockHook(
nullptr);
1550 g_NestedTerminalShutdownContext =
nullptr;
1553 check(context.entered == 1 && context.hookCalls == 1 && context.hookFailures == 0 &&
1554 context.pushed == 1 && context.shutdowns == 1 && context.popped == 1 &&
1555 context.finished == 1 && context.terminalResult == 1 &&
1556 context.waiterUnlinked == 1 && context.queue.waiterCount() == 0 && joined,
1557 "nested-terminal-cancel-before-block",
1558 "shutdown cancelled a lower-state waiter without returning it from "
1559 "blockCurrent cleanly");
1562 "HOSTED-WAIT-TEST: PASS "
1563 "nested-terminal-cancel-before-block");
1568bool conditionVariableSignalBeforeBlock() {
1571 ConditionVariableHookContext context(&condition, &mutex,
1574 const bool initiallyAcquired = mutex.acquire();
1575 g_ConditionVariableContext = &context;
1576 WaitQueue::setBeforeBlockHook(conditionVariableSignalHook);
1578 ConditionVariable::Error waitError = ConditionVariable::NoError;
1579 const bool waitSucceeded = condition.
wait(mutex, waitError);
1581 WaitQueue::setBeforeBlockHook(
nullptr);
1582 g_ConditionVariableContext =
nullptr;
1586 const bool mutexWasUnlocked = mutex.tryAcquire();
1590 passed &= check(initiallyAcquired,
"condition-variable-pre-block",
1591 "the caller could not acquire its mutex");
1592 passed &= check(waitSucceeded && waitError == ConditionVariable::NoError,
1593 "condition-variable-pre-block",
"wait did not report a successful signal");
1594 passed &= check(context.hookCalls == 1,
"condition-variable-pre-block",
1595 "the pre-block hook did not run exactly once");
1596 passed &= check(context.hookFailures == 0,
"condition-variable-pre-block",
1597 "the signal window did not release the caller mutex");
1598 passed &= check(context.acquiredMutex == 1,
"condition-variable-pre-block",
1599 "the hook could not acquire the released caller mutex");
1600 passed &= check(!mutexWasUnlocked,
"condition-variable-pre-block",
1601 "wait returned without reacquiring the caller mutex");
1603 check(context.waiter && context.waiter->getStatus() == Thread::Running,
1604 "condition-variable-pre-block",
"the scheduler committed Sleeping after the signal");
1607 NOTICE(
"HOSTED-WAIT-TEST: PASS condition-variable-pre-block");
1612bool conditionVariableCompletionBarrier() {
1613 CompletionConditionContext context;
1616 &context,
nullptr,
false,
true,
true);
1617 waiter->setName(
"hosted terminal-pending CV completion waiter");
1619 const bool started =
waiter->start();
1621 const bool gateQueued = [&
waiter]() {
1622 for (
size_t i = 0; i < 10000; ++i) {
1624 uintptr_t debugAddress = 0;
1625 if (
waiter->getWaitDebugInfo(wait) && wait.queued &&
1626 waiter->getDebugState(debugAddress) == Thread::SemWait) {
1635 context.gate.release();
1637 const bool conditionQueued = [&
waiter]() {
1638 for (
size_t i = 0; i < 10000; ++i) {
1640 uintptr_t debugAddress = 0;
1641 if (
waiter->getWaitDebugInfo(wait) && wait.queued &&
1642 waiter->getDebugState(debugAddress) == Thread::CondWait) {
1650 const bool mutexAcquired = context.mutex.acquireForCompletion();
1651 context.predicate = 1;
1652 context.condition.signal();
1653 if (mutexAcquired) {
1654 context.mutex.release();
1656 const bool joined =
waiter->join();
1659 check(started && gateQueued && conditionQueued && mutexAcquired && joined &&
1660 context.entered == 1 && context.returned == 1 && context.terminalReported == 1 &&
1661 context.terminalMutexHeld == 1 && context.signalPreserved == 1,
1662 "condition-variable-completion-barrier",
1663 "terminal state escaped the barrier, mutex ownership was lost, or "
1664 "its signal marker was discarded");
1667 "HOSTED-WAIT-TEST: PASS "
1668 "condition-variable-completion-barrier");
1673bool conditionVariableContendedReacquire(
Thread*
waiter,
1674 ContendedConditionContext::Trigger trigger) {
1675 const char* test = trigger == ContendedConditionContext::Signal
1676 ?
"condition-variable-contended-signal-reacquire"
1677 :
"condition-variable-contended-timeout-reacquire";
1678 ContendedConditionContext context(
waiter, trigger);
1680 const bool initiallyAcquired = context.mutex.acquire();
1681 g_ContendedSignalHandlerCalls = 0;
1683 &context,
nullptr,
false,
true);
1684 holder->setName(
"hosted contended condition mutex holder");
1686 ConditionVariable::Error error = ConditionVariable::NoError;
1687 Time::Timestamp timeout = trigger == ContendedConditionContext::Timeout
1688 ? 100 * Time::Multiplier::Millisecond
1690 const bool waited = context.condition.wait(context.mutex, timeout, error);
1691 const bool mutexHeld = context.mutex.isOwnedByCurrentThread();
1693 context.mutex.release();
1697 const bool expectedError = trigger == ContendedConditionContext::Signal
1698 ? error == ConditionVariable::Interrupted
1699 : error == ConditionVariable::TimedOut;
1701 passed &= check(initiallyAcquired && !waited && expectedError, test,
1702 "the wait did not report its interruption after mutex reacquisition");
1703 passed &= check(mutexHeld, test,
"the interrupted wait returned without owning its mutex");
1704 passed &= check(joined && context.holderAcquired == 1 && context.conditionWaitPublished == 1 &&
1705 context.reacquireWaitPublished == 1 && context.holderReleased == 1,
1706 test,
"the contender did not hold the mutex across interruption");
1707 if (trigger == ContendedConditionContext::Signal) {
1708 passed &= check(context.triggerSent == 1 && g_ContendedSignalHandlerCalls == 1, test,
1709 "the published signal was not delivered exactly once");
1711 passed &= check(context.triggerSent == 0 && timeout == 0, test,
1712 "the timeout did not expire while the mutex was contended");
1716 if (trigger == ContendedConditionContext::Signal) {
1718 "HOSTED-WAIT-TEST: PASS "
1719 "condition-variable-contended-signal-reacquire");
1722 "HOSTED-WAIT-TEST: PASS "
1723 "condition-variable-contended-timeout-reacquire");
1729bool conditionVariableTerminalReacquire() {
1730 TerminalConditionContext context;
1732 &context,
nullptr,
false,
true,
true);
1733 waiter->setName(
"hosted terminal condition mutex waiter");
1735 const bool started =
waiter->start();
1737 const bool conditionQueued = waitForDebugState(
waiter, Thread::CondWait);
1738 const bool mutexAcquired = conditionQueued && context.mutex.acquireForCompletion();
1739 context.condition.signal();
1740 const bool reacquireQueued = waitForDebugState(
waiter, Thread::SemWait);
1742 if (mutexAcquired) {
1743 context.mutex.release();
1746 const bool joined =
waiter->joinForCompletion();
1747 const bool mutexRecoverable = context.mutex.tryAcquire();
1748 if (mutexRecoverable) {
1749 context.mutex.release();
1752 const bool passed = check(
1753 started && context.entered == 1 && conditionQueued && mutexAcquired && reacquireQueued &&
1754 joined && context.waitReturned == 1 && context.terminalResult == 1 &&
1755 context.mutexHeldOnReturn == 1 && context.destructed == 1 && mutexRecoverable,
1756 "condition-variable-terminal-reacquire",
1757 "terminal propagation did not return with mutex ownership and unwind the stack");
1760 "HOSTED-WAIT-TEST: PASS "
1761 "condition-variable-terminal-reacquire");
1766bool unlikelyLockWriterPreference() {
1768 UnlikelyLockFairnessContext context(&lock);
1772 &context,
nullptr,
false,
true);
1773 writer->setName(
"hosted queued UnlikelyLock writer");
1774 const bool writerQueued = waitForDebugState(writer, Thread::CondWait);
1777 &context,
nullptr,
false,
true);
1778 readerA->setName(
"hosted later UnlikelyLock reader A");
1780 &context,
nullptr,
false,
true);
1781 readerB->setName(
"hosted later UnlikelyLock reader B");
1782 const bool readerAQueued = waitForDebugState(readerA, Thread::CondWait);
1783 const bool readerBQueued = waitForDebugState(readerB, Thread::CondWait);
1790 return check(writerQueued && readerAQueued && readerBQueued && writerJoined && readerAJoined &&
1791 readerBJoined && context.writerOrder == 1 && context.writerReturned == 1 &&
1792 context.readersEntered == 2 && context.admissionSequence == 3,
1793 "unlikely-lock-admission",
"later readers bypassed a queued writer");
1796bool unlikelyLockAdmission() {
1801 UnlikelyLockHookContext
reader(&lock, UnlikelyLockHookContext::WriterHeld);
1802 g_UnlikelyLockContext = &
reader;
1803 WaitQueue::setBeforeBlockHook(unlikelyLockAdmissionHook);
1805 &
reader,
nullptr,
false,
true,
true);
1806 readerThread->setName(
"hosted UnlikelyLock reader");
1807 reader.thread = readerThread;
1808 const bool readerStarted = readerThread->
start();
1809 for (
size_t i = 0; i < 10000 &&
reader.hookCalls == 0; ++i) {
1813 WaitQueue::setBeforeBlockHook(
nullptr);
1814 g_UnlikelyLockContext =
nullptr;
1816 const bool readerJoined = readerThread->
join();
1817 WaitQueue::setBeforeBlockHook(
nullptr);
1818 g_UnlikelyLockContext =
nullptr;
1821 check(readerStarted && readerJoined &&
reader.hookCalls == 1 &&
reader.hookFailures == 0 &&
1823 "unlikely-lock-admission",
"reader admission lost a publication wake or signal marker");
1826 UnlikelyLockHookContext writer(&lock, UnlikelyLockHookContext::ReaderHeld);
1827 g_UnlikelyLockContext = &writer;
1828 WaitQueue::setBeforeBlockHook(unlikelyLockAdmissionHook);
1831 nullptr,
false,
true,
true);
1832 writerThread->setName(
"hosted terminal-pending UnlikelyLock writer");
1833 writer.thread = writerThread;
1834 const bool writerStarted = writerThread->
start();
1835 for (
size_t i = 0; i < 10000 && writer.hookCalls == 0; ++i) {
1838 if (writer.hookCalls) {
1839 WaitQueue::setBeforeBlockHook(
nullptr);
1840 g_UnlikelyLockContext =
nullptr;
1842 const bool writerJoined = writerThread->
join();
1843 WaitQueue::setBeforeBlockHook(
nullptr);
1844 g_UnlikelyLockContext =
nullptr;
1847 check(writerStarted && writerJoined && writer.hookCalls == 1 && writer.hookFailures == 0 &&
1848 writer.acquired == 1 && writer.returned == 1,
1849 "unlikely-lock-admission",
"writer admission abandoned ownership for terminal state");
1851 passed &= unlikelyLockWriterPreference();
1854 NOTICE(
"HOSTED-WAIT-TEST: PASS unlikely-lock-admission");
1859bool ordinaryBlockAndWake() {
1863 nullptr,
false,
true);
1864 waker->setName(
"hosted wait regression waker");
1866 __atomic_store_n(&g_BlockingContext, &context, __ATOMIC_RELEASE);
1867 WaitQueue::setBeforeBlockHook(blockingHook);
1868 auto guard = context.queue.acquire();
1869 const WaitQueue::WakeReason reason = guard.wait(
1870 context.channel, Thread::EventWait,
reinterpret_cast<uintptr_t
>(__builtin_return_address(0)));
1871 WaitQueue::setBeforeBlockHook(
nullptr);
1872 __atomic_store_n(&g_BlockingContext,
nullptr, __ATOMIC_RELEASE);
1874 const bool joined = waker->
join();
1877 passed &= check(reason == WaitQueue::WakeReason::Signalled,
"ordinary-block-wake",
1878 "wait returned the wrong wake reason");
1879 passed &= check(context.hookCalls == 1,
"ordinary-block-wake",
1880 "the publication hook did not run exactly once");
1881 passed &= check(context.hookFailures == 0,
"ordinary-block-wake",
1882 "the waker did not observe the expected wait state");
1883 passed &= check(context.workerWoke == 1,
"ordinary-block-wake",
1884 "the worker did not wake one published waiter");
1885 passed &= check(context.phase == 3,
"ordinary-block-wake",
1886 "the worker did not complete the blocking sequence");
1887 passed &= check(joined,
"ordinary-block-wake",
"the waker could not be joined");
1888 passed &= check(context.queue.waiterCount() == 0,
"ordinary-block-wake",
1889 "the completed waiter remained queued");
1892 NOTICE(
"HOSTED-WAIT-TEST: PASS ordinary-block-wake");
1897bool waitQueueWakeAndRequeue() {
1898 constexpr const char* Test =
"waitqueue-wake-and-requeue";
1899 RequeueWaitContext context;
1902 bool enrolled =
true;
1903 for (
size_t i = 0; i < 3; ++i) {
1905 nullptr,
false,
true);
1906 waiters[i]->setName(
"hosted WaitQueue requeue waiter");
1909 enrolled &= waitForDebugState(
waiter, Thread::FutexWait);
1913 bool sourceReachedMovedWaiter =
true;
1914 size_t destinationWoken = 0;
1916 auto guard = context.queue.acquire();
1917 moved = guard.wakeAndRequeue(context.source, 1, context.destination, 2);
1918 sourceReachedMovedWaiter = guard.wakeOne(WaitQueue::WakeReason::Signalled, context.source);
1919 destinationWoken = guard.wakeAll(WaitQueue::WakeReason::Signalled, context.destination);
1922 if (!enrolled || moved != 3 || sourceReachedMovedWaiter || destinationWoken != 2) {
1930 joined &=
waiter->joinForCompletion();
1934 passed &= check(enrolled, Test,
"the source waiters did not all publish");
1935 passed &= check(moved == 3, Test,
"wake-and-requeue did not select all three waiters");
1936 passed &= check(!sourceReachedMovedWaiter, Test,
1937 "the source channel reached a completed or requeued waiter");
1938 passed &= check(destinationWoken == 2, Test,
1939 "the destination did not wake exactly the two requeued waiters");
1940 passed &= check(joined && context.entered == 3 && context.returned == 3 &&
1941 context.signalled == 3 && context.terminated == 0,
1942 Test,
"the requeued waiters did not all return as signalled");
1944 check(context.queue.waiterCount() == 0, Test,
"completed requeued waiters remained linked");
1946 RequeueWaitContext cancellation;
1948 &cancellation,
nullptr,
false,
true);
1949 cancelled->setName(
"hosted WaitQueue cancelled requeue waiter");
1950 const bool cancellationEnrolled = waitForDebugState(cancelled, Thread::FutexWait);
1951 size_t cancellationMoved = 0;
1952 if (cancellationEnrolled) {
1953 auto guard = cancellation.queue.acquire();
1954 cancellationMoved = guard.wakeAndRequeue(cancellation.source, 0, cancellation.destination, 1);
1959 wait.queue == &cancellation.queue && wait.queued &&
1960 wait.reason == WaitQueue::WakeReason::Waiting &&
1961 wait.channelOwner == cancellation.destination.owner &&
1962 wait.channelValue == cancellation.destination.value;
1966 passed &= check(cancellationEnrolled && cancellationMoved == 1 && destinationPublished, Test,
1967 "the cancellation waiter was not requeued to the destination");
1968 passed &= check(cancellationJoined && cancellation.returned == 1 && cancellation.signalled == 0 &&
1969 cancellation.terminated == 1 && cancellation.queue.waiterCount() == 0,
1970 Test,
"cancelling a requeued waiter did not unlink it safely");
1973 NOTICE(
"HOSTED-WAIT-TEST: PASS waitqueue-wake-and-requeue");
1978bool processSuspendResume() {
1986 auto consumeContinued = [process]() {
1990 transition.kind == Process::ChildTransitionKind::Continued &&
1991 !transition.stopSignal;
1993 return stopWasNotSelected && continuedSelected && consumedExactlyOnce;
1996 ProcessSuspendContext blocking(process, ProcessSuspendContext::ObserveBlock);
1997 Thread* resumer =
new Thread(process, resumeBlockedProcess, &blocking,
nullptr,
false,
true);
1998 resumer->setName(
"hosted process resume regression");
2000 g_ProcessSuspendContext = &blocking;
2001 WaitQueue::setBeforeBlockHook(processSuspendHook);
2003 WaitQueue::setBeforeBlockHook(
nullptr);
2004 g_ProcessSuspendContext =
nullptr;
2007 check(resumer->
join(),
"process-suspend-resume",
"the ordinary resumer could not be joined");
2009 check(blocking.hookCalls == 1 && blocking.hookFailures == 0 && blocking.phase == 2 &&
2010 blocking.resumes == 1,
2011 "process-suspend-resume",
"the ordinary suspend did not block and resume exactly once");
2012 const bool blockingTransition = consumeContinued();
2014 check(process->getState() == Process::Active && blockingTransition,
"process-suspend-resume",
2015 "ordinary resume did not replace and exclusively report the pending stop");
2017 ProcessSuspendContext immediate(process, ProcessSuspendContext::ResumeBeforeBlock);
2018 g_ProcessSuspendContext = &immediate;
2019 WaitQueue::setBeforeBlockHook(processSuspendHook);
2021 WaitQueue::setBeforeBlockHook(
nullptr);
2022 g_ProcessSuspendContext =
nullptr;
2024 passed &= check(immediate.hookCalls == 1 && immediate.hookFailures == 0 && immediate.resumes == 1,
2025 "process-suspend-resume",
"resume in the publication window was not retained");
2026 const bool immediateTransition = consumeContinued();
2028 check(process->getState() == Process::Active && immediateTransition,
"process-suspend-resume",
2029 "pre-block resume did not replace and exclusively report the pending stop");
2031 ProcessSuspendContext eventThenResume(process, ProcessSuspendContext::EventThenResume);
2032 g_ProcessSuspendContext = &eventThenResume;
2033 WaitQueue::setBeforeBlockHook(processSuspendHook);
2035 WaitQueue::setBeforeBlockHook(
nullptr);
2036 g_ProcessSuspendContext =
nullptr;
2039 check(eventThenResume.hookCalls == 2 && eventThenResume.hookFailures == 0 &&
2040 eventThenResume.eventWakes == 1 && eventThenResume.resumes == 1,
2041 "process-suspend-resume",
"an event wake bypassed the Suspended predicate recheck");
2042 const bool eventTransition = consumeContinued();
2044 check(process->getState() == Process::Active && eventTransition,
"process-suspend-resume",
2045 "event/recheck resume did not replace and exclusively report the pending stop");
2048 NOTICE(
"HOSTED-WAIT-TEST: PASS process-suspend-resume");
2053bool processStopGatesPeerReturns() {
2054 constexpr const char* Test =
"process-stop-gates-peer-returns";
2058 ProcessStopReturnGateContext gateContext;
2060 new Thread(process, waitAtProcessStopReturnGate, &gateContext,
nullptr,
false,
true,
true);
2061 first->setName(
"hosted stopped return-gate peer one");
2063 new Thread(process, waitAtProcessStopReturnGate, &gateContext,
nullptr,
false,
true,
true);
2064 second->setName(
"hosted stopped return-gate peer two");
2066 ProcessStopOwnerContext ownerContext(process);
2067 Thread* owner =
new Thread(process, ownProcessStop, &ownerContext,
nullptr,
false,
true,
true);
2068 owner->setName(
"hosted process-stop owner");
2070 const bool firstStarted = first->
start();
2071 const bool secondStarted = second->
start();
2072 const bool peersWaiting = firstStarted && secondStarted &&
2073 waitForDebugState(first, Thread::SemWait) &&
2074 waitForDebugState(second, Thread::SemWait) && gateContext.entered == 2;
2075 const bool ownerStarted = peersWaiting && owner->start();
2076 const Time::Timestamp stopDeadline = Time::getTicks() + (2 * Time::Multiplier::Second);
2077 while (ownerStarted && !process->isSuspended() && !ownerContext.returned &&
2078 Time::getTicks() < stopDeadline) {
2081 const bool suspended = process->isSuspended();
2082 const bool ownerWaiting = suspended && waitForDebugState(owner, Thread::ProcessWait);
2084 gateContext.release.release(2);
2085 const bool firstGated = suspended && waitForDebugState(first, Thread::ProcessWait);
2086 const bool secondGated = suspended && waitForDebugState(second, Thread::ProcessWait);
2087 for (
size_t attempt = 0; attempt < 32; ++attempt) {
2090 const bool bothHeld = firstGated && secondGated && !gateContext.returned &&
2091 !gateContext.crossings && !gateContext.terminalReturns;
2093 bool resumed =
false;
2094 if (process->isSuspended()) {
2096 resumed = process->getState() == Process::Active;
2097 }
else if (ownerStarted && !ownerContext.returned) {
2101 const bool ownerJoined = ownerStarted && owner->joinForCompletion();
2103 if (process->isSuspended()) {
2108 if (!firstStarted) {
2111 if (!secondStarted) {
2114 if (!ownerStarted) {
2118 const bool passed = check(
2119 firstStarted && secondStarted && peersWaiting && ownerStarted && suspended && ownerWaiting &&
2120 bothHeld && resumed && firstJoined && secondJoined && ownerJoined &&
2121 gateContext.returned == 2 && gateContext.crossings == 2 && !gateContext.terminalReturns &&
2122 !gateContext.failures && ownerContext.entered == 1 && ownerContext.returned == 1,
2124 "a running process peer crossed its user-return gate while stopped or failed to resume");
2128 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
2133bool stoppedProcessDefersUserReturnEvent() {
2134 constexpr const char* Test =
"stopped-process-defers-user-return-event";
2138 ProcessStopReturnGateContext gateContext;
2140 new Thread(process, waitAtProcessStopReturnGate, &gateContext,
nullptr,
false,
true,
true);
2141 peer->setName(
"hosted stopped user-event return-gate peer");
2142 ProcessStopOwnerContext ownerContext(process);
2143 Thread* owner =
new Thread(process, ownProcessStop, &ownerContext,
nullptr,
false,
true,
true);
2144 owner->setName(
"hosted user-event process-stop owner");
2146 g_StoppedUserHandlerCalls = 0;
2147 const bool peerStarted = peer->
start();
2148 const bool peerWaiting =
2149 peerStarted && waitForDebugState(peer, Thread::SemWait) && gateContext.entered == 1;
2150 const bool ownerStarted = peerWaiting && owner->start();
2151 const Time::Timestamp stopDeadline = Time::getTicks() + (2 * Time::Multiplier::Second);
2152 while (ownerStarted && !process->isSuspended() && !ownerContext.returned &&
2153 Time::getTicks() < stopDeadline) {
2156 const bool suspended = process->isSuspended();
2157 const bool ownerWaiting = suspended && waitForDebugState(owner, Thread::ProcessWait);
2159 StoppedUserReturnEvent userEvent;
2160 const bool eventQueued = ownerWaiting && peer->
sendEvent(&userEvent);
2161 const bool remainedOnOriginalWait =
2162 eventQueued && waitForDebugState(peer, Thread::SemWait) && peer->
hasEvent(&userEvent);
2163 gateContext.release.release();
2164 const bool peerGated = remainedOnOriginalWait && waitForDebugState(peer, Thread::ProcessWait);
2165 for (
size_t attempt = 0; attempt < 32; ++attempt) {
2168 const bool heldWhileStopped = peerGated && process->isSuspended() && !gateContext.returned &&
2169 peer->
hasEvent(&userEvent) && !g_StoppedUserHandlerCalls;
2171 bool resumed =
false;
2172 if (process->isSuspended()) {
2174 resumed = process->getState() == Process::Active;
2175 }
else if (ownerStarted && !ownerContext.returned) {
2179 const bool ownerJoined = ownerStarted && owner->joinForCompletion();
2183 if (!ownerStarted) {
2187 const bool passed = check(
2188 peerStarted && peerWaiting && ownerStarted && suspended && ownerWaiting && eventQueued &&
2189 remainedOnOriginalWait && heldWhileStopped && resumed && peerJoined && ownerJoined &&
2190 gateContext.returned == 1 && gateContext.crossings == 1 && !gateContext.terminalReturns &&
2191 !gateContext.failures && !g_StoppedUserHandlerCalls && ownerContext.entered == 1 &&
2192 ownerContext.returned == 1,
2193 Test,
"a stopped process entered or consumed a userspace event before resume");
2197 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
2202bool terminalUnwindEscapesProcessStopGate() {
2203 constexpr const char* Test =
"terminal-unwind-escapes-process-stop-gate";
2207 ProcessStopReturnGateContext gateContext;
2209 new Thread(process, waitAtProcessStopReturnGate, &gateContext,
nullptr,
false,
true,
true);
2210 peer->setName(
"hosted terminal stopped return-gate peer");
2211 ProcessStopOwnerContext ownerContext(process);
2212 Thread* owner =
new Thread(process, ownProcessStop, &ownerContext,
nullptr,
false,
true,
true);
2213 owner->setName(
"hosted terminal process-stop owner");
2215 const bool peerStarted = peer->
start();
2216 const bool peerWaiting =
2217 peerStarted && waitForDebugState(peer, Thread::SemWait) && gateContext.entered == 1;
2218 const bool ownerStarted = peerWaiting && owner->start();
2219 const Time::Timestamp stopDeadline = Time::getTicks() + (2 * Time::Multiplier::Second);
2220 while (ownerStarted && !process->isSuspended() && !ownerContext.returned &&
2221 Time::getTicks() < stopDeadline) {
2224 const bool suspended = process->isSuspended();
2225 const bool ownerWaiting = suspended && waitForDebugState(owner, Thread::ProcessWait);
2227 gateContext.release.release();
2228 const bool peerGated = suspended && waitForDebugState(peer, Thread::ProcessWait);
2232 const Time::Timestamp terminalDeadline = Time::getTicks() + (2 * Time::Multiplier::Second);
2233 while (peerGated && !gateContext.returned && Time::getTicks() < terminalDeadline) {
2236 const bool returnedWhileStopped =
2237 peerGated && gateContext.returned == 1 && process->isSuspended();
2238 const bool terminalWon = returnedWhileStopped && gateContext.terminalReturns == 1 &&
2239 !gateContext.crossings && !gateContext.failures;
2241 if (!gateContext.returned && peerStarted) {
2244 if (process->isSuspended()) {
2246 }
else if (ownerStarted && !ownerContext.returned) {
2251 const bool ownerJoined = ownerStarted && owner->joinForCompletion();
2255 if (!ownerStarted) {
2258 if (process->isSuspended()) {
2263 check(peerStarted && peerWaiting && ownerStarted && suspended && ownerWaiting && peerGated &&
2264 returnedWhileStopped && terminalWon && peerJoined && ownerJoined &&
2265 gateContext.entered == 1 && gateContext.returned == 1 &&
2266 ownerContext.entered == 1 && ownerContext.returned == 1,
2267 Test,
"terminal unwind did not escape a stopped process gate before resume");
2271 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
2276bool prequeuedTerminalEventEscapesProcessStopGate() {
2277 constexpr const char* Test =
"prequeued-terminal-event-before-resume";
2281 ProcessStopReturnGateContext gateContext;
2282 Thread* peer =
new Thread(process, waitAtProcessStopReturnGateWithDeferredEvents, &gateContext,
2283 nullptr,
false,
true,
true);
2284 peer->setName(
"hosted prequeued-terminal stopped return-gate peer");
2285 ProcessStopOwnerContext ownerContext(process);
2286 Thread* owner =
new Thread(process, ownProcessStop, &ownerContext,
nullptr,
false,
true,
true);
2287 owner->setName(
"hosted prequeued-terminal process-stop owner");
2289 const bool peerStarted = peer->
start();
2290 const bool peerWaiting =
2291 peerStarted && waitForDebugState(peer, Thread::SemWait) && gateContext.entered == 1;
2292 const bool ownerStarted = peerWaiting && owner->start();
2293 const Time::Timestamp stopDeadline = Time::getTicks() + (2 * Time::Multiplier::Second);
2294 while (ownerStarted && !process->isSuspended() && !ownerContext.returned &&
2295 Time::getTicks() < stopDeadline) {
2298 const bool suspended = process->isSuspended();
2299 const bool ownerWaiting = suspended && waitForDebugState(owner, Thread::ProcessWait);
2301 SignalEvent terminalEvent(
reinterpret_cast<uintptr_t
>(&terminateStoppedReturnGatePeer), 9, ~0UL,
2302 0,
true,
false, Event::HandlerPrivilege::Kernel,
2303 SignalEvent::DeliveryDisposition::DefaultAction);
2304 const bool terminalQueued = ownerWaiting && peer->
sendEvent(&terminalEvent);
2305 const bool remainedPrequeued =
2306 terminalQueued && waitForDebugState(peer, Thread::SemWait) && !gateContext.returned;
2308 gateContext.release.release();
2309 const Time::Timestamp terminalDeadline = Time::getTicks() + (2 * Time::Multiplier::Second);
2310 while (remainedPrequeued && !gateContext.returned && Time::getTicks() < terminalDeadline) {
2313 const bool returnedWhileStopped = remainedPrequeued && gateContext.returned == 1 &&
2314 process->isSuspended() && !ownerContext.returned;
2315 const bool terminalWon = returnedWhileStopped && gateContext.terminalReturns == 1 &&
2316 !gateContext.crossings && !gateContext.failures;
2318 if (!gateContext.returned && peerStarted) {
2322 if (process->isSuspended()) {
2324 }
else if (ownerStarted && !ownerContext.returned) {
2327 const bool ownerJoined = ownerStarted && owner->joinForCompletion();
2331 if (!ownerStarted) {
2336 check(peerStarted && peerWaiting && ownerStarted && suspended && ownerWaiting &&
2337 terminalQueued && remainedPrequeued && terminalWon && peerJoined && ownerJoined &&
2338 gateContext.entered == 1 && gateContext.returned == 1 &&
2339 ownerContext.entered == 1 && ownerContext.returned == 1,
2340 Test,
"a terminal event queued before gate enrollment was lost behind process stop");
2344 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
2349bool prequeuedTerminalEventEscapesStopOwnerGate() {
2350 constexpr const char* Test =
"prequeued-terminal-event-before-stop-owner-wait";
2354 PrequeuedStopOwnerContext context(process);
2355 Thread* owner =
new Thread(process, stopOwnerWithPrequeuedTerminalEvent, &context,
nullptr,
false,
2357 owner->setName(
"hosted prequeued-terminal process-stop owner");
2359 const bool started = owner->start();
2360 const Time::Timestamp terminalDeadline = Time::getTicks() + (2 * Time::Multiplier::Second);
2361 while (started && !context.returned && Time::getTicks() < terminalDeadline) {
2364 const bool returnedWhileStopped = started && context.returned == 1 && process->isSuspended();
2365 const bool terminalWon = returnedWhileStopped && context.entered == 1 &&
2366 context.eventsQueued == 1 && context.terminalReturns == 1 &&
2369 if (!context.returned && started) {
2372 const bool joined = started && owner->joinForCompletion();
2373 if (process->isSuspended()) {
2381 check(started && terminalWon && joined, Test,
2382 "a terminal event queued before the stop owner enrolled was lost behind process stop");
2386 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
2391bool activeDirectTransitionDrainsKernelEvent() {
2392 constexpr const char* Test =
"active-direct-transition-drains-kernel-event";
2396 PrequeuedStopOwnerContext context(process);
2397 Thread* thread =
new Thread(process, activeDirectTransitionWithPrequeuedTerminalEvent, &context,
2398 nullptr,
false,
true,
true);
2399 thread->setName(
"hosted active direct-transition terminal gate");
2401 const bool started = thread->
start();
2402 const Time::Timestamp terminalDeadline = Time::getTicks() + (2 * Time::Multiplier::Second);
2403 while (started && !context.returned && Time::getTicks() < terminalDeadline) {
2406 const bool terminalWon = started && context.returned == 1 && context.entered == 1 &&
2407 context.eventsQueued == 1 && context.terminalReturns == 1 &&
2408 !context.failures && process->getState() == Process::Active;
2410 if (!context.returned && started) {
2419 check(terminalWon && joined, Test,
2420 "an active direct userspace transition bypassed a queued kernel terminal event");
2424 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
2429bool prequeuedEventDispatch() {
2430 Thread* thread = g_ImmediateWaiter;
2431 if (!check(thread !=
nullptr,
"prequeued-event",
2432 "the suite did not capture its current thread")) {
2436 const bool passed = check(thread->runHostedPrequeuedEventRegression(),
"prequeued-event",
2437 "a ready event was not dispatched exactly once without blocking");
2440 NOTICE(
"HOSTED-WAIT-TEST: PASS prequeued-event");
2445bool stateLevelPublication() {
2446 Thread* thread = g_ImmediateWaiter;
2447 if (!check(thread !=
nullptr,
"state-level-publication",
2448 "the suite did not capture its current thread")) {
2453 check(thread->runHostedStatePublicationRegression(),
"state-level-publication",
2454 "an event sender observed a partially published state level");
2457 NOTICE(
"HOSTED-WAIT-TEST: PASS state-level-publication");
2462bool stateCleanupOrder() {
2463 Thread* thread = g_ImmediateWaiter;
2464 if (!check(thread !=
nullptr,
"state-cleanup-order",
2465 "the suite did not capture its current thread")) {
2469 const bool passed = check(thread->runHostedStateCleanupRegression(),
"state-cleanup-order",
2470 "cleanup was not LIFO, checkpointed, or level-scoped");
2472 NOTICE(
"HOSTED-WAIT-TEST: PASS state-cleanup-order");
2477bool execStackOwnership() {
2478 Thread* thread = g_ImmediateWaiter;
2479 if (!check(thread !=
nullptr,
"exec-stack-ownership",
2480 "the suite did not capture its current thread")) {
2485 check(thread->runHostedExecStackOwnershipRegression(),
"exec-stack-ownership",
2486 "exec did not discard stale nested stack metadata or adopt the replacement at base");
2488 NOTICE(
"HOSTED-WAIT-TEST: PASS exec-stack-ownership");
2493bool activeEventDeliveryLease() {
2494 Thread* thread = g_ImmediateWaiter;
2495 if (!check(thread !=
nullptr,
"event-delivery-lease",
2496 "the suite did not capture its current thread")) {
2500 const bool passed = check(thread->runHostedEventDeliveryLeaseRegression(),
"event-delivery-lease",
2501 "a dequeued Event was not retained through scheduler dispatch");
2504 NOTICE(
"HOSTED-WAIT-TEST: PASS event-delivery-lease");
2509bool eventQueueShutdown() {
2510 Thread* thread = g_ImmediateWaiter;
2511 if (!check(thread !=
nullptr,
"event-shutdown-drain",
2512 "the suite did not capture its current thread")) {
2516 const bool passed = check(thread->runHostedEventShutdownRegression(),
"event-shutdown-drain",
2517 "thread shutdown did not reject or drain queued event deliveries");
2520 NOTICE(
"HOSTED-WAIT-TEST: PASS event-shutdown-drain");
2525bool immediateExitJoinLifecycle() {
2526 constexpr size_t JoinIterations = 4;
2529 for (
size_t iteration = 0; iteration < JoinIterations; ++iteration) {
2531 nullptr,
nullptr,
false,
true);
2532 thread->setName(
"hosted immediate-exit join regression");
2534 while (g_ImmediateThreadExits < (iteration + 1)) {
2540 passed &= check(thread->
join(),
"thread-join-lifecycle",
2541 "an immediate-exit thread could not be joined");
2544 passed &= check(g_ImmediateThreadExits == JoinIterations,
"thread-join-lifecycle",
2545 "the repeated immediate-exit entries did not all run");
2548 NOTICE(
"HOSTED-WAIT-TEST: PASS thread-join-lifecycle");
2553bool joinPublicationAndDetachExclusion() {
2555 JoinPublicationContext context;
2559 new Thread(process, blockedJoinPublicationTarget, &context,
nullptr,
false,
true);
2560 context.target->setName(
"hosted join publication target");
2562 constexpr size_t Attempts = 10000;
2563 bool targetBlocked =
false;
2564 for (
size_t attempt = 0; attempt < Attempts; ++attempt) {
2565 if (context.targetEntered == 1 && waitForDebugState(context.target, Thread::SemWait)) {
2566 targetBlocked =
true;
2571 passed &= check(targetBlocked,
"thread-join-pre-block-wake",
2572 "the target did not publish its completion gate");
2573 if (!targetBlocked) {
2574 context.targetGate.release();
2575 context.target->join();
2579 context.joiner =
new Thread(process, joinPublicationTarget, &context,
nullptr,
false,
true,
true);
2580 context.joiner->setName(
"hosted exact-window joiner");
2582 __atomic_store_n(&g_JoinPublicationContext, &context, __ATOMIC_RELEASE);
2583 WaitQueue::setBeforeBlockHook(joinPublicationHook);
2584 const bool joinerStarted = context.joiner->start();
2586 bool joinerCompleted =
false;
2587 for (
size_t attempt = 0; attempt < Attempts; ++attempt) {
2588 if (context.joinReturned == 1) {
2589 joinerCompleted =
true;
2594 WaitQueue::setBeforeBlockHook(
nullptr);
2595 __atomic_store_n(&g_JoinPublicationContext,
static_cast<JoinPublicationContext*
>(
nullptr),
2598 const bool joinerJoined = context.joiner->join();
2599 passed &= check(joinerStarted && joinerCompleted && joinerJoined && context.hookCalls == 1 &&
2600 context.hookFailures == 0 && context.targetReapableInHook == 1 &&
2601 context.joinSucceeded == 1,
2602 "thread-join-pre-block-wake",
2603 "a target wake in join's published-before-sleep window was lost");
2604 passed &= check(context.detachRejected == 1,
"thread-join-detach-exclusion",
2605 "detach stole a target after an exclusive join claim was published");
2608 NOTICE(
"HOSTED-WAIT-TEST: PASS thread-join-pre-block-wake");
2609 NOTICE(
"HOSTED-WAIT-TEST: PASS thread-join-detach-exclusion");
2614bool terminalJoinPropagation() {
2617 JoinPropagationContext context(&targetGate);
2620 context.target =
new Thread(process, blockedJoinTarget, &context,
nullptr,
false,
true);
2621 context.target->setName(
"hosted join-propagation target");
2623 while (context.targetEntered != 1) {
2627 Thread* interruptedJoiner =
new Thread(process, joinTarget, &context,
nullptr,
false,
true);
2628 interruptedJoiner->setName(
"hosted terminal join-propagation waiter");
2630 bool enrolled =
false;
2631 constexpr size_t EnrolmentAttempts = 10000;
2632 for (
size_t attempt = 0; attempt < EnrolmentAttempts; ++attempt) {
2634 uintptr_t debugAddress = 0;
2635 if (interruptedJoiner->
getWaitDebugInfo(info) && info.queue && info.queued &&
2636 interruptedJoiner->
getDebugState(debugAddress) == Thread::Joining &&
2637 interruptedJoiner->
getStatus() == Thread::Sleeping) {
2643 passed &= check(enrolled,
"join-terminal-propagation",
2644 "the first joiner did not publish its join wait");
2647 passed &= check(interruptedJoiner->
joinForCompletion(),
"join-terminal-propagation",
2648 "the terminated joiner did not become reapable");
2650 context.joinReturned == 1 && context.joinSucceeded == 0 && context.joinerDestructed == 1,
2651 "join-terminal-propagation",
"the interrupted join did not return and unwind its stack");
2653 Thread* replacementJoiner =
new Thread(process, joinTarget, &context,
nullptr,
false,
true);
2654 replacementJoiner->setName(
"hosted replacement join waiter");
2656 bool replacementEnrolled =
false;
2657 for (
size_t attempt = 0; attempt < EnrolmentAttempts; ++attempt) {
2659 if (replacementJoiner->
getWaitDebugInfo(info) && info.queue && info.queued &&
2660 replacementJoiner->
getStatus() == Thread::Sleeping) {
2661 replacementEnrolled =
true;
2666 passed &= check(replacementEnrolled,
"join-terminal-propagation",
2667 "the interrupted exclusive join claim blocked a replacement joiner");
2669 targetGate.release();
2670 passed &= check(replacementJoiner->
join(),
"join-terminal-propagation",
2671 "the replacement joiner did not become reapable");
2673 context.joinReturned == 2 && context.joinSucceeded == 1 && context.joinerDestructed == 2,
2674 "join-terminal-propagation",
"the replacement join did not consume the released claim");
2677 NOTICE(
"HOSTED-WAIT-TEST: PASS join-terminal-propagation");
2684bool runHostedWaitRegressions() {
2685 NOTICE(
"HOSTED-WAIT-TEST: BEGIN");
2688 runHostedSpinlockRegressions() && runHostedElfValidationRegressions() &&
2689#if !PEDIGREE_HOSTED_CORE_SMOKE
2690 runHostedPs2ControllerRegressions() &&
2692 wakeBeforeBlock() && semaphoreReleaseBeforeBlock() && semaphoreReleaseBeforeIntent() &&
2693 semaphorePartialReleaseBeforeBlock() && terminalCancellationReturns() &&
2694 publishedWaitDiscardCleanup() && terminalCancelBeforeBlock() &&
2695 nestedTerminalShutdownBeforeBlock() && conditionVariableSignalBeforeBlock() &&
2696 runHostedRingBufferRegressions() && runHostedBufferRegressions() &&
2697 runHostedAtaPioRegressions() &&
2699 .getBootstrapProcessorScheduler()
2700 ->runHostedNewThreadWorkerRegressions() &&
2701#if !PEDIGREE_HOSTED_CORE_SMOKE
2702 runHostedSchedulerRegressions() &&
2704 runHostedRequestQueueRegressions() &&
2705#if PEDIGREE_HOSTED_CORE_SMOKE
2706 runHostedStoragePageRegressions() && runHostedCacheDiscardRegressions() &&
2707 runHostedCacheTimerRegressions() &&
2709#if !PEDIGREE_HOSTED_CORE_SMOKE
2710 runHostedUsbCallbackDeliveryRegressions() && runHostedUsbHcdPortChangeRegressions() &&
2711 runHostedUsbTransferLifecycleRegressions() && runHostedMutexRegressions() &&
2712 runHostedInterruptManagerRegressions() && runHostedCdiIrqRegressions() &&
2713 runHostedInputRegressions() && runNetworkFilterConcurrencyRegressions() &&
2714 runVfsCallbackLifetimeRegressions() && runHostedCacheRegressions() &&
2715 runHostedFatSectorRegressions() && runHostedRawFsContractRegressions() &&
2716 runHostedUnixDatagramRegressions() &&
2718 runHostedLifetimeLeaseRegressions() &&
2719#if !PEDIGREE_HOSTED_CORE_SMOKE
2720 runHostedLogRegressions() &&
2722 runHostedProcessExitRegressions() && runHostedPs2MousePacketRegressions() &&
2723#if !PEDIGREE_HOSTED_CORE_SMOKE
2724 runHostedPs2MouseRegressions() &&
2726 conditionVariableContendedReacquire(g_ImmediateWaiter, ContendedConditionContext::Signal) &&
2727 conditionVariableContendedReacquire(g_ImmediateWaiter, ContendedConditionContext::Timeout) &&
2728 conditionVariableTerminalReacquire() && conditionVariableCompletionBarrier() &&
2729 unlikelyLockAdmission() && runHostedPageFaultRegressions() &&
2730 runHostedPayloadPageRegressions() &&
2731#if !PEDIGREE_HOSTED_CORE_SMOKE
2732 runHostedPipeRegressions() && runHostedUsbSyncOwnershipRegression() &&
2733 runHostedUsbInterruptOwnershipRegression() && runHostedUsbContainerOwnershipRegression() &&
2734 runHostedUsbEndpointHaltRegressions() && runHostedUsbBotRegressions() &&
2735 runHostedHidReportRegressions() && runHostedScsiWriteRegressions() &&
2736 UsbPnP::runHostedRegistrationRegression() && Ipc::runHostedIpcInterruptionRegression() &&
2738 runHostedTimerRegressions(g_ImmediateWaiter) &&
2739 runHostedPrimitiveRegressions(g_ImmediateWaiter) &&
2740 runHostedSignalInterruptionRegressions(g_ImmediateWaiter) &&
2741#if !PEDIGREE_HOSTED_CORE_SMOKE
2742 runHostedSyscallRegressions() &&
2744 ordinaryBlockAndWake() && waitQueueWakeAndRequeue() && processSuspendResume() &&
2745 processStopGatesPeerReturns() && stoppedProcessDefersUserReturnEvent() &&
2746 terminalUnwindEscapesProcessStopGate() && prequeuedTerminalEventEscapesProcessStopGate() &&
2747 prequeuedTerminalEventEscapesStopOwnerGate() && activeDirectTransitionDrainsKernelEvent() &&
2748 immediateExitJoinLifecycle() && joinPublicationAndDetachExclusion() &&
2749 terminalJoinPropagation() && prequeuedEventDispatch() && stateLevelPublication() &&
2750 stateCleanupOrder() && execStackOwnership() && activeEventDeliveryLease() &&
2751 eventQueueShutdown();
2753 NOTICE(
"HOSTED-WAIT-TEST: PASS all");
2755 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)