1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-24 22:17:42 +00:00

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.
This commit is contained in:
Idan Horowitz 2021-12-12 01:01:42 +02:00
parent 13d98999b7
commit 762e047ec9
8 changed files with 120 additions and 0 deletions

View file

@ -176,6 +176,7 @@ enum class NeedsBigProcessLock {
S(sigpending, NeedsBigProcessLock::Yes) \
S(sigprocmask, NeedsBigProcessLock::Yes) \
S(sigreturn, NeedsBigProcessLock::Yes) \
S(sigtimedwait, NeedsBigProcessLock::Yes) \
S(socket, NeedsBigProcessLock::Yes) \
S(socketpair, NeedsBigProcessLock::Yes) \
S(stat, NeedsBigProcessLock::Yes) \

View file

@ -338,6 +338,7 @@ public:
ErrorOr<FlatPtr> sys$sigaltstack(Userspace<const stack_t*> ss, Userspace<stack_t*> old_ss);
ErrorOr<FlatPtr> sys$sigprocmask(int how, Userspace<const sigset_t*> set, Userspace<sigset_t*> old_set);
ErrorOr<FlatPtr> sys$sigpending(Userspace<sigset_t*>);
ErrorOr<FlatPtr> sys$sigtimedwait(Userspace<sigset_t const*>, Userspace<siginfo_t*>, Userspace<const timespec*>);
ErrorOr<FlatPtr> sys$getgroups(size_t, Userspace<gid_t*>);
ErrorOr<FlatPtr> sys$setgroups(size_t, Userspace<const gid_t*>);
ErrorOr<FlatPtr> sys$pipe(int pipefd[2], int flags);

View file

@ -307,4 +307,35 @@ ErrorOr<FlatPtr> Process::sys$sigaltstack(Userspace<const stack_t*> ss, Userspac
return 0;
}
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigtimedwait.html
ErrorOr<FlatPtr> Process::sys$sigtimedwait(Userspace<const sigset_t*> set, Userspace<siginfo_t*> info, Userspace<const timespec*> timeout)
{
VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this)
REQUIRE_PROMISE(sigaction);
sigset_t set_value;
TRY(copy_from_user(&set_value, set));
Thread::BlockTimeout block_timeout = {};
if (timeout) {
auto timeout_time = TRY(copy_time_from_user(timeout));
block_timeout = Thread::BlockTimeout(false, &timeout_time);
}
siginfo_t info_value = {};
auto block_result = Thread::current()->block<Thread::SignalBlocker>(block_timeout, set_value, info_value);
if (block_result.was_interrupted())
return EINTR;
// We check for an unset signal instead of directly checking for a timeout interruption
// in order to allow polling the pending signals by setting the timeout to 0.
if (info_value.si_signo == SIGINVAL) {
VERIFY(block_result == Thread::BlockResult::InterruptedByTimeout);
return EAGAIN;
}
if (info)
TRY(copy_to_user(info, &info_value));
return info_value.si_signo;
}
}

View file

@ -637,6 +637,7 @@ void Thread::send_signal(u8 signal, [[maybe_unused]] Process* sender)
m_pending_signals |= 1 << (signal - 1);
m_have_any_unmasked_pending_signals.store((pending_signals_for_state() & ~m_signal_mask) != 0, AK::memory_order_release);
m_signal_blocker_set.unblock_all_blockers_whose_conditions_are_met();
if (!has_unmasked_pending_signals())
return;

View file

@ -292,6 +292,7 @@ public:
Queue,
Routing,
Sleep,
Signal,
Wait
};
virtual ~Blocker();
@ -698,6 +699,41 @@ public:
bool m_did_unblock { false };
};
class SignalBlocker final : public Blocker {
public:
explicit SignalBlocker(sigset_t pending_set, siginfo_t& result);
virtual StringView state_string() const override { return "Pending Signal"sv; }
virtual Type blocker_type() const override { return Type::Signal; }
void will_unblock_immediately_without_blocking(UnblockImmediatelyReason) override;
virtual bool setup_blocker() override;
bool check_pending_signals(bool from_add_blocker);
private:
sigset_t m_pending_set { 0 };
siginfo_t& m_result;
bool m_did_unblock { false };
};
class SignalBlockerSet final : public BlockerSet {
public:
void unblock_all_blockers_whose_conditions_are_met()
{
BlockerSet::unblock_all_blockers_whose_conditions_are_met([&](auto& b, void*, bool&) {
VERIFY(b.blocker_type() == Blocker::Type::Signal);
auto& blocker = static_cast<Thread::SignalBlocker&>(b);
return blocker.check_pending_signals(false);
});
}
private:
bool should_add_blocker(Blocker& b, void*) override
{
VERIFY(b.blocker_type() == Blocker::Type::Signal);
auto& blocker = static_cast<Thread::SignalBlocker&>(b);
return !blocker.check_pending_signals(true);
}
};
class WaitBlocker final : public Blocker {
public:
enum class UnblockFlags {
@ -1302,6 +1338,7 @@ private:
u32 m_signal_mask { 0 };
FlatPtr m_alternative_signal_stack { 0 };
FlatPtr m_alternative_signal_stack_size { 0 };
SignalBlockerSet m_signal_blocker_set;
FlatPtr m_kernel_stack_base { 0 };
FlatPtr m_kernel_stack_top { 0 };
OwnPtr<Memory::Region> m_kernel_stack_region;

View file

@ -446,6 +446,48 @@ void Thread::SelectBlocker::was_unblocked(bool did_timeout)
}
}
Thread::SignalBlocker::SignalBlocker(sigset_t pending_set, siginfo_t& result)
: m_pending_set(pending_set)
, m_result(result)
{
}
void Thread::SignalBlocker::will_unblock_immediately_without_blocking(UnblockImmediatelyReason unblock_immediately_reason)
{
if (unblock_immediately_reason != UnblockImmediatelyReason::TimeoutInThePast)
return;
// If the specified timeout is 0 the caller is simply trying to poll once for pending signals,
// so simply calling check_pending_signals should populate the requested information.
check_pending_signals(false);
}
bool Thread::SignalBlocker::setup_blocker()
{
return add_to_blocker_set(thread().m_signal_blocker_set);
}
bool Thread::SignalBlocker::check_pending_signals(bool from_add_blocker)
{
{
SpinlockLocker lock(m_lock);
if (m_did_unblock)
return false;
auto matching_pending_signal = __builtin_ffsl(thread().pending_signals() & m_pending_set);
if (matching_pending_signal == 0)
return false;
m_did_unblock = true;
m_result = {};
m_result.si_signo = matching_pending_signal;
m_result.si_code = 0; // FIXME: How can we determine this?
}
if (!from_add_blocker)
unblock_from_blocker();
return true;
}
Thread::WaitBlockerSet::ProcessBlockInfo::ProcessBlockInfo(NonnullRefPtr<Process>&& process, WaitBlocker::UnblockFlags flags, u8 signal)
: process(move(process))
, flags(flags)

View file

@ -163,6 +163,12 @@ int sigsuspend(const sigset_t* set)
return pselect(0, nullptr, nullptr, nullptr, nullptr, set);
}
int sigtimedwait(sigset_t const* set, siginfo_t* info, struct timespec const* timeout)
{
int rc = syscall(Syscall::SC_sigtimedwait, set, info, timeout);
__RETURN_WITH_ERRNO(rc, rc, -1);
}
const char* sys_signame[] = {
"INVAL",
"HUP",

View file

@ -27,6 +27,7 @@ int sigismember(const sigset_t*, int sig);
int sigprocmask(int how, const sigset_t* set, sigset_t* old_set);
int sigpending(sigset_t*);
int sigsuspend(const sigset_t*);
int sigtimedwait(sigset_t const*, siginfo_t*, struct timespec const*);
int raise(int sig);
int getsignalbyname(const char*);
const char* getsignalname(int);