Skip to content

Commit 762e047

Browse files
committed
Kernel+LibC: Implement sigtimedwait()
This includes a new Thread::Blocker called SignalBlocker which blocks until a signal of a matching type is pending. The current Blocker implementation in the Kernel is very complicated, but cleaning it up is a different yak for a different day.
1 parent 13d9899 commit 762e047

File tree

8 files changed

+120
-0
lines changed

8 files changed

+120
-0
lines changed

Kernel/API/Syscall.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ enum class NeedsBigProcessLock {
176176
S(sigpending, NeedsBigProcessLock::Yes) \
177177
S(sigprocmask, NeedsBigProcessLock::Yes) \
178178
S(sigreturn, NeedsBigProcessLock::Yes) \
179+
S(sigtimedwait, NeedsBigProcessLock::Yes) \
179180
S(socket, NeedsBigProcessLock::Yes) \
180181
S(socketpair, NeedsBigProcessLock::Yes) \
181182
S(stat, NeedsBigProcessLock::Yes) \

Kernel/Process.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@ class Process final
338338
ErrorOr<FlatPtr> sys$sigaltstack(Userspace<const stack_t*> ss, Userspace<stack_t*> old_ss);
339339
ErrorOr<FlatPtr> sys$sigprocmask(int how, Userspace<const sigset_t*> set, Userspace<sigset_t*> old_set);
340340
ErrorOr<FlatPtr> sys$sigpending(Userspace<sigset_t*>);
341+
ErrorOr<FlatPtr> sys$sigtimedwait(Userspace<sigset_t const*>, Userspace<siginfo_t*>, Userspace<const timespec*>);
341342
ErrorOr<FlatPtr> sys$getgroups(size_t, Userspace<gid_t*>);
342343
ErrorOr<FlatPtr> sys$setgroups(size_t, Userspace<const gid_t*>);
343344
ErrorOr<FlatPtr> sys$pipe(int pipefd[2], int flags);

Kernel/Syscalls/sigaction.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,4 +307,35 @@ ErrorOr<FlatPtr> Process::sys$sigaltstack(Userspace<const stack_t*> ss, Userspac
307307
return 0;
308308
}
309309

310+
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigtimedwait.html
311+
ErrorOr<FlatPtr> Process::sys$sigtimedwait(Userspace<const sigset_t*> set, Userspace<siginfo_t*> info, Userspace<const timespec*> timeout)
312+
{
313+
VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this)
314+
REQUIRE_PROMISE(sigaction);
315+
316+
sigset_t set_value;
317+
TRY(copy_from_user(&set_value, set));
318+
319+
Thread::BlockTimeout block_timeout = {};
320+
if (timeout) {
321+
auto timeout_time = TRY(copy_time_from_user(timeout));
322+
block_timeout = Thread::BlockTimeout(false, &timeout_time);
323+
}
324+
325+
siginfo_t info_value = {};
326+
auto block_result = Thread::current()->block<Thread::SignalBlocker>(block_timeout, set_value, info_value);
327+
if (block_result.was_interrupted())
328+
return EINTR;
329+
// We check for an unset signal instead of directly checking for a timeout interruption
330+
// in order to allow polling the pending signals by setting the timeout to 0.
331+
if (info_value.si_signo == SIGINVAL) {
332+
VERIFY(block_result == Thread::BlockResult::InterruptedByTimeout);
333+
return EAGAIN;
334+
}
335+
336+
if (info)
337+
TRY(copy_to_user(info, &info_value));
338+
return info_value.si_signo;
339+
}
340+
310341
}

Kernel/Thread.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,7 @@ void Thread::send_signal(u8 signal, [[maybe_unused]] Process* sender)
637637

638638
m_pending_signals |= 1 << (signal - 1);
639639
m_have_any_unmasked_pending_signals.store((pending_signals_for_state() & ~m_signal_mask) != 0, AK::memory_order_release);
640+
m_signal_blocker_set.unblock_all_blockers_whose_conditions_are_met();
640641

641642
if (!has_unmasked_pending_signals())
642643
return;

Kernel/Thread.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ class Thread
292292
Queue,
293293
Routing,
294294
Sleep,
295+
Signal,
295296
Wait
296297
};
297298
virtual ~Blocker();
@@ -698,6 +699,41 @@ class Thread
698699
bool m_did_unblock { false };
699700
};
700701

702+
class SignalBlocker final : public Blocker {
703+
public:
704+
explicit SignalBlocker(sigset_t pending_set, siginfo_t& result);
705+
virtual StringView state_string() const override { return "Pending Signal"sv; }
706+
virtual Type blocker_type() const override { return Type::Signal; }
707+
void will_unblock_immediately_without_blocking(UnblockImmediatelyReason) override;
708+
virtual bool setup_blocker() override;
709+
bool check_pending_signals(bool from_add_blocker);
710+
711+
private:
712+
sigset_t m_pending_set { 0 };
713+
siginfo_t& m_result;
714+
bool m_did_unblock { false };
715+
};
716+
717+
class SignalBlockerSet final : public BlockerSet {
718+
public:
719+
void unblock_all_blockers_whose_conditions_are_met()
720+
{
721+
BlockerSet::unblock_all_blockers_whose_conditions_are_met([&](auto& b, void*, bool&) {
722+
VERIFY(b.blocker_type() == Blocker::Type::Signal);
723+
auto& blocker = static_cast<Thread::SignalBlocker&>(b);
724+
return blocker.check_pending_signals(false);
725+
});
726+
}
727+
728+
private:
729+
bool should_add_blocker(Blocker& b, void*) override
730+
{
731+
VERIFY(b.blocker_type() == Blocker::Type::Signal);
732+
auto& blocker = static_cast<Thread::SignalBlocker&>(b);
733+
return !blocker.check_pending_signals(true);
734+
}
735+
};
736+
701737
class WaitBlocker final : public Blocker {
702738
public:
703739
enum class UnblockFlags {
@@ -1302,6 +1338,7 @@ class Thread
13021338
u32 m_signal_mask { 0 };
13031339
FlatPtr m_alternative_signal_stack { 0 };
13041340
FlatPtr m_alternative_signal_stack_size { 0 };
1341+
SignalBlockerSet m_signal_blocker_set;
13051342
FlatPtr m_kernel_stack_base { 0 };
13061343
FlatPtr m_kernel_stack_top { 0 };
13071344
OwnPtr<Memory::Region> m_kernel_stack_region;

Kernel/ThreadBlockers.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,48 @@ void Thread::SelectBlocker::was_unblocked(bool did_timeout)
446446
}
447447
}
448448

449+
Thread::SignalBlocker::SignalBlocker(sigset_t pending_set, siginfo_t& result)
450+
: m_pending_set(pending_set)
451+
, m_result(result)
452+
{
453+
}
454+
455+
void Thread::SignalBlocker::will_unblock_immediately_without_blocking(UnblockImmediatelyReason unblock_immediately_reason)
456+
{
457+
if (unblock_immediately_reason != UnblockImmediatelyReason::TimeoutInThePast)
458+
return;
459+
// If the specified timeout is 0 the caller is simply trying to poll once for pending signals,
460+
// so simply calling check_pending_signals should populate the requested information.
461+
check_pending_signals(false);
462+
}
463+
464+
bool Thread::SignalBlocker::setup_blocker()
465+
{
466+
return add_to_blocker_set(thread().m_signal_blocker_set);
467+
}
468+
469+
bool Thread::SignalBlocker::check_pending_signals(bool from_add_blocker)
470+
{
471+
{
472+
SpinlockLocker lock(m_lock);
473+
if (m_did_unblock)
474+
return false;
475+
476+
auto matching_pending_signal = __builtin_ffsl(thread().pending_signals() & m_pending_set);
477+
if (matching_pending_signal == 0)
478+
return false;
479+
480+
m_did_unblock = true;
481+
m_result = {};
482+
m_result.si_signo = matching_pending_signal;
483+
m_result.si_code = 0; // FIXME: How can we determine this?
484+
}
485+
486+
if (!from_add_blocker)
487+
unblock_from_blocker();
488+
return true;
489+
}
490+
449491
Thread::WaitBlockerSet::ProcessBlockInfo::ProcessBlockInfo(NonnullRefPtr<Process>&& process, WaitBlocker::UnblockFlags flags, u8 signal)
450492
: process(move(process))
451493
, flags(flags)

Userland/Libraries/LibC/signal.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,12 @@ int sigsuspend(const sigset_t* set)
163163
return pselect(0, nullptr, nullptr, nullptr, nullptr, set);
164164
}
165165

166+
int sigtimedwait(sigset_t const* set, siginfo_t* info, struct timespec const* timeout)
167+
{
168+
int rc = syscall(Syscall::SC_sigtimedwait, set, info, timeout);
169+
__RETURN_WITH_ERRNO(rc, rc, -1);
170+
}
171+
166172
const char* sys_signame[] = {
167173
"INVAL",
168174
"HUP",

Userland/Libraries/LibC/signal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ int sigismember(const sigset_t*, int sig);
2727
int sigprocmask(int how, const sigset_t* set, sigset_t* old_set);
2828
int sigpending(sigset_t*);
2929
int sigsuspend(const sigset_t*);
30+
int sigtimedwait(sigset_t const*, siginfo_t*, struct timespec const*);
3031
int raise(int sig);
3132
int getsignalbyname(const char*);
3233
const char* getsignalname(int);

0 commit comments

Comments
 (0)