feat: add sigRaise and sigRaiseGroup to signal
This commit is contained in:
1 parent
26476fc78b
commit
8f2554114f
1 file changed
+161
+161
@@ -154,3 +154,164 @@ bool sigRaiseInfo(uint32 targetId, int signum, int siCode, int siPid, int siStat
|
||||
target->sigPendingInfo[signum].siPid = siPid;
|
||||
target->sigPendingInfo[signum].siStatus = siStatus;
|
||||
|
||||
/* Real Unix's interruptible sleep/EINTR (deliberately out of scope
|
||||
until now, see the file comment's history): a Sleeping/Blocked
|
||||
target whose sleep timer or wait-channel condition would otherwise
|
||||
never naturally fire again (e.g. user/eintrtarget.c's read that no
|
||||
writer will ever satisfy) must still actually receive a signal that
|
||||
would genuinely be delivered, not sit on it forever. Skipped
|
||||
entirely for a blocked or ignored signal -- see sigWouldDeliver --
|
||||
so a masked/ignored signal never disturbs a thread's sleep/wait at
|
||||
all, matching real Unix. threadWakeForSignal itself refuses anything
|
||||
other than Sleeping/Blocked (Stopped is excluded on purpose -- only
|
||||
threadContinue may move it), so this is safe to call unconditionally
|
||||
whenever sigWouldDeliver agrees. */
|
||||
if (sigWouldDeliver(target, signum))
|
||||
threadWakeForSignal(targetId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sigRaise(uint32 targetId, int signum)
|
||||
{
|
||||
return sigRaiseInfo(targetId, signum, siCodeKernel, 0, 0);
|
||||
}
|
||||
|
||||
bool sigRaiseGroup(uint32 pgid, int signum, int siCode, int siPid)
|
||||
{
|
||||
bool anyTarget = false;
|
||||
for (uint32 id = 1; id < threadCapacity(); id++) {
|
||||
Thread* candidate = threadAt(id);
|
||||
if (candidate->state == threadDead || candidate->state == threadUnused)
|
||||
continue;
|
||||
if (candidate->pgid != pgid)
|
||||
continue;
|
||||
|
||||
anyTarget = true;
|
||||
sigRaiseInfo(id, signum, siCode, siPid, 0);
|
||||
}
|
||||
return anyTarget;
|
||||
}
|
||||
|
||||
uint64 sigSetHandler(Thread* thread, int signum, uint64 handlerAddr, uint64 mask, uint64 flags)
|
||||
{
|
||||
if (signum <= 0 || signum >= sigMaxSignals)
|
||||
return sigSetFailed;
|
||||
if (signum == sigKill || signum == sigStop)
|
||||
return sigSetFailed;
|
||||
|
||||
uint64 previous = thread->sigHandlers[signum];
|
||||
thread->sigHandlers[signum] = handlerAddr;
|
||||
|
||||
/* Same stripping sigProcMask already does to a mask it is handed: a
|
||||
mask cannot make sigKill/sigStop blockable just because it asked. */
|
||||
mask &= ~(1ull << sigKill);
|
||||
mask &= ~(1ull << sigStop);
|
||||
thread->sigHandlerMasks[signum] = mask;
|
||||
thread->sigHandlerFlags[signum] = flags;
|
||||
|
||||
return previous;
|
||||
}
|
||||
|
||||
uint64 sigProcMask(Thread* thread, int how, uint64 mask)
|
||||
{
|
||||
uint64 previous = thread->sigBlocked;
|
||||
mask &= ~(1ull << sigKill);
|
||||
mask &= ~(1ull << sigStop);
|
||||
|
||||
switch (how) {
|
||||
case sigMaskBlock:
|
||||
thread->sigBlocked |= mask;
|
||||
break;
|
||||
case sigMaskUnblock:
|
||||
thread->sigBlocked &= ~mask;
|
||||
break;
|
||||
case sigMaskSetMask:
|
||||
thread->sigBlocked = mask;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return previous;
|
||||
}
|
||||
|
||||
SigOutcome sigConsumePending(Thread* thread, Registers* regs, int* outSignum)
|
||||
{
|
||||
if (thread->sigHandlerDepth >= sigMaxNestDepth)
|
||||
return sigOutcomeNone;
|
||||
|
||||
for (int signum = 1; signum < sigMaxSignals; signum++) {
|
||||
uint64 bit = 1ull << (uint32)signum;
|
||||
if (!(thread->sigPending & bit) || (thread->sigBlocked & bit))
|
||||
continue;
|
||||
|
||||
thread->sigPending &= ~bit;
|
||||
uint64 handler = thread->sigHandlers[signum];
|
||||
|
||||
if (handler == sigHandlerIgnore ||
|
||||
(handler == sigHandlerDefault && sigDefaultIsIgnore(signum)))
|
||||
continue;
|
||||
|
||||
if (outSignum)
|
||||
*outSignum = signum;
|
||||
|
||||
if (handler == sigHandlerDefault) {
|
||||
if (sigDefaultIsStop(signum))
|
||||
return sigOutcomeStop;
|
||||
return sigOutcomeTerminate;
|
||||
}
|
||||
|
||||
/* SA_RESTART / EINTR (see file comment): consumed here, exactly
|
||||
once, regardless of which way the decision goes -- a signal
|
||||
nested INSIDE the handler this delivery is about to enter is
|
||||
interrupting that handler's own code, not a retryable syscall
|
||||
retry point, so it must never see this as still true. Only
|
||||
rewrites `regs` (the context about to be saved below) when this
|
||||
thread was genuinely mid one of sysRead/sysWrite/sysWait's own
|
||||
retry-on-block pattern AND this particular signal was not
|
||||
registered with sigFlagRestart -- anything else (plain running
|
||||
code, a sleep, an already-nested handler, or sigFlagRestart
|
||||
itself) leaves `regs` completely untouched, preserving the
|
||||
unconditional-retry behavior this kernel had before the flag
|
||||
existed. */
|
||||
bool interruptedRetry = thread->sigInRetryableSyscall;
|
||||
thread->sigInRetryableSyscall = false;
|
||||
if (interruptedRetry && !(thread->sigHandlerFlags[signum] & sigFlagRestart)) {
|
||||
regs->rip += 2;
|
||||
regs->rax = (uint64)(int64)errInterrupted;
|
||||
}
|
||||
|
||||
/* Push the whole interrupted trapframe (now possibly rewritten
|
||||
above) onto this thread's small context stack (the only copy of
|
||||
it that exists anywhere -- unlike a real kernel this never pushes
|
||||
one onto the user stack) so sigOnSigReturn can restore it byte
|
||||
for byte, even if a DIFFERENT, unmasked signal nests another
|
||||
handler on top before this one ever returns. */
|
||||
uint32 depth = thread->sigHandlerDepth;
|
||||
thread->sigSavedRegs[depth] = *regs;
|
||||
thread->sigSavedBlocked[depth] = thread->sigBlocked;
|
||||
thread->sigHandlerDepth = depth + 1;
|
||||
|
||||
/* sa_mask (sigHandlerMasks[signum], set by sigSetHandler) plus the
|
||||
signal's own bit (self-blocked, like real Unix's implicit
|
||||
default) are ADDED to whatever was already blocked -- a union,
|
||||
not a replacement, so an outer handler's own blocked set stays
|
||||
blocked for this nested one too. sigFlagNoDefer (signalnum.h)
|
||||
opts out of exactly the self-block half of that union: a signal
|
||||
registered with it does not gain its own bit here, so a second
|
||||
raise of the SAME signum while this handler is still active
|
||||
finds sigBlocked genuinely clear for it and nests, instead of
|
||||
merely staying pending until this handler returns. Stripped of
|
||||
sigKill/sigStop for the same reason every other mask operation in
|
||||
this file is: neither ever consults sigBlocked at all, so this is
|
||||
purely cosmetic, but leaving either bit set here would visibly
|
||||
contradict "can never be blocked" for anything that inspects
|
||||
sigBlocked. */
|
||||
uint64 newBlocked = thread->sigBlocked | thread->sigHandlerMasks[signum];
|
||||
if (!(thread->sigHandlerFlags[signum] & sigFlagNoDefer))
|
||||
newBlocked |= bit;
|
||||
newBlocked &= ~(1ull << sigKill);
|
||||
newBlocked &= ~(1ull << sigStop);
|
||||
thread->sigBlocked = newBlocked;
|
||||
|
||||
Reference in new issue
Block a user