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:
parent
13d98999b7
commit
762e047ec9
8 changed files with 120 additions and 0 deletions
|
@ -176,6 +176,7 @@ enum class NeedsBigProcessLock {
|
||||||
S(sigpending, NeedsBigProcessLock::Yes) \
|
S(sigpending, NeedsBigProcessLock::Yes) \
|
||||||
S(sigprocmask, NeedsBigProcessLock::Yes) \
|
S(sigprocmask, NeedsBigProcessLock::Yes) \
|
||||||
S(sigreturn, NeedsBigProcessLock::Yes) \
|
S(sigreturn, NeedsBigProcessLock::Yes) \
|
||||||
|
S(sigtimedwait, NeedsBigProcessLock::Yes) \
|
||||||
S(socket, NeedsBigProcessLock::Yes) \
|
S(socket, NeedsBigProcessLock::Yes) \
|
||||||
S(socketpair, NeedsBigProcessLock::Yes) \
|
S(socketpair, NeedsBigProcessLock::Yes) \
|
||||||
S(stat, NeedsBigProcessLock::Yes) \
|
S(stat, NeedsBigProcessLock::Yes) \
|
||||||
|
|
|
@ -338,6 +338,7 @@ public:
|
||||||
ErrorOr<FlatPtr> sys$sigaltstack(Userspace<const stack_t*> ss, Userspace<stack_t*> old_ss);
|
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$sigprocmask(int how, Userspace<const sigset_t*> set, Userspace<sigset_t*> old_set);
|
||||||
ErrorOr<FlatPtr> sys$sigpending(Userspace<sigset_t*>);
|
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$getgroups(size_t, Userspace<gid_t*>);
|
||||||
ErrorOr<FlatPtr> sys$setgroups(size_t, Userspace<const gid_t*>);
|
ErrorOr<FlatPtr> sys$setgroups(size_t, Userspace<const gid_t*>);
|
||||||
ErrorOr<FlatPtr> sys$pipe(int pipefd[2], int flags);
|
ErrorOr<FlatPtr> sys$pipe(int pipefd[2], int flags);
|
||||||
|
|
|
@ -307,4 +307,35 @@ ErrorOr<FlatPtr> Process::sys$sigaltstack(Userspace<const stack_t*> ss, Userspac
|
||||||
return 0;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -637,6 +637,7 @@ void Thread::send_signal(u8 signal, [[maybe_unused]] Process* sender)
|
||||||
|
|
||||||
m_pending_signals |= 1 << (signal - 1);
|
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_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())
|
if (!has_unmasked_pending_signals())
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -292,6 +292,7 @@ public:
|
||||||
Queue,
|
Queue,
|
||||||
Routing,
|
Routing,
|
||||||
Sleep,
|
Sleep,
|
||||||
|
Signal,
|
||||||
Wait
|
Wait
|
||||||
};
|
};
|
||||||
virtual ~Blocker();
|
virtual ~Blocker();
|
||||||
|
@ -698,6 +699,41 @@ public:
|
||||||
bool m_did_unblock { false };
|
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 {
|
class WaitBlocker final : public Blocker {
|
||||||
public:
|
public:
|
||||||
enum class UnblockFlags {
|
enum class UnblockFlags {
|
||||||
|
@ -1302,6 +1338,7 @@ private:
|
||||||
u32 m_signal_mask { 0 };
|
u32 m_signal_mask { 0 };
|
||||||
FlatPtr m_alternative_signal_stack { 0 };
|
FlatPtr m_alternative_signal_stack { 0 };
|
||||||
FlatPtr m_alternative_signal_stack_size { 0 };
|
FlatPtr m_alternative_signal_stack_size { 0 };
|
||||||
|
SignalBlockerSet m_signal_blocker_set;
|
||||||
FlatPtr m_kernel_stack_base { 0 };
|
FlatPtr m_kernel_stack_base { 0 };
|
||||||
FlatPtr m_kernel_stack_top { 0 };
|
FlatPtr m_kernel_stack_top { 0 };
|
||||||
OwnPtr<Memory::Region> m_kernel_stack_region;
|
OwnPtr<Memory::Region> m_kernel_stack_region;
|
||||||
|
|
|
@ -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)
|
Thread::WaitBlockerSet::ProcessBlockInfo::ProcessBlockInfo(NonnullRefPtr<Process>&& process, WaitBlocker::UnblockFlags flags, u8 signal)
|
||||||
: process(move(process))
|
: process(move(process))
|
||||||
, flags(flags)
|
, flags(flags)
|
||||||
|
|
|
@ -163,6 +163,12 @@ int sigsuspend(const sigset_t* set)
|
||||||
return pselect(0, nullptr, nullptr, nullptr, nullptr, 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[] = {
|
const char* sys_signame[] = {
|
||||||
"INVAL",
|
"INVAL",
|
||||||
"HUP",
|
"HUP",
|
||||||
|
|
|
@ -27,6 +27,7 @@ int sigismember(const sigset_t*, int sig);
|
||||||
int sigprocmask(int how, const sigset_t* set, sigset_t* old_set);
|
int sigprocmask(int how, const sigset_t* set, sigset_t* old_set);
|
||||||
int sigpending(sigset_t*);
|
int sigpending(sigset_t*);
|
||||||
int sigsuspend(const sigset_t*);
|
int sigsuspend(const sigset_t*);
|
||||||
|
int sigtimedwait(sigset_t const*, siginfo_t*, struct timespec const*);
|
||||||
int raise(int sig);
|
int raise(int sig);
|
||||||
int getsignalbyname(const char*);
|
int getsignalbyname(const char*);
|
||||||
const char* getsignalname(int);
|
const char* getsignalname(int);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue