8#include "pedigree/kernel/Atomic.h"
9#include "pedigree/kernel/Log.h"
10#include "pedigree/kernel/process/ConditionVariable.h"
11#include "pedigree/kernel/process/Mutex.h"
12#include "pedigree/kernel/process/PerProcessorScheduler.h"
13#include "pedigree/kernel/process/Scheduler.h"
14#include "pedigree/kernel/process/Semaphore.h"
15#include "pedigree/kernel/process/SignalEvent.h"
16#include "pedigree/kernel/process/Thread.h"
17#include "pedigree/kernel/process/Uninterruptible.h"
18#include "pedigree/kernel/processor/Processor.h"
19#include "pedigree/kernel/processor/ProcessorInformation.h"
20#include "pedigree/kernel/processor/VirtualAddressSpace.h"
21#include "pedigree/kernel/processor/state.h"
22#include "pedigree/kernel/time/Time.h"
23#include "pedigree/kernel/utilities/Buffer.h"
24#include "pedigree/kernel/utilities/RingBuffer.h"
26#if !defined(PEDIGREE_HOSTED_CORE_SMOKE)
29#include "modules/subsys/posix/PosixProcess.h"
30#include "modules/subsys/posix/PosixSubsystem.h"
31#include "modules/subsys/posix/signal-syscalls.h"
35constexpr size_t HostedSignalNumber = 10;
48void hostedSignalHandler(
size_t) {
49 g_SignalHandlerCalls += 1;
52void hostedNestedSignalHandler(
size_t) {
54 g_NestedSignalHandlerCalls += 1;
55 g_NestedSignalHandlerLevel = thread ? thread->
getStateLevel() : 0;
58void hostedNestedWaitHandler(
size_t) {
65 g_NestedWaitHandlerCalls += 1;
66 g_NestedWaitHandlerLevel = stateLevel;
69 g_NestedWaitReturned += 1;
73void hostedDefaultActionHandler(
size_t) {
74 g_DefaultActionHandlerCalls += 1;
77class HostedNestedWaitEvent :
public Event {
79 HostedNestedWaitEvent() :
Event(reinterpret_cast<uintptr_t>(&hostedNestedWaitHandler), false) {}
90class HostedMonitorEvent :
public Event {
92 HostedMonitorEvent() :
Event(reinterpret_cast<uintptr_t>(&hostedSignalHandler), false) {}
94 ~HostedMonitorEvent()
override {
95 g_MonitorEventDestructions += 1;
107class SignalNumberCollisionEvent :
public Event {
109 SignalNumberCollisionEvent()
110 :
Event(reinterpret_cast<uintptr_t>(&hostedSignalHandler), false, MAX_NESTED_EVENTS) {}
117 return HostedSignalNumber;
121class HostedDeferredUserReturnSignalEvent :
public SignalEvent {
125 HostedDeferredUserReturnSignalEvent()
126 :
SignalEvent(reinterpret_cast<uintptr_t>(&hostedSignalHandler), HostedSignalNumber, ~0UL, 0,
127 true, false,
Event::HandlerPrivilege::
User) {}
134 g_ExactUserReturnCalls += 1;
136 g_ExactUserReturnSawInterrupts += 1;
138 return UserReturnDelivery::Delivered;
142bool check(
bool condition,
const char* detail) {
147 ERROR(
"HOSTED-WAIT-TEST: FAIL signal-interruption: " << detail);
151bool waitUntilQueued(
Thread* thread,
size_t debugState) {
152 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
153 while (Time::getTicks() < deadline) {
155 uintptr_t debugAddress = 0;
165bool eventHandlerPrivilege() {
166 constexpr const char* Test =
"event-handler-privilege";
167 SignalEvent kernelEvent(
reinterpret_cast<uintptr_t
>(&hostedSignalHandler), HostedSignalNumber);
168 SignalEvent userEvent(
reinterpret_cast<uintptr_t
>(&hostedSignalHandler), HostedSignalNumber, ~0UL,
169 0,
true,
false, Event::HandlerPrivilege::User);
171 SignalEvent alternateEvent(
reinterpret_cast<uintptr_t
>(&hostedSignalHandler), HostedSignalNumber,
172 ~0UL, 0,
true,
false, Event::HandlerPrivilege::User,
173 SignalEvent::DeliveryDisposition::CaughtHandler,
true);
177 check(kernelEvent.getHandlerPrivilege() == Event::HandlerPrivilege::Kernel,
178 "the compatible Event constructor did not default to kernel privilege") &&
180 "a kernel event rejected a kernel mapping") &&
182 "a kernel event accepted a userspace mapping") &&
183 check(userEvent.getHandlerPrivilege() == Event::HandlerPrivilege::User,
184 "a user event lost its explicit privilege") &&
186 "a user event rejected an executable userspace mapping") &&
187 check(!userEvent.isValidHandlerMapping(0),
188 "a user event accepted a non-executable mapping") &&
191 "a user event accepted a kernel mapping") &&
193 "a signal delivery snapshot lost its user privilege") &&
194 check(alternateEvent.prefersAlternateUserStack() && alternateDelivery &&
196 "a signal delivery snapshot lost its alternate-stack preference");
199 delete alternateDelivery;
201 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
206bool pendingSignalRunsAtSyscallReturn(
Thread* thread) {
207 constexpr const char* Test =
"pending-signal-at-syscall-return";
208 constexpr uint64_t SignalBit =
static_cast<uint64_t
>(1) << (HostedSignalNumber - 1);
211 g_SignalHandlerCalls = 0;
214 SignalEvent event(
reinterpret_cast<uintptr_t
>(&hostedSignalHandler), HostedSignalNumber);
215 const bool queued = thread->
sendEvent(&event);
217 SyscallState state = {};
219 const bool stayedPending = thread->
hasEvent(&event) && g_SignalHandlerCalls == 0;
223 const bool delivered = !thread->
hasEvent(&event) && g_SignalHandlerCalls == 1;
227 check(queued && !terminalWhileBlocked && stayedPending && !terminalAfterUnblock &&
229 "a newly unblocked signal did not run at the syscall return boundary");
231 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
236bool exactUserReturnSignalDefersWithoutContext(
Thread* thread) {
237 constexpr const char* Test =
"exact-user-return-signal-deferral";
239 thread->clearInterruption();
240 g_SignalHandlerCalls = 0;
241 g_ExactUserReturnCalls = 0;
242 g_ExactUserReturnSawInterrupts = 0;
244 HostedDeferredUserReturnSignalEvent event;
245 const bool queued = thread->
sendEvent(&event);
246 const bool delayed = queued ? Time::delay(5 * Time::Multiplier::Second) : true;
247 const Thread::InterruptionReason reason = thread->getInterruptionReason();
248 const bool stayedPending = thread->
hasEvent(&event);
249 thread->clearInterruption();
251 SyscallState state = {};
254 const bool deliveredAtExactBoundary = !thread->
hasEvent(&event) && g_ExactUserReturnCalls == 1 &&
255 g_ExactUserReturnSawInterrupts == 1 &&
257 if (!deliveredAtExactBoundary) {
262 check(queued && !delayed && reason == Thread::InterruptedBySignal && stayedPending &&
263 !terminal && deliveredAtExactBoundary && !g_SignalHandlerCalls &&
265 "an exact-context signal ran from a wait boundary or was not delivered IRQ-enabled");
267 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
272bool signalCullPreservesNumberCollision(
Thread* thread) {
273 constexpr const char* Test =
"signal-cull-number-collision";
274 constexpr uint64_t SignalBit =
static_cast<uint64_t
>(1) << (HostedSignalNumber - 1);
278 SignalNumberCollisionEvent collision;
279 SignalEvent signal(
reinterpret_cast<uintptr_t
>(&hostedSignalHandler), HostedSignalNumber);
280 const bool collisionQueued = thread->
sendEvent(&collision);
281 const bool signalQueued = collisionQueued && thread->
sendEvent(&signal);
283 const bool preservedCollision = thread->
hasEvent(&collision) && !thread->
hasEvent(&signal);
288 check(collisionQueued && signalQueued && preservedCollision,
289 "signal culling removed a non-signal event with the same numeric identifier");
291 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
296#if !defined(PEDIGREE_HOSTED_CORE_SMOKE)
300struct DefaultStopContext {
301 DefaultStopContext() : entered(0), returned(0) {}
307struct StopEpochHookContext {
310 ContinueThenFreshStop,
315 : subsystem(subsystem),
316 staleTarget(staleTarget),
317 freshTarget(freshTarget),
346StopEpochHookContext* g_StopEpochHookContext =
nullptr;
348void continueAfterStopDequeue(Thread::StateTransitionWindow window,
Thread* thread,
size_t,
350 StopEpochHookContext* context = __atomic_load_n(&g_StopEpochHookContext, __ATOMIC_ACQUIRE);
351 if (!context || window != Thread::StatePushBeforePublish || thread != context->staleTarget) {
355 Thread::setStateTransitionHook(
nullptr);
356 context->hookCalls += 1;
358 if (!context->cancelled) {
361 const size_t continuationCount =
362 context->mode == StopEpochHookContext::ContinueThenFreshStop ? 2 : 1;
363 for (
size_t i = 0; i < continuationCount && !context->cancelled; ++i) {
364 if (context->subsystem->queueSignalDelivery(thread, SIGCONT) ==
365 PosixSubsystem::SignalDeliveryResult::Ignored) {
366 context->continuations += 1;
368 context->failures += 1;
374 if (context->freshTarget && !context->cancelled) {
375 if (context->subsystem->queueSignalDelivery(context->freshTarget, SIGSTOP) ==
376 PosixSubsystem::SignalDeliveryResult::Queued) {
377 context->freshStopQueued += 1;
378 if (!context->cancelled && context->freshTarget->start()) {
379 context->freshStarted += 1;
380 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
381 while (!context->cancelled && !process->isSuspended() && Time::getTicks() < deadline) {
384 if (process->isSuspended()) {
385 context->freshSuspended += 1;
386 }
else if (!context->cancelled) {
387 context->failures += 1;
389 }
else if (!context->cancelled) {
390 context->failures += 1;
393 context->failures += 1;
396 context->hookCompleted += 1;
399void hostedContinueHandler(
size_t) {
401 if (current && current->
getParent()->getState() == Process::Active) {
402 g_ContinueHandlerObservedActive += 1;
404 g_ContinueHandlerCalls += 1;
407void installSignalDisposition(
PosixSubsystem& subsystem,
size_t signal,
int type,
408 void (*handlerAddress)(
size_t) = &hostedSignalHandler) {
410 handler->
type = type;
411 handler->
pEvent =
new SignalEvent(
reinterpret_cast<uintptr_t
>(handlerAddress), signal);
415int dormantSignalThread(
void*) {
419int waitForDefaultStop(
void* parameter) {
420 DefaultStopContext* context =
reinterpret_cast<DefaultStopContext*
>(parameter);
421 context->entered += 1;
423 context->returned += 1;
427struct ExecSignalResetContext {
428 ExecSignalResetContext() : entered(0), returned(0) {}
434int deliverExecResetSignal(
void* parameter) {
435 ExecSignalResetContext* context =
reinterpret_cast<ExecSignalResetContext*
>(parameter);
437 context->entered += 1;
440 context->returned += 1;
444struct IgnoredContinueContext {
445 IgnoredContinueContext(
Process* process,
bool blockSignal)
446 : process(process), blockSignal(blockSignal), entered(0), returned(0) {}
454struct IgnoredSignalWaitContext {
455 explicit IgnoredSignalWaitContext(
size_t blockedSignal = 0)
457 blockedSignal(blockedSignal),
462 interruption(
Thread::NotInterrupted) {}
465 size_t blockedSignal;
473int waitThroughIgnoredSignal(
void* parameter) {
474 IgnoredSignalWaitContext* context =
reinterpret_cast<IgnoredSignalWaitContext*
>(parameter);
477 if (context->blockedSignal) {
479 (
static_cast<uint64_t
>(1) << (context->blockedSignal - 1)));
481 context->entered += 1;
482 Semaphore::SemaphoreError error = Semaphore::NoError;
483 context->acquired = context->gate.acquireWithError(1, 0, 0, error) ? 1 : 0;
484 context->error =
static_cast<size_t>(error);
485 if (context->blockedSignal) {
489 context->interruption =
static_cast<size_t>(current->getInterruptionReason());
490 current->clearInterruption();
491 context->returned += 1;
497 installSignalDisposition(*subsystem, signal, type);
499 const bool queryPreserved =
501 IgnoredSignalWaitContext context;
503 new Thread(process, waitThroughIgnoredSignal, &context,
nullptr,
false,
true,
true);
504 target->setName(
"hosted ignored signal waiter");
505 const bool started = target->
start();
506 const bool queued = started && waitUntilQueued(target, Thread::SemWait);
509 subsystem->
sendSignal(target,
static_cast<int>(signal),
false);
511 for (
size_t attempt = 0; attempt < 32 && !context.returned; ++attempt) {
515 const bool stayedQueued = queued && !context.returned && target->
getWaitDebugInfo(wait) &&
516 wait.queued && !target->hasEvents();
518 context.gate.release();
523 return queryPreserved && started && queued && stayedQueued && joined && context.entered == 1 &&
524 context.returned == 1 && context.acquired == 1 && context.error == Semaphore::NoError &&
525 context.interruption == Thread::NotInterrupted;
528bool ignoredSignalDoesNotInterruptWait(
Process* kernelProcess) {
529 constexpr const char* Test =
"ignored-signal-does-not-interrupt";
532 process->setSubsystem(subsystem);
535 g_SignalHandlerCalls = 0;
536 const bool explicitIgnore = ignoredSignalDoesNotWakeWait(process, subsystem, SIGUSR1, 2);
537 const bool defaultIgnore = ignoredSignalDoesNotWakeWait(process, subsystem, SIGCHLD, 1);
538 const bool passed = check(explicitIgnore && defaultIgnore && g_SignalHandlerCalls == 0,
539 "an explicit or default ignored signal woke an interruptible wait");
543 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
550 installSignalDisposition(*subsystem, signal, 0);
551 IgnoredSignalWaitContext context(signal);
553 new Thread(process, waitThroughIgnoredSignal, &context,
nullptr,
false,
true,
true);
554 target->setName(
"hosted pending signal discard target");
555 const bool started = target->
start();
556 const bool waiting = started && waitUntilQueued(target, Thread::SemWait);
558 const PosixSubsystem::SignalDeliveryResult queued =
560 : PosixSubsystem::SignalDeliveryResult::Rejected;
561 const bool observedPending =
562 queued == PosixSubsystem::SignalDeliveryResult::Queued && target->
hasEvent(signal);
563 installSignalDisposition(*subsystem, signal, ignoredType);
564 const bool discarded = observedPending && !target->
hasEvent(signal);
566 context.gate.release();
571 return waiting && observedPending && discarded && joined && context.entered == 1 &&
572 context.returned == 1 && context.acquired == 1 && context.error == Semaphore::NoError &&
573 context.interruption == Thread::NotInterrupted;
576bool ignoredDispositionDiscardsPendingSignals(
Process* kernelProcess) {
577 constexpr const char* Test =
"ignored-disposition-discards-pending";
580 process->setSubsystem(subsystem);
583 g_SignalHandlerCalls = 0;
584 const bool explicitIgnore = pendingSignalDiscarded(process, subsystem, SIGUSR1, 2);
585 const bool defaultIgnore = pendingSignalDiscarded(process, subsystem, SIGCHLD, 1);
587 check(explicitIgnore && defaultIgnore && g_SignalHandlerCalls == 0,
588 "a pending caught signal survived transition to an ignored disposition");
592 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
597bool execSignalResetRebindsPending(
Process* kernelProcess) {
598 constexpr const char* Test =
"exec-signal-reset-rebinds-pending";
601 process->setSubsystem(subsystem);
605 caught->
sigMask =
static_cast<uint64_t
>(1) << (SIGUSR1 - 1);
606 caught->
flags = SA_RESTART;
609 caught->
pEvent =
new SignalEvent(
reinterpret_cast<uintptr_t
>(&hostedSignalHandler), SIGCHLD);
613 ignored->
sigMask =
static_cast<uint64_t
>(1) << (SIGCHLD - 1);
614 ignored->
flags = SA_RESTART;
617 ignored->
pEvent =
new SignalEvent(
reinterpret_cast<uintptr_t
>(&hostedSignalHandler), SIGUSR2);
620 ExecSignalResetContext context;
622 new Thread(process, deliverExecResetSignal, &context,
nullptr,
false,
true,
true);
623 target->setName(
"hosted exec signal reset target");
624 target->
setSignalMask(
static_cast<uint64_t
>(1) << (SIGCHLD - 1));
626 alternate.base = 0x100000;
627 alternate.size = 0x4000;
628 alternate.enabled =
true;
629 alternate.inUse =
true;
631 g_SignalHandlerCalls = 0;
632 const PosixSubsystem::SignalDeliveryResult queued =
634 const bool initiallyPending =
635 queued == PosixSubsystem::SignalDeliveryResult::Queued && target->
hasSignalEvent(SIGCHLD);
637 pedigree_reset_signals_for_exec(target);
642 resetCaught.type == 1 && !resetCaught.signalMask && !resetCaught.flags &&
643 !resetCaught.restorer &&
644 resetCaught.handler !=
reinterpret_cast<uintptr_t
>(&hostedSignalHandler);
645 const bool ignoreRetained =
647 !resetIgnored.signalMask && !resetIgnored.flags && !resetIgnored.restorer &&
648 resetIgnored.handler !=
reinterpret_cast<uintptr_t
>(&hostedSignalHandler);
650 const bool alternateReset =
651 !alternate.base && !alternate.size && !alternate.enabled && !alternate.inUse;
653 const bool started = target->
start();
660 const bool passed = check(initiallyPending && caughtReset && ignoreRetained && pendingRebound &&
661 alternateReset && started && joined && context.entered == 1 &&
662 context.returned == 1 && g_SignalHandlerCalls == 0,
663 "exec discarded a pending signal, retained old handler metadata, or "
664 "left the alternate stack enabled");
668 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
683bool defaultStopControl(
Process* kernelProcess) {
684 constexpr const char* Test =
"default-stop-control";
687 process->setSubsystem(subsystem);
690 DefaultStopContext context;
691 Thread* target =
new Thread(process, waitForDefaultStop, &context,
nullptr,
false,
true,
true);
692 target->setName(
"hosted default stop control");
693 pedigree_reset_signals_for_exec(target);
696 PosixSubsystem::SignalDeliveryResult::Queued;
697 const bool started = queued && target->
start();
698 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
699 while (started && !process->isSuspended() && !context.returned && Time::getTicks() < deadline) {
703 const bool suspended = process->isSuspended();
705 const bool stoppedReported = suspended && takeJobControlTransition(process, stoppedTransition) &&
706 stoppedTransition.kind == Process::ChildTransitionKind::Stopped &&
707 stoppedTransition.stopSignal == SIGTSTP;
709 PosixSubsystem::SignalDeliveryResult continueResult =
710 PosixSubsystem::SignalDeliveryResult::Unavailable;
713 if (process->isSuspended()) {
716 }
else if (started && !context.returned) {
727 const bool continuedReported =
728 takeJobControlTransition(process, continuedTransition) &&
729 continuedTransition.kind == Process::ChildTransitionKind::Continued &&
730 !continuedTransition.stopSignal;
732 check(queued && started && suspended && stoppedReported &&
733 continueResult == PosixSubsystem::SignalDeliveryResult::Queued && joined &&
734 context.entered == 1 && context.returned == 1 && continuedReported,
735 "a fresh default stop did not suspend, preserve its signal number, or continue");
739 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
744bool staleDefaultStopRejectedAfterContinue(
Process* kernelProcess) {
745 constexpr const char* Test =
"stale-default-stop-after-continue";
748 process->setSubsystem(subsystem);
751 DefaultStopContext context;
752 Thread* target =
new Thread(process, waitForDefaultStop, &context,
nullptr,
false,
true,
true);
753 target->setName(
"hosted stale default stop target");
754 pedigree_reset_signals_for_exec(target);
755 installSignalDisposition(*subsystem, SIGCONT, 2);
758 PosixSubsystem::SignalDeliveryResult::Queued;
759 StopEpochHookContext hookContext(subsystem, target,
nullptr, StopEpochHookContext::ContinueOnce);
760 __atomic_store_n(&g_StopEpochHookContext, &hookContext, __ATOMIC_RELEASE);
761 Thread::setStateTransitionHook(continueAfterStopDequeue);
762 const bool started = queued && target->
start();
764 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
765 while (started && !context.returned && !process->isSuspended() && Time::getTicks() < deadline) {
768 Thread::setStateTransitionHook(
nullptr);
769 __atomic_store_n(&g_StopEpochHookContext,
static_cast<StopEpochHookContext*
>(
nullptr),
773 const bool publishedStop = takeJobControlTransition(process, unexpectedTransition);
774 const bool returnedWithoutStop = context.returned == 1 && process->getState() == Process::Active;
775 if (process->isSuspended()) {
777 }
else if (started && !context.returned) {
788 check(queued && started && hookContext.hookCalls == 1 && !hookContext.failures &&
789 hookContext.hookCompleted == 1 && hookContext.continuations == 1 &&
790 hookContext.epochAfter == (hookContext.epochBefore + 1) && returnedWithoutStop &&
791 !publishedStop && joined && context.entered == 1,
792 "a dequeued default stop survived a later SIGCONT generation");
796 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
801bool staleDefaultStopRejectedAcrossAba(
Process* kernelProcess) {
802 constexpr const char* Test =
"stale-default-stop-aba";
805 process->setSubsystem(subsystem);
808 DefaultStopContext staleContext;
810 new Thread(process, waitForDefaultStop, &staleContext,
nullptr,
false,
true,
true);
811 staleTarget->setName(
"hosted ABA stale stop target");
812 pedigree_reset_signals_for_exec(staleTarget);
813 installSignalDisposition(*subsystem, SIGCONT, 2);
815 DefaultStopContext freshContext;
817 new Thread(process, waitForDefaultStop, &freshContext,
nullptr,
false,
true,
true);
818 freshTarget->setName(
"hosted ABA fresh stop target");
821 PosixSubsystem::SignalDeliveryResult::Queued;
822 StopEpochHookContext hookContext(subsystem, staleTarget, freshTarget,
823 StopEpochHookContext::ContinueThenFreshStop);
824 __atomic_store_n(&g_StopEpochHookContext, &hookContext, __ATOMIC_RELEASE);
825 Thread::setStateTransitionHook(continueAfterStopDequeue);
826 const bool staleStarted = staleQueued && staleTarget->
start();
828 const Time::Timestamp hookDeadline = Time::getTicks() + (1 * Time::Multiplier::Second);
829 while (staleStarted && !hookContext.hookCompleted && Time::getTicks() < hookDeadline) {
832 const bool hookTimedOut = staleStarted && !hookContext.hookCompleted;
834 hookContext.cancelled += 1;
836 Thread::setStateTransitionHook(
nullptr);
837 __atomic_store_n(&g_StopEpochHookContext,
static_cast<StopEpochHookContext*
>(
nullptr),
841 if (!staleContext.returned) {
844 if (process->isSuspended()) {
850 const bool freshStarted = hookContext.freshStarted == 1;
851 if (freshStarted && !freshContext.returned) {
854 if (process->isSuspended()) {
862 if (process->isSuspended()) {
866 const bool cleaned = staleJoined && (!freshStarted || freshJoined);
867 check(
false, cleaned ?
"the ABA dequeue hook timed out"
868 :
"the ABA dequeue hook timed out and thread cleanup failed");
873 const bool freshStarted = hookContext.freshStarted == 1;
874 const bool freshSuspended = hookContext.freshSuspended == 1 && process->isSuspended();
875 const bool staleJoinedStop = freshSuspended && waitUntilQueued(staleTarget, Thread::ProcessWait);
876 const bool freshJoinedStop = freshSuspended && waitUntilQueued(freshTarget, Thread::ProcessWait);
877 const bool bothStoppedBeforeContinue = !staleContext.returned && !freshContext.returned;
879 const bool freshStopReported = freshSuspended &&
880 takeJobControlTransition(process, freshTransition) &&
881 freshTransition.kind == Process::ChildTransitionKind::Stopped &&
882 freshTransition.stopSignal == SIGSTOP;
883 PosixSubsystem::SignalDeliveryResult continueResult =
884 PosixSubsystem::SignalDeliveryResult::Unavailable;
885 if (freshSuspended) {
887 if (process->isSuspended()) {
891 if (staleStarted && !staleContext.returned) {
894 if (freshStarted && !freshContext.returned) {
910 const bool continuedReported =
911 takeJobControlTransition(process, continuedTransition) &&
912 continuedTransition.kind == Process::ChildTransitionKind::Continued;
913 const bool finalEpochAdvanced = process->
getContinuationEpoch() == (hookContext.epochBefore + 3);
915 check(staleQueued && staleStarted && hookContext.hookCalls == 1 && !hookContext.failures &&
916 hookContext.hookCompleted == 1 && hookContext.continuations == 2 &&
917 hookContext.freshStopQueued == 1 &&
918 hookContext.epochAfter == (hookContext.epochBefore + 2) && finalEpochAdvanced &&
919 staleJoined && freshStarted && freshSuspended && staleJoinedStop &&
920 freshJoinedStop && bothStoppedBeforeContinue && freshStopReported &&
921 continueResult == PosixSubsystem::SignalDeliveryResult::Ignored && freshJoined &&
922 staleContext.entered == 1 && staleContext.returned == 1 &&
923 freshContext.entered == 1 && freshContext.returned == 1 && continuedReported,
924 "a stale stop escaped or invalidated a newer process stop generation");
928 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
933bool execSignalResetRestampsPendingStop(
Process* kernelProcess) {
934 constexpr const char* Test =
"exec-signal-reset-restamps-pending-stop";
937 process->setSubsystem(subsystem);
940 DefaultStopContext context;
941 Thread* target =
new Thread(process, waitForDefaultStop, &context,
nullptr,
false,
true,
true);
942 target->setName(
"hosted exec rebound default stop target");
943 pedigree_reset_signals_for_exec(target);
944 installSignalDisposition(*subsystem, SIGCONT, 2);
948 PosixSubsystem::SignalDeliveryResult::Ignored;
949 const bool queued = continuedWhileActive && subsystem->
queueSignalDelivery(target, SIGSTOP) ==
950 PosixSubsystem::SignalDeliveryResult::Queued;
951 const bool pendingBeforeReset = queued && target->
hasSignalEvent(SIGSTOP);
952 if (pendingBeforeReset) {
953 pedigree_reset_signals_for_exec(target);
955 const bool pendingAfterReset = pendingBeforeReset && target->
hasSignalEvent(SIGSTOP);
956 const bool started = pendingAfterReset && target->
start();
958 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
959 while (started && !process->isSuspended() && !context.returned && Time::getTicks() < deadline) {
963 const bool suspended = process->isSuspended();
965 const bool stoppedReported = suspended && takeJobControlTransition(process, stoppedTransition) &&
966 stoppedTransition.kind == Process::ChildTransitionKind::Stopped &&
967 stoppedTransition.stopSignal == SIGSTOP;
968 PosixSubsystem::SignalDeliveryResult continueResult =
969 PosixSubsystem::SignalDeliveryResult::Unavailable;
972 if (process->isSuspended()) {
975 }
else if (started && !context.returned) {
985 const bool continuedReported =
986 takeJobControlTransition(process, continuedTransition) &&
987 continuedTransition.kind == Process::ChildTransitionKind::Continued;
988 const bool passed = check(
990 pendingBeforeReset && pendingAfterReset && started && suspended && stoppedReported &&
991 continueResult == PosixSubsystem::SignalDeliveryResult::Ignored && joined &&
992 context.entered == 1 && context.returned == 1 && continuedReported,
993 "exec rebound a pending default stop with a stale continuation generation");
997 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
1002int suspendForIgnoredContinue(
void* parameter) {
1003 IgnoredContinueContext* context =
reinterpret_cast<IgnoredContinueContext*
>(parameter);
1004 if (context->blockSignal) {
1008 context->entered += 1;
1009 context->process->suspend();
1010 context->returned += 1;
1016 installSignalDisposition(*subsystem, SIGCONT, type);
1017 IgnoredContinueContext context(process, blockSignal);
1019 new Thread(process, suspendForIgnoredContinue, &context,
nullptr,
false,
true,
true);
1020 target->setName(
"hosted SIGCONT target");
1021 const bool started = target->
start();
1023 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
1024 while (started && !process->isSuspended() && Time::getTicks() < deadline) {
1027 const bool suspended = process->isSuspended();
1029 g_SignalHandlerCalls = 0;
1031 subsystem->
sendSignal(target, SIGCONT,
false);
1033 const bool continuedBySignal = process->getState() == Process::Active;
1034 if (!continuedBySignal) {
1043 bool continuedReported =
false;
1044 bool reportedExactlyOnce =
false;
1049 transition.kind == Process::ChildTransitionKind::Continued &&
1050 !transition.stopSignal;
1053 return started && context.entered == 1 && suspended && continuedBySignal && joined &&
1054 context.returned == 1 && continuedReported && reportedExactlyOnce;
1057bool signalContinueStillResumes(
Process* kernelProcess) {
1058 constexpr const char* Test =
"sigcont-resumes-before-disposition";
1061 process->setSubsystem(subsystem);
1064 g_SignalHandlerCalls = 0;
1065 const bool ignored = signalContinueResumes(process, subsystem, 2,
false);
1066 const bool caughtAndBlocked = signalContinueResumes(process, subsystem, 0,
true);
1068 check(ignored && caughtAndBlocked && g_SignalHandlerCalls == 0,
1069 "an ignored or blocked SIGCONT required handler delivery to resume its target");
1073 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
1078bool opposingJobControlSignalsCancelAcrossThreads(
Process* kernelProcess) {
1079 constexpr const char* Test =
"opposing-job-control-signals-cancel";
1082 process->setSubsystem(subsystem);
1085 constexpr size_t StopSignals[] = {SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU};
1086 constexpr uint64_t JobControlMask =
1087 (
static_cast<uint64_t
>(1) << (SIGCONT - 1)) | (
static_cast<uint64_t
>(1) << (SIGSTOP - 1)) |
1088 (
static_cast<uint64_t
>(1) << (SIGTSTP - 1)) | (
static_cast<uint64_t
>(1) << (SIGTTIN - 1)) |
1089 (
static_cast<uint64_t
>(1) << (SIGTTOU - 1));
1090 installSignalDisposition(*subsystem, SIGCONT, 0);
1091 for (
size_t stopSignal : StopSignals) {
1092 installSignalDisposition(*subsystem, stopSignal, 0);
1095 Thread* first =
new Thread(process, dormantSignalThread,
nullptr,
nullptr,
false,
true,
true);
1096 Thread* second =
new Thread(process, dormantSignalThread,
nullptr,
nullptr,
false,
true,
true);
1101 PosixSubsystem::SignalDeliveryResult::Queued &&
1103 const bool stopQueued = continueQueued && subsystem->
queueSignalDelivery(second, SIGTSTP) ==
1104 PosixSubsystem::SignalDeliveryResult::Queued;
1105 const bool stopCancelledContinue =
1108 bool everyStopQueued = stopCancelledContinue;
1109 for (
size_t i = 0; i <
sizeof(StopSignals) /
sizeof(StopSignals[0]); ++i) {
1110 Thread* target = i % 2 ? second : first;
1112 PosixSubsystem::SignalDeliveryResult::Queued;
1114 const bool blockedContinueQueued =
1116 PosixSubsystem::SignalDeliveryResult::Queued;
1117 bool continueCancelledEveryStop = blockedContinueQueued && first->
hasEvent(SIGCONT);
1118 for (
size_t stopSignal : StopSignals) {
1119 continueCancelledEveryStop &= !first->
hasEvent(stopSignal) && !second->
hasEvent(stopSignal);
1122 installSignalDisposition(*subsystem, SIGCONT, 2);
1123 bool ignoredSetupQueued = continueCancelledEveryStop;
1124 for (
size_t i = 0; i <
sizeof(StopSignals) /
sizeof(StopSignals[0]); ++i) {
1125 Thread* target = i % 2 ? first : second;
1127 PosixSubsystem::SignalDeliveryResult::Queued;
1129 const bool ignoredContinue =
1131 PosixSubsystem::SignalDeliveryResult::Ignored;
1132 bool ignoredContinueCancelledEveryStop =
1133 ignoredContinue && !first->
hasEvent(SIGCONT) && !second->
hasEvent(SIGCONT);
1134 for (
size_t stopSignal : StopSignals) {
1135 ignoredContinueCancelledEveryStop &=
1140 bool redundantResumeWasSilent =
false;
1146 const bool firstStarted = first->
start();
1147 const bool secondStarted = second->
start();
1150 if (!firstStarted) {
1153 if (!secondStarted) {
1157 const bool passed = check(
1158 stopCancelledContinue && continueCancelledEveryStop && ignoredContinueCancelledEveryStop &&
1159 redundantResumeWasSilent && firstStarted && secondStarted && firstJoined && secondJoined,
1160 "a process retained mutually exclusive pending stop and continue signals");
1164 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
1169struct CaughtContinueContext {
1170 explicit CaughtContinueContext(
Process* process)
1171 : process(process), finish(0), entered(0), resumed(0) {}
1179int suspendForCaughtContinue(
void* parameter) {
1180 CaughtContinueContext* context =
reinterpret_cast<CaughtContinueContext*
>(parameter);
1181 context->entered += 1;
1182 context->process->suspend();
1183 context->resumed += 1;
1184 (void)context->finish.acquire();
1188bool stoppedProcessDefersSignalsUntilContinue(
Process* kernelProcess) {
1189 constexpr const char* Test =
"stopped-process-defers-signals";
1192 process->setSubsystem(subsystem);
1194 installSignalDisposition(*subsystem, SIGUSR1, 0);
1195 installSignalDisposition(*subsystem, SIGCONT, 0, &hostedContinueHandler);
1197 IgnoredSignalWaitContext ordinaryContext;
1199 new Thread(process, waitThroughIgnoredSignal, &ordinaryContext,
nullptr,
false,
true,
true);
1200 ordinary->setName(
"hosted stopped ordinary-signal target");
1201 const bool ordinaryStarted = ordinary->
start();
1202 const bool ordinaryWaiting = ordinaryStarted && waitUntilQueued(ordinary, Thread::SemWait);
1204 CaughtContinueContext continueContext(process);
1206 new Thread(process, suspendForCaughtContinue, &continueContext,
nullptr,
false,
true,
true);
1207 continuer->setName(
"hosted caught SIGCONT target");
1208 const bool continuerStarted = ordinaryWaiting && continuer->
start();
1209 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
1210 while (continuerStarted && !process->isSuspended() && Time::getTicks() < deadline) {
1213 const bool suspended = process->isSuspended();
1215 g_SignalHandlerCalls = 0;
1216 g_ContinueHandlerCalls = 0;
1217 g_ContinueHandlerObservedActive = 0;
1218 const PosixSubsystem::SignalDeliveryResult ordinaryResult =
1220 : PosixSubsystem::SignalDeliveryResult::Unavailable;
1221 for (
size_t attempt = 0; attempt < 32 && !ordinaryContext.returned; ++attempt) {
1225 const bool ordinaryStayedPending =
1226 ordinaryResult == PosixSubsystem::SignalDeliveryResult::Queued &&
1227 ordinary->
hasEvent(SIGUSR1) && !ordinaryContext.returned && !g_SignalHandlerCalls &&
1230 const PosixSubsystem::SignalDeliveryResult continueResult =
1232 : PosixSubsystem::SignalDeliveryResult::Unavailable;
1233 const bool continuedBySignal = process->getState() == Process::Active;
1234 const Time::Timestamp deliveryDeadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
1235 while ((!ordinaryContext.returned || !g_ContinueHandlerCalls) &&
1236 Time::getTicks() < deliveryDeadline) {
1239 const bool deliveredByContinue = ordinaryContext.returned && g_ContinueHandlerCalls;
1241 if (continuerStarted && !suspended) {
1244 if (!ordinaryContext.returned) {
1245 ordinaryContext.gate.release();
1247 continueContext.finish.release();
1250 const bool continuerJoined = continuerStarted && continuer->
joinForCompletion();
1255 if (!ordinaryStarted) {
1258 if (!continuerStarted) {
1262 const bool passed = check(
1263 ordinaryStarted && ordinaryWaiting && continuerStarted && suspended &&
1264 ordinaryStayedPending && continueResult == PosixSubsystem::SignalDeliveryResult::Queued &&
1265 continuedBySignal && deliveredByContinue && ordinaryJoined && continuerJoined &&
1266 ordinaryContext.entered == 1 && ordinaryContext.returned == 1 &&
1267 ordinaryContext.acquired == 0 && ordinaryContext.error == Semaphore::Interrupted &&
1268 ordinaryContext.interruption == Thread::InterruptedBySignal &&
1269 continueContext.entered == 1 && continueContext.resumed == 1 &&
1270 g_SignalHandlerCalls == 1 && g_ContinueHandlerCalls == 1 &&
1271 g_ContinueHandlerObservedActive == 1,
1272 "a stopped signal escaped early, remained stranded, or ran SIGCONT before Active");
1276 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
1282bool execPreservesNestedSignalMask(
Thread* thread) {
1283 constexpr const char* Test =
"exec-preserves-nested-signal-mask";
1284 constexpr uint64_t TemporaryMask =
static_cast<uint64_t
>(1) << (HostedSignalNumber + 1);
1285 constexpr uint64_t NestedTemporaryMask =
static_cast<uint64_t
>(1) << (HostedSignalNumber + 2);
1286 constexpr uint64_t EffectiveMask = (
static_cast<uint64_t
>(1) << (HostedSignalNumber - 1)) |
1287 (
static_cast<uint64_t
>(1) << (HostedSignalNumber + 3));
1289 const Thread::InterruptionReason originalInterruption = thread->getInterruptionReason();
1293 bool baseTemporaryMaskActive =
false;
1294 bool nestedTemporaryMaskActive =
false;
1295 bool firstPushed =
false;
1296 bool secondPushed =
false;
1297 bool execScopePreserved =
false;
1298 bool execScopeReleased =
false;
1299 if (!originalLevel) {
1302 firstPushed = thread->
pushState() !=
nullptr;
1307 secondPushed = thread->
pushState() !=
nullptr;
1311 alternate.base = 0x200000;
1312 alternate.size = 0x8000;
1313 alternate.enabled =
true;
1314 alternate.inUse =
true;
1331 const bool passed = check(
1332 baseTemporaryMaskActive && nestedTemporaryMaskActive && firstPushed && secondPushed &&
1333 execScopePreserved && execScopeReleased && thread->
getStateLevel() == originalLevel &&
1334 thread->
getSignalMask() == EffectiveMask && !resetAlternate.base &&
1335 !resetAlternate.size && !resetAlternate.enabled && !resetAlternate.inUse &&
1337 thread->getInterruptionReason() == Thread::NotInterrupted,
1338 "exec lost the active handler mask or retained an outer temporary-mask scope");
1341 thread->setInterruptionReason(originalInterruption);
1342 thread->getAlternateSignalStack() = originalAlternate;
1344 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
1349bool invalidUserHandlerDeliveryFailsClosed(
Thread* thread) {
1350 constexpr const char* Test =
"invalid-user-handler-delivery";
1351 g_SignalHandlerCalls = 0;
1354 new SignalEvent(
reinterpret_cast<uintptr_t
>(&hostedSignalHandler), HostedSignalNumber, ~0UL,
1355 0,
true,
true, Event::HandlerPrivilege::User);
1356 const bool queued = thread->
sendEvent(event);
1363 const bool passed = check(queued && !g_SignalHandlerCalls && !thread->
getStateLevel(),
1364 "an unmapped user handler executed or retained scheduler state");
1366 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
1371struct SignalContext {
1372 SignalContext(
Thread* target,
size_t debugState,
Semaphore* releaseAfterDelivery =
nullptr)
1374 debugState(debugState),
1375 releaseAfterDelivery(releaseAfterDelivery),
1388struct DefaultActionSemaphoreContext {
1390 : target(target), gate(gate), published(0), sent(0), released(0) {}
1399struct TemporaryMaskMutexContext {
1400 TemporaryMaskMutexContext()
1401 : mutex(), holderReady(0), releaseHolder(0), holderAcquired(0), holderReturned(0) {}
1410struct SemaphoreWakeCollisionContext {
1411 SemaphoreWakeCollisionContext()
1418 interruption(
Thread::NotInterrupted),
1431SemaphoreWakeCollisionContext* g_SemaphoreWakeCollision =
nullptr;
1433struct ConditionWakeCollisionContext {
1434 ConditionWakeCollisionContext()
1456ConditionWakeCollisionContext* g_ConditionWakeCollision =
nullptr;
1458struct EventDrainContext {
1459 explicit EventDrainContext(
Event* event) : event(event), entered(0), completed(0) {}
1466int drainEventRegistrations(
void* parameter) {
1467 EventDrainContext* context =
reinterpret_cast<EventDrainContext*
>(parameter);
1468 context->entered += 1;
1469 context->event->waitForDeliveries();
1470 context->completed += 1;
1474int interruptPublishedWait(
void* parameter) {
1475 SignalContext* context =
reinterpret_cast<SignalContext*
>(parameter);
1476 if (waitUntilQueued(context->target, context->debugState)) {
1477 context->published += 1;
1481 HostedSignalNumber, ~0UL, 0,
true,
true);
1482 if (context->target->sendEvent(event)) {
1486 if (context->releaseAfterDelivery) {
1487 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
1488 while (!g_SignalHandlerCalls && Time::getTicks() < deadline) {
1491 context->releaseAfterDelivery->release();
1492 context->released += 1;
1497int holdTemporaryMaskMutex(
void* parameter) {
1498 TemporaryMaskMutexContext* context =
reinterpret_cast<TemporaryMaskMutexContext*
>(parameter);
1499 const bool acquired = context->mutex.acquire();
1501 context->holderAcquired += 1;
1503 context->holderReady.release();
1505 bool released =
false;
1507 released = context->releaseHolder.acquireForCompletion();
1508 context->mutex.release();
1510 context->holderReturned += 1;
1511 return acquired && released ? 0 : 1;
1514int waitForSemaphoreWakeCollision(
void* parameter) {
1515 SemaphoreWakeCollisionContext* context =
1516 reinterpret_cast<SemaphoreWakeCollisionContext*
>(parameter);
1517 context->entered += 1;
1519 Semaphore::SemaphoreError error = Semaphore::NoError;
1520 const bool acquired = context->semaphore.acquireWithError(1, 0, 0, error);
1521 context->acquired = acquired ? 1 : 0;
1522 context->error =
static_cast<size_t>(error);
1525 context->interruption =
static_cast<size_t>(thread->getInterruptionReason());
1526 thread->clearInterruption();
1527 context->returned += 1;
1534 SemaphoreWakeCollisionContext* context = g_SemaphoreWakeCollision;
1535 if (!context || thread != context->waiter || channel.owner != &context->semaphore ||
1536 channel.value || debugState != Thread::SemWait) {
1540 context->rescueWaits += 1;
1541 context->semaphore.release();
1544int waitForConditionWakeCollision(
void* parameter) {
1545 ConditionWakeCollisionContext* context =
1546 reinterpret_cast<ConditionWakeCollisionContext*
>(parameter);
1547 if (!context->mutex.acquireForCompletion()) {
1550 context->entered += 1;
1552 ConditionVariable::Error error = ConditionVariable::NoError;
1554 while (!context->predicate && result) {
1555 context->waits += 1;
1556 result = context->condition.wait(context->mutex, error);
1558 context->lastResult = result ? 1 : 0;
1559 context->error =
static_cast<size_t>(error);
1560 context->mutex.release();
1561 context->returned += 1;
1568 ConditionWakeCollisionContext* context = g_ConditionWakeCollision;
1569 if (!context || thread != context->waiter || channel.owner || channel.value ||
1570 debugState != Thread::CondWait) {
1574 context->rescueWaits += 1;
1575 if (context->mutex.acquireForCompletion()) {
1576 context->predicate = 1;
1577 context->condition.signal();
1578 context->mutex.release();
1582Thread* startInterrupter(SignalContext& context) {
1584 &context,
nullptr,
false,
true);
1585 thread->setName(
"hosted signal interrupter");
1589int publishDefaultActionDuringSemaphoreWait(
void* parameter) {
1590 DefaultActionSemaphoreContext* context =
1591 reinterpret_cast<DefaultActionSemaphoreContext*
>(parameter);
1592 if (waitUntilQueued(context->target, Thread::SemWait)) {
1593 context->published += 1;
1597 reinterpret_cast<uintptr_t
>(&hostedDefaultActionHandler), HostedSignalNumber, ~0UL, 0,
true,
1598 true, Event::HandlerPrivilege::Kernel, SignalEvent::DeliveryDisposition::DefaultAction);
1599 if (context->target->sendEvent(event)) {
1605 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
1606 while (!g_DefaultActionHandlerCalls && Time::getTicks() < deadline) {
1609 context->gate->release();
1610 context->released += 1;
1614bool temporarySignalMaskNestedPrequeued(
Thread* thread) {
1615 constexpr const char* Test =
"temporary-signal-mask-nested-prequeued";
1616 constexpr uint64_t SignalBit =
static_cast<uint64_t
>(1) << (HostedSignalNumber - 1);
1618 const uint64_t blockedMask = originalMask | SignalBit;
1619 const uint64_t temporaryMask = blockedMask & ~SignalBit;
1623 thread->clearInterruption();
1624 g_NestedWaitHandlerCalls = 0;
1625 g_NestedWaitHandlerLevel = 0;
1626 g_NestedWaitReturned = 0;
1627 g_NestedSignalHandlerCalls = 0;
1628 g_NestedSignalHandlerLevel = 0;
1630 HostedNestedWaitEvent outerEvent;
1631 SignalEvent signalEvent(
reinterpret_cast<uintptr_t
>(&hostedNestedSignalHandler),
1632 HostedSignalNumber);
1633 const bool outerQueued = thread->
sendEvent(&outerEvent);
1634 const bool signalQueued = outerQueued && thread->
sendEvent(&signalEvent);
1636 bool activeMaskObserved =
false;
1637 bool nestedStateRestored =
false;
1638 bool interrupted =
false;
1639 bool exactMaskRestored =
false;
1640 bool interruptionConsumed =
false;
1641 if (outerQueued && signalQueued) {
1643 activeMaskObserved = thread->
getSignalMask() == temporaryMask;
1645 nestedStateRestored = thread->
getStateLevel() == initialStateLevel;
1646 interrupted = signalWait.finish();
1648 interruptionConsumed = thread->getInterruptionReason() == Thread::NotInterrupted;
1651 const bool queuesDrained = !thread->
hasEvent(&outerEvent) && !thread->
hasEvent(&signalEvent);
1652 if (thread->
hasEvent(&outerEvent)) {
1655 if (thread->
hasEvent(&signalEvent)) {
1659 thread->clearInterruption();
1661 const bool passed = check(
1662 outerQueued && signalQueued && activeMaskObserved && nestedStateRestored && interrupted &&
1663 exactMaskRestored && interruptionConsumed && queuesDrained &&
1664 g_NestedWaitHandlerCalls == 1 && g_NestedWaitHandlerLevel == (initialStateLevel + 1) &&
1665 g_NestedWaitReturned == 1 && g_NestedSignalHandlerCalls == 1 &&
1666 g_NestedSignalHandlerLevel == (initialStateLevel + 2),
1667 "a nested prequeued signal missed its owning temporary mask or exact restoration");
1669 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
1674bool temporarySignalMaskAcrossMutex(
Thread* thread) {
1675 constexpr const char* Test =
"temporary-signal-mask-across-mutex";
1676 constexpr uint64_t SignalBit =
static_cast<uint64_t
>(1) << (HostedSignalNumber - 1);
1678 const uint64_t blockedMask = originalMask | SignalBit;
1679 const uint64_t temporaryMask = blockedMask & ~SignalBit;
1682 thread->clearInterruption();
1683 g_SignalHandlerCalls = 0;
1685 TemporaryMaskMutexContext context;
1687 &context,
nullptr,
false,
true,
true);
1688 holder->setName(
"hosted temporary signal-mask mutex holder");
1689 const bool holderStarted = holder->
start();
1690 const bool holderReady = holderStarted && context.holderReady.acquireForCompletion();
1692 bool activeMaskObserved =
false;
1693 bool mutexAcquired =
false;
1694 bool stickyAfterMutex =
false;
1695 bool interruptibleWaitReturned =
false;
1696 Semaphore::SemaphoreError waitError = Semaphore::NoError;
1697 bool interrupted =
false;
1698 bool exactMaskRestored =
false;
1699 bool interruptionConsumed =
false;
1700 bool interrupterJoined =
false;
1701 SignalContext signalContext(thread, Thread::SemWait, &context.releaseHolder);
1702 if (holderReady && context.holderAcquired == 1) {
1704 activeMaskObserved = thread->
getSignalMask() == temporaryMask;
1705 Thread* interrupter = startInterrupter(signalContext);
1707 mutexAcquired = context.mutex.acquire();
1709 if (mutexAcquired) {
1710 context.mutex.release();
1713 if (stickyAfterMutex) {
1715 interruptibleWaitReturned = !interruptible.acquireWithError(1, 0, 0, waitError);
1718 interrupted = signalWait.finish();
1720 interruptionConsumed = thread->getInterruptionReason() == Thread::NotInterrupted;
1721 interrupterJoined = interrupter->
join();
1723 context.releaseHolder.release();
1726 const bool holderJoined = holderStarted && holder->
join();
1727 if (!holderStarted) {
1731 thread->clearInterruption();
1733 const bool passed = check(
1734 holderStarted && holderReady && context.holderAcquired == 1 && context.holderReturned == 1 &&
1735 activeMaskObserved && mutexAcquired && stickyAfterMutex && interruptibleWaitReturned &&
1736 waitError == Semaphore::Interrupted && interrupted && exactMaskRestored &&
1737 interruptionConsumed && interrupterJoined && holderJoined &&
1738 signalContext.published == 1 && signalContext.sent == 1 && signalContext.released == 1 &&
1739 g_SignalHandlerCalls == 1,
1740 "a non-interruptible Mutex lost or consumed its armed temporary-wait signal");
1742 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
1747bool temporarySignalMaskIgnoresDefaultAction(
Thread* thread) {
1748 constexpr const char* Test =
"temporary-signal-mask-default-action";
1750 thread->clearInterruption();
1751 g_DefaultActionHandlerCalls = 0;
1754 DefaultActionSemaphoreContext context(thread, &gate);
1757 &context,
nullptr,
false,
true);
1758 publisher->setName(
"hosted default-action signal publisher");
1760 Semaphore::SemaphoreError error = Semaphore::NoError;
1761 bool acquired =
false;
1762 bool interrupted =
true;
1765 acquired = gate.acquireWithError(1, 0, 0, error);
1766 interrupted = signalWait.finish();
1768 const bool joined = publisher->
join();
1769 const bool interruptionConsumed = thread->getInterruptionReason() == Thread::NotInterrupted;
1771 thread->clearInterruption();
1774 check(acquired && error == Semaphore::NoError && !interrupted && joined &&
1775 interruptionConsumed && context.published == 1 && context.sent == 1 &&
1776 context.released == 1 && g_DefaultActionHandlerCalls == 1,
1777 "a default signal action terminated a temporary-mask Semaphore wait");
1779 NOTICE(
"HOSTED-WAIT-TEST: PASS " << Test);
1784bool conditionVariableSignalInterruption(
Thread* thread) {
1787 SignalContext context(thread, Thread::CondWait);
1789 if (!mutex.acquire()) {
1790 return check(
false,
"the ConditionVariable mutex was unavailable");
1793 g_SignalHandlerCalls = 0;
1794 Thread* interrupter = startInterrupter(context);
1796 ConditionVariable::Error error = ConditionVariable::NoError;
1797 const bool waited = condition.
wait(mutex, error);
1798 const bool mutexHeld = mutex.isOwnedByCurrentThread();
1802 const bool joined = interrupter->
join();
1804 passed &= check(!waited && error == ConditionVariable::Interrupted,
1805 "ConditionVariable did not report Interrupted");
1806 passed &= check(mutexHeld,
"ConditionVariable did not reacquire its mutex");
1808 check(joined && context.published == 1 && context.sent == 1 && g_SignalHandlerCalls == 1,
1809 "ConditionVariable wait did not receive one published signal");
1813bool bufferSignalInterruption(
Thread* thread) {
1815 SignalContext context(thread, Thread::CondWait);
1817 thread->clearInterruption();
1818 g_SignalHandlerCalls = 0;
1819 Thread* interrupter = startInterrupter(context);
1822 const size_t read = buffer.read(&value, 1,
true);
1823 const Thread::InterruptionReason reason = thread->getInterruptionReason();
1824 thread->clearInterruption();
1825 const bool joined = interrupter->
join();
1827 return check(read == 0 && reason == Thread::InterruptedBySignal && joined &&
1828 context.published == 1 && context.sent == 1 && g_SignalHandlerCalls == 1,
1829 "Buffer did not preserve its signal interruption");
1832bool semaphoreSignalInterruption(
Thread* thread) {
1834 SignalContext context(thread, Thread::SemWait);
1836 thread->clearInterruption();
1837 g_SignalHandlerCalls = 0;
1838 Thread* interrupter = startInterrupter(context);
1840 Semaphore::SemaphoreError error = Semaphore::NoError;
1841 const bool acquired = semaphore.acquireWithError(1, 0, 0, error);
1842 const Thread::InterruptionReason reason = thread->getInterruptionReason();
1843 thread->clearInterruption();
1844 const bool joined = interrupter->
join();
1846 return check(!acquired && error == Semaphore::Interrupted &&
1847 reason == Thread::InterruptedBySignal && joined && context.published == 1 &&
1848 context.sent == 1 && g_SignalHandlerCalls == 1,
1849 "Semaphore did not preserve its signal interruption");
1852bool semaphoreSignalAfterOrdinaryWake() {
1853 SemaphoreWakeCollisionContext context;
1855 waitForSemaphoreWakeCollision, &context,
nullptr,
false,
true);
1856 waiter->setName(
"hosted semaphore wake/signal collision");
1859 const bool queued = waitUntilQueued(
waiter, Thread::SemWait);
1860 g_SignalHandlerCalls = 0;
1861 g_SemaphoreWakeCollision = &context;
1862 WaitQueue::setBeforeBlockHook(semaphoreWakeCollisionRescue);
1866 context.semaphore.release();
1867 const bool releaseConsumed = context.semaphore.tryAcquire();
1869 HostedSignalNumber, ~0UL, 0,
true,
true);
1870 const bool sent =
waiter->sendEvent(event);
1875 const bool joined =
waiter->join();
1876 WaitQueue::setBeforeBlockHook(
nullptr);
1877 g_SemaphoreWakeCollision =
nullptr;
1879 const bool passed = check(queued && releaseConsumed && sent && joined && context.entered == 1 &&
1880 context.returned == 1 && context.acquired == 0 &&
1881 context.error == Semaphore::Interrupted &&
1882 context.interruption == Thread::InterruptedBySignal &&
1883 context.rescueWaits == 0 && g_SignalHandlerCalls == 1,
1884 "Semaphore re-blocked after a signal lost the waiter.reason race");
1887 "HOSTED-WAIT-TEST: PASS "
1888 "semaphore-signal-after-ordinary-wake");
1893bool conditionSignalAfterOrdinaryWake() {
1894 ConditionWakeCollisionContext context;
1896 waitForConditionWakeCollision, &context,
nullptr,
false,
true);
1897 waiter->setName(
"hosted condition wake/signal collision");
1900 const bool queued = waitUntilQueued(
waiter, Thread::CondWait);
1901 g_SignalHandlerCalls = 0;
1902 g_ConditionWakeCollision = &context;
1903 WaitQueue::setBeforeBlockHook(conditionWakeCollisionRescue);
1907 context.condition.signal();
1909 HostedSignalNumber, ~0UL, 0,
true,
true);
1910 const bool sent =
waiter->sendEvent(event);
1915 const bool joined =
waiter->join();
1916 WaitQueue::setBeforeBlockHook(
nullptr);
1917 g_ConditionWakeCollision =
nullptr;
1920 check(queued && sent && joined && context.entered == 1 && context.returned == 1 &&
1921 context.waits == 1 && context.lastResult == 0 &&
1922 context.error == ConditionVariable::Interrupted && context.rescueWaits == 0 &&
1923 g_SignalHandlerCalls == 1,
1924 "ConditionVariable re-blocked after a signal lost the waiter.reason race");
1927 "HOSTED-WAIT-TEST: PASS "
1928 "condition-signal-after-ordinary-wake");
1933bool completionSemaphoreSignalDeferral(
Thread* thread) {
1935 SignalContext context(thread, Thread::SemWait, &semaphore);
1937 thread->clearInterruption();
1938 g_SignalHandlerCalls = 0;
1939 Thread* interrupter = startInterrupter(context);
1941 const bool acquired = semaphore.acquireForCompletion();
1942 const Thread::InterruptionReason reason = thread->getInterruptionReason();
1943 thread->clearInterruption();
1944 const bool joined = interrupter->
join();
1946 return check(acquired && reason == Thread::InterruptedBySignal && joined &&
1947 context.published == 1 && context.sent == 1 && context.released == 1 &&
1948 g_SignalHandlerCalls == 1 && semaphore.getValue() == 0,
1949 "a completion semaphore returned before its signal-delayed release");
1952bool ringBufferSignalInterruption(
Thread* thread) {
1954 SignalContext context(thread, Thread::CondWait);
1956 thread->clearInterruption();
1957 g_SignalHandlerCalls = 0;
1958 Thread* interrupter = startInterrupter(context);
1960 Time::Timestamp timeout = Time::Infinity;
1963 const bool read = buffer.read(value, timeout, error);
1964 const Thread::InterruptionReason reason = thread->getInterruptionReason();
1965 thread->clearInterruption();
1966 const bool joined = interrupter->
join();
1969 reason == Thread::InterruptedBySignal && joined && context.published == 1 &&
1970 context.sent == 1 && g_SignalHandlerCalls == 1,
1971 "RingBuffer did not preserve its signal interruption");
1974bool ringBufferMonitorCull(
Thread* thread) {
1976 SignalEvent event(
reinterpret_cast<uintptr_t
>(&hostedSignalHandler), HostedSignalNumber);
1978 buffer.monitor(thread, &event);
1979 buffer.cullMonitorTargets(thread);
1981 return check(!buffer.dataReady() && buffer.canWrite(),
1982 "RingBuffer monitor culling retained its internal mutex");
1985bool ringBufferMonitorRetirement(
Thread* thread) {
1987 HostedMonitorEvent event;
1988 buffer.monitor(thread, &event);
1990 EventDrainContext context(&event);
1992 &context,
nullptr,
false,
true);
1993 drainer->setName(
"hosted RingBuffer monitor retirement");
1995 const bool closePublished = waitUntilQueued(drainer, Thread::EventWait);
2000 buffer.monitor(drainer, &event);
2001 buffer.cullMonitorTargets(thread);
2003 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
2004 while (!context.completed && Time::getTicks() < deadline) {
2007 const bool rearmRejected = context.completed == 1;
2010 if (!rearmRejected) {
2011 buffer.cullMonitorTargets(drainer);
2013 const bool joined = drainer->
join();
2015 return check(context.entered == 1 && closePublished && rearmRejected && joined,
2016 "RingBuffer accepted a monitor registration after Event retirement");
2019bool ringBufferMonitorDestructor(
Thread* thread) {
2020 const size_t destructionsBefore = g_MonitorEventDestructions;
2021 HostedMonitorEvent*
event =
new HostedMonitorEvent;
2024 buffer.monitor(thread, event);
2030 const bool closeNotified =
event->pendingCount() == 1 && thread->
hasEvent(event);
2033 event->beginRetirement(retirement);
2036 return check(closeNotified && g_MonitorEventDestructions == (destructionsBefore + 1),
2037 "RingBuffer destruction retained its Event source lease after the "
2038 "queued close notification drained");
2041bool delaySignalInterruption(
Thread* thread) {
2042 SignalContext context(thread, Thread::EventWait);
2044 thread->clearInterruption();
2045 g_SignalHandlerCalls = 0;
2046 Thread* interrupter = startInterrupter(context);
2048 const bool delayed = Time::delay(5 * Time::Multiplier::Second);
2049 const Thread::InterruptionReason reason = thread->getInterruptionReason();
2050 thread->clearInterruption();
2051 const bool joined = interrupter->
join();
2053 return check(!delayed && reason == Thread::InterruptedBySignal && joined &&
2054 context.published == 1 && context.sent == 1 && g_SignalHandlerCalls == 1,
2055 "Time::delay did not preserve its signal interruption");
2058bool prequeuedDelaySignalInterruption(
Thread* thread) {
2059 thread->clearInterruption();
2060 g_SignalHandlerCalls = 0;
2063 HostedSignalNumber, ~0UL, 0,
true,
true);
2066 return check(
false,
"a prequeued signal could not be published");
2069 const bool delayed = Time::delay(5 * Time::Multiplier::Second);
2070 const Thread::InterruptionReason reason = thread->getInterruptionReason();
2071 thread->clearInterruption();
2073 return check(!delayed && reason == Thread::InterruptedBySignal && g_SignalHandlerCalls == 1,
2074 "a prequeued signal did not interrupt Time::delay");
2078bool runHostedSignalInterruptionRegressions(
Thread* thread) {
2080 eventHandlerPrivilege() && signalCullPreservesNumberCollision(thread) &&
2081 pendingSignalRunsAtSyscallReturn(thread) &&
2082 exactUserReturnSignalDefersWithoutContext(thread) && execPreservesNestedSignalMask(thread) &&
2083 invalidUserHandlerDeliveryFailsClosed(thread) &&
2084#if !defined(PEDIGREE_HOSTED_CORE_SMOKE)
2085 ignoredSignalDoesNotInterruptWait(thread->
getParent()) &&
2086 ignoredDispositionDiscardsPendingSignals(thread->
getParent()) &&
2087 execSignalResetRebindsPending(thread->
getParent()) &&
2088 defaultStopControl(thread->
getParent()) &&
2089 staleDefaultStopRejectedAfterContinue(thread->
getParent()) &&
2090 staleDefaultStopRejectedAcrossAba(thread->
getParent()) &&
2091 execSignalResetRestampsPendingStop(thread->
getParent()) &&
2092 signalContinueStillResumes(thread->
getParent()) &&
2093 opposingJobControlSignalsCancelAcrossThreads(thread->
getParent()) &&
2094 stoppedProcessDefersSignalsUntilContinue(thread->
getParent()) &&
2096 temporarySignalMaskNestedPrequeued(thread) && temporarySignalMaskAcrossMutex(thread) &&
2097 temporarySignalMaskIgnoresDefaultAction(thread) &&
2098 conditionVariableSignalInterruption(thread) && bufferSignalInterruption(thread) &&
2099 semaphoreSignalInterruption(thread) && semaphoreSignalAfterOrdinaryWake() &&
2100 conditionSignalAfterOrdinaryWake() && completionSemaphoreSignalDeferral(thread) &&
2101 ringBufferSignalInterruption(thread) && ringBufferMonitorCull(thread) &&
2102 ringBufferMonitorRetirement(thread) && ringBufferMonitorDestructor(thread) &&
2103 delaySignalInterruption(thread) && prequeuedDelaySignalInterruption(thread);
2105 NOTICE(
"HOSTED-WAIT-TEST: PASS signal-interruption");
MUST_USE_RESULT bool wait(Mutex &mutex, Time::Timestamp &timeout, Error &error, WaitQueue::StackDiscardCleanup onStackDiscard=nullptr, void *stackDiscardContext=nullptr)
virtual bool prefersAlternateUserStack() const
HandlerPrivilege getHandlerPrivilege() const
virtual UserReturnDelivery deliverAtUserReturn(InterruptState &)
virtual bool requiresExactUserReturnState() const
virtual size_t getNumber()=0
virtual size_t serialize(uint8_t *pBuffer)=0
virtual Event * cloneForDelivery()
void checkEventState(uintptr_t userStack)
MUST_USE_RESULT bool serviceUserReturnWork(InterruptState &state, UserReturnFrame::Origin origin=UserReturnFrame::Origin::Interrupt, bool diagnosticSample=false)
virtual void sendSignal(Thread *pThread, int signal, bool yield=true, bool processDirected=false)
SignalDeliveryResult queueSignalDelivery(Thread *target, size_t sig, uint32_t *flags=nullptr, int32_t signalCode=0, bool processDirected=false, uint64_t signalValue=0, const SharedPointer< SignalEventState > &state=SharedPointer< SignalEventState >())
void setSignalHandler(size_t sig, SignalHandler *handler)
bool getSignalDisposition(size_t sig, SignalDisposition &disposition, bool beginDelivery=false)
WaitQueue::Guard acquireChildStateWait()
bool takePendingChildTransition(bool includeStopped, bool includeContinued, ChildTransition &transition)
size_t getContinuationEpoch()
static bool getInterrupts()
static ProcessorInformation & information()
Utility class to provide a ring buffer.
static Scheduler & instance()
bool hasActiveTemporarySignalMask()
void setUnwindState(UnwindType ut)
@ TerminateThread
Exit only this thread during Process exit.
bool hasSignalEvent(size_t signalNumber, int processDirected=-1)
bool getWaitDebugInfo(WaitDebugInfo &info)
bool hasEvent(Event *pEvent)
void waitForEvent(WaitQueue::StackDiscardCleanup onStackDiscard=nullptr, void *stackDiscardContext=nullptr)
bool eventsDeferred() const
void cullEvent(Event *pEvent)
DebugState getDebugState(uintptr_t &address)
Process * getParent() const
void prepareSignalStateForExec()
void cullSignalEvent(size_t signalNumber)
void setSignalMask(uint64_t mask)
class PerProcessorScheduler * getScheduler() const
bool hasTemporarySignalWaitInterruption()
SchedulerState * pushState()
bool sendEvent(Event *pEvent)
size_t getStateLevel() const
void abandonCurrentState(bool clean=false)
static const size_t KernelMode
static const size_t Execute
int type
Type - 0 = normal, 1 = SIG_DFL, 2 = SIG_IGN.
SignalEvent * pEvent
Event for the signal handler.
uintptr_t restorer
Userspace restorer for Linux-compatible signal delivery.
uint64_t sigMask
Signal mask to set when this signal handler is called.
uint32_t flags
Signal handler flags.