Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 32 additions & 20 deletions core/libs/prx/libkernel/System/src/Signals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ constexpr int GuestSigunblock = 2;
constexpr int GuestSigsetmask = 3;
constexpr int GuestEintr = 4;
constexpr int GuestEagain = 35;
constexpr int GuestSiuser = 0;
constexpr std::int32_t GuestSiUser = 0x10001;
constexpr std::int32_t GuestSiKernel = 0x10006;
constexpr std::int32_t GuestSiLwp = 0x10007;
struct GuestSiginfo {
std::int32_t signo;
std::int32_t error;
Expand Down Expand Up @@ -115,19 +117,21 @@ struct PendingSignals {
std::mutex lock;
TimedWait::Condition changed;
std::atomic<std::uint32_t> words[4]{};
std::atomic<std::int32_t> codes[MaxSignal + 1]{};
};

PendingSignals& Pending() {
static auto* state = new PendingSignals();
return *state;
}

void MarkPending(int guest) {
void MarkPending(int guest, std::int32_t code) {
auto& pending = Pending();
{
// under the same lock the waits check the set with: without it a signal raised
// between the check and the wait is lost and the wait never returns
std::lock_guard lock(pending.lock);
pending.codes[guest].store(code, std::memory_order_relaxed);
pending.words[SignalWord(guest)].fetch_or(SignalBit(guest), std::memory_order_relaxed);
}
pending.changed.NotifyAll();
Expand All @@ -145,10 +149,11 @@ void DiscardPendingIfIgnored(int guest, const GuestSigaction& action) {
if (action.handler == 1 || (action.handler == 0 && IgnoredByDefault(guest))) TakePending(guest);
}

bool PendingInSet(const GuestSignalSet& set, int* guest) {
bool PendingInSet(const GuestSignalSet& set, int* guest, std::int32_t* code) {
for (int candidate = 1; candidate <= MaxSignal; ++candidate) {
if (!SignalInSet(set, candidate) || !TakePending(candidate)) continue;
*guest = candidate;
*code = Pending().codes[candidate].load(std::memory_order_relaxed);
return true;
}
return false;
Expand Down Expand Up @@ -221,7 +226,7 @@ void Dispatch(int native) {
std::signal(native, Dispatch);
#endif
if (SignalBlocked(guest)) {
MarkPending(guest);
MarkPending(guest, GuestSiLwp);
return;
}
const auto callback = handlers[guest].load();
Expand Down Expand Up @@ -321,26 +326,26 @@ std::optional<std::uint64_t> SignalWaitDeadline(const KernelTimespec& timeout) {
return now + static_cast<std::uint64_t>(timeout.tv_sec) * 1000000000ULL + static_cast<std::uint64_t>(timeout.tv_nsec);
}

int WaitForPendingInSet(const GuestSignalSet& set, bool waitForever, const KernelTimespec* timeout, int* guest) {
int WaitForPendingInSet(const GuestSignalSet& set, bool waitForever, const KernelTimespec* timeout, int* guest, std::int32_t* code) {
auto& pending = Pending();
std::unique_lock lock(pending.lock);
const std::optional<std::uint64_t> deadline = waitForever ? std::nullopt : SignalWaitDeadline(*timeout);
for (;;) {
if (PendingInSet(set, guest)) return 0;
if (PendingInSet(set, guest, code)) return 0;
if (!deadline) {
pending.changed.Wait(lock);
continue;
}
if (pending.changed.WaitUntil(lock, *deadline)) continue;
return PendingInSet(set, guest) ? 0 : GuestEagain;
return PendingInSet(set, guest, code) ? 0 : GuestEagain;
}
}

bool FillSiginfo(GuestSiginfo* info, int guest) {
bool FillSiginfo(GuestSiginfo* info, int guest, std::int32_t code) {
GuestSiginfo local{};
local.signo = guest;
local.code = GuestSiuser;
local.pid = getpid_nid_postfix();
local.code = code;
local.pid = local.code == GuestSiKernel ? 0 : getpid_nid_postfix();
return GuestCopyOut_nid_no_patch(info, &local, sizeof(local));
}

Expand Down Expand Up @@ -391,7 +396,7 @@ void ExpireAlarm(std::chrono::microseconds interval) {
}
if (handler == 1) return;
if (SignalBlocked(GuestSigalrm)) {
MarkPending(GuestSigalrm);
MarkPending(GuestSigalrm, GuestSiKernel);
std::atomic_thread_fence(std::memory_order_seq_cst);
if (SignalBlocked(GuestSigalrm) || !TakePending(GuestSigalrm)) return;
handler = Disposition(GuestSigalrm).handler;
Expand Down Expand Up @@ -661,14 +666,14 @@ int APS5_VABI sigaction_nid_postfix(int guest, const GuestSigaction* action, Gue
int APS5_VABI _sigaction_nid_postfix(int guest, const GuestSigaction* action, GuestSigaction* previous) {
return sigaction_nid_postfix(guest, action, previous);
}
int APS5_VABI raise_nid_postfix(int guest) {
static int RaiseWithCode(int guest, std::int32_t code) {
if (guest == 0) return 0;
if (guest < 1 || guest > MaxSignal) { *__error_nid_postfix() = 22; return -1; }
const GuestSigaction action = Disposition(guest);
if (action.handler > 1 && !Delivered(action)) throw std::runtime_error("raise: SA_SIGINFO handlers are not delivered");
if (action.handler == 1 || (action.handler == 0 && guest != GuestSigcont && IgnoredByDefault(guest))) return 0;
if (SignalBlocked(guest)) {
MarkPending(guest);
MarkPending(guest, code);
return 0;
}
if (action.handler == 0 && IgnoredByDefault(guest)) return 0;
Expand All @@ -684,11 +689,15 @@ int APS5_VABI raise_nid_postfix(int guest) {
DeliverUnblockedPending();
return 0;
}
int APS5_VABI raise_nid_postfix(int guest) {
return RaiseWithCode(guest, GuestSiLwp);
}
int APS5_VABI kill_nid_postfix(int pid, int guest) {
if (guest < 0 || guest > MaxSignal) { *__error_nid_postfix() = 22; return -1; }
const int self = getpid_nid_postfix();
if (pid != self && pid != 0 && pid != -self) { *__error_nid_postfix() = 3; return -1; }
return guest == 0 ? 0 : raise_nid_postfix(guest);
if (guest == 0) return 0;
return RaiseWithCode(guest, GuestSiUser);
}
int APS5_VABI killpg_nid_postfix(int pgrp, int guest) {
if (pgrp < 0) { *__error_nid_postfix() = 22; return -1; }
Expand All @@ -700,7 +709,7 @@ int APS5_VABI pthread_kill_nid_postfix(Pthread thread, int guest) {
if (thread == scePthreadSelf()) return guest == 0 || raise_nid_postfix(guest) == 0 ? 0 : *__error_nid_postfix();
if (!GuestThreadIsLive(thread)) return PosixThread::GUEST_ESRCH;
if (guest == 0) return 0;
if (Disposition(guest).handler != 1) MarkPending(guest);
if (Disposition(guest).handler != 1) MarkPending(guest, GuestSiLwp);
return 0;
}
int APS5_VABI sigaltstack_nid_postfix(const GuestStack* stack, GuestStack* previous) {
Expand Down Expand Up @@ -754,9 +763,10 @@ int APS5_VABI sigtimedwait_nid_postfix(const GuestSignalSet* set, void* info, co
}
}
int guest = 0;
const int result = WaitForPendingInSet(requestedSet, timeout == nullptr, timeout != nullptr ? &requestedTimeout : nullptr, &guest);
std::int32_t code = 0;
const int result = WaitForPendingInSet(requestedSet, timeout == nullptr, timeout != nullptr ? &requestedTimeout : nullptr, &guest, &code);
if (result != 0) { *__error_nid_postfix() = result; return -1; }
if (info != nullptr && !FillSiginfo(static_cast<GuestSiginfo*>(info), guest)) {
if (info != nullptr && !FillSiginfo(static_cast<GuestSiginfo*>(info), guest, code)) {
*__error_nid_postfix() = 14;
return -1;
}
Expand All @@ -769,8 +779,9 @@ int APS5_VABI sigwaitinfo_nid_postfix(const GuestSignalSet* set, void* info) {
if (!GuestCopyIn_nid_no_patch(&requestedSet, set, sizeof(requestedSet))) { *__error_nid_postfix() = 14; return -1; }
if (!SetHasSignal(requestedSet)) { *__error_nid_postfix() = 22; return -1; }
int guest = 0;
if (WaitForPendingInSet(requestedSet, true, nullptr, &guest) != 0) { *__error_nid_postfix() = GuestEagain; return -1; }
if (info != nullptr && !FillSiginfo(static_cast<GuestSiginfo*>(info), guest)) {
std::int32_t code = 0;
if (WaitForPendingInSet(requestedSet, true, nullptr, &guest, &code) != 0) { *__error_nid_postfix() = GuestEagain; return -1; }
if (info != nullptr && !FillSiginfo(static_cast<GuestSiginfo*>(info), guest, code)) {
*__error_nid_postfix() = 14;
return -1;
}
Expand All @@ -790,7 +801,8 @@ int APS5_VABI sigwait_nid_postfix(const GuestSignalSet* set, int* received) {
return 22;
}
int guest = 0;
const int result = WaitForPendingInSet(requestedSet, true, nullptr, &guest);
std::int32_t code = 0;
const int result = WaitForPendingInSet(requestedSet, true, nullptr, &guest, &code);
if (result != 0) {
*__error_nid_postfix() = saved;
return result;
Expand Down
89 changes: 85 additions & 4 deletions core/libs/tests/GuestSigtimedwait.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <algorithm>
#include <array>
#include <atomic>
#include <csetjmp>
#include <chrono>
#include <cstdint>
#include <cstdio>
Expand All @@ -27,7 +28,7 @@ struct GuestSiginfo {
std::int32_t tail;
};
using Handler = void (APS5_VABI *)(int);

extern "C" unsigned int APS5_VABI GuestAlarm_nid_no_patch(unsigned int);
volatile int received = 0;
void APS5_VABI Callback(int value) { received = value; }
static void Require(bool value) { if (!value) std::abort(); }
Expand Down Expand Up @@ -102,6 +103,73 @@ static void CheckTimeoutWithUnrelatedSignals() {
Require(sigtimedwait_nid_postfix(&pipe, nullptr, &poll) == -1 && *__error_nid_postfix() == 35);
Require(_sigprocmask_nid_postfix(3, &previous, nullptr) == 0);
}
constexpr int RoundCount = 300;
struct RoundSender {
Pthread target = nullptr;
std::atomic<int> go{0};
std::atomic<int> sent{0};
};
static void* APS5_VABI SendTwoCodes(void* arg) {
auto& sender = *static_cast<RoundSender*>(arg);
for (int round = 1; round <= RoundCount; ++round) {
while (sender.go.load() != round) std::this_thread::yield();
Require(kill_nid_postfix(getpid_nid_postfix(), 2) == 0);
Require(pthread_kill_nid_postfix(sender.target, 2) == 0);
sender.sent.store(round);
}
return nullptr;
}
static void CheckReportedCodeBelongsToConsumedSignal() {
const GuestSignalSet interrupt{{1u << 1, 0, 0, 0}};
const KernelTimespec patience{5, 0};
const KernelTimespec poll{0, 0};
GuestSignalSet previous{};
Require(_sigprocmask_nid_postfix(1, &interrupt, &previous) == 0);
RoundSender sender;
sender.target = scePthreadSelf();
Pthread thread = nullptr;
Require(scePthreadCreate(&thread, nullptr, SendTwoCodes, &sender, nullptr) == 0);
for (int round = 1; round <= RoundCount; ++round) {
GuestSiginfo first{};
GuestSiginfo second{};
sender.go.store(round);
Require(sigtimedwait_nid_postfix(&interrupt, &first, &patience) == 2);
while (sender.sent.load() != round) std::this_thread::yield();
const bool twice = sigtimedwait_nid_postfix(&interrupt, &second, &poll) == 2;
Require(twice ? first.code != second.code : first.code == 0x10007);
}
Require(scePthreadJoin(thread, nullptr) == 0);
Require(_sigprocmask_nid_postfix(3, &previous, nullptr) == 0);
}
static std::jmp_buf handlerExit;
static void APS5_VABI RaiseSecondFromHandler(int) { Require(raise_nid_postfix(31) == 0); }
static void APS5_VABI RaiseSecondAndLeave(int) {
Require(raise_nid_postfix(31) == 0);
std::longjmp(handlerExit, 1);
}
static int TakeSecondCode(const GuestSignalSet& second) {
const KernelTimespec poll{0, 0};
GuestSiginfo info{};
Require(sigtimedwait_nid_postfix(&second, &info, &poll) == 31);
const int code = info.code;
Require(sigtimedwait_nid_postfix(&second, &info, &poll) == -1);
return code;
}
static void CheckRaiseInsideKillHandlerIsFromThread() {
const GuestSignalSet second{{1u << 30, 0, 0, 0}};
GuestSignalSet previous{};
Require(_sigprocmask_nid_postfix(1, &second, &previous) == 0);
signal_nid_postfix(30, RaiseSecondFromHandler);
Require(kill_nid_postfix(getpid_nid_postfix(), 30) == 0);
Require(TakeSecondCode(second) == 0x10007);
signal_nid_postfix(30, RaiseSecondAndLeave);
if (setjmp(handlerExit) == 0) kill_nid_postfix(getpid_nid_postfix(), 30);
Require(TakeSecondCode(second) == 0x10007);
Require(raise_nid_postfix(31) == 0);
Require(TakeSecondCode(second) == 0x10007);
signal_nid_postfix(30, nullptr);
Require(_sigprocmask_nid_postfix(3, &previous, nullptr) == 0);
}
static void CheckShortTimeouts() {
const GuestSignalSet pipe{{1u << 12, 0, 0, 0}};
for (const std::int64_t nanos : {100000, 1000000}) {
Expand Down Expand Up @@ -165,15 +233,26 @@ int main() {
Require(sigwait_nid_postfix(&interrupt, &taken) == 0 && taken == 2 && received == 0);
Require(raise_nid_postfix(2) == 0 && received == 0);
Require(sigtimedwait_nid_postfix(&interrupt, &info, &poll) == 2 && received == 0);
Require(info.signo == 2 && info.error == 0 && info.code == 0 && info.pid == getpid_nid_postfix() && info.uid == 0);
Require(info.signo == 2 && info.error == 0 && info.code == 0x10007 && info.pid == getpid_nid_postfix() && info.uid == 0);
Require(info.status == 0 && info.address == 0 && info.value == 0 && info.band == 0);
Require(raise_nid_postfix(2) == 0 && received == 0);
Require(sigwaitinfo_nid_postfix(&interrupt, &info) == 2 && received == 0);
Require(info.signo == 2 && info.code == 0 && info.pid == getpid_nid_postfix());
Require(info.signo == 2 && info.code == 0x10007 && info.pid == getpid_nid_postfix());
Require(kill_nid_postfix(getpid_nid_postfix(), 2) == 0 && received == 0);
Require(sigwaitinfo_nid_postfix(&interrupt, &info) == 2 && received == 0);
Require(info.signo == 2 && info.code == 0x10001 && info.pid == getpid_nid_postfix() && info.uid == 0);
Require(raise_nid_postfix(2) == 0 && received == 0);
Require(sigwaitinfo_nid_postfix(&interrupt, nullptr) == 2 && received == 0);
Require(raise_nid_postfix(2) == 0 && received == 0);
Require(sigtimedwait_nid_postfix(&interrupt, nullptr, nullptr) == 2 && received == 0);
const GuestSignalSet alarmSet{{1u << 13, 0, 0, 0}};
const KernelTimespec alarmWait{3, 0};
GuestSignalSet beforeAlarm{};
Require(_sigprocmask_nid_postfix(1, &alarmSet, &beforeAlarm) == 0);
Require(GuestAlarm_nid_no_patch(1) == 0);
Require(sigtimedwait_nid_postfix(&alarmSet, &info, &alarmWait) == 14);
Require(info.signo == 14 && info.code == 0x10006 && info.pid == 0 && info.uid == 0);
Require(_sigprocmask_nid_postfix(3, &beforeAlarm, nullptr) == 0);
Require(raise_nid_postfix(2) == 0 && received == 0);
*__error_nid_postfix() = 75;
Require(sigwait_nid_postfix(&interrupt, reinterpret_cast<int*>(0x10)) == 14 && *__error_nid_postfix() == 75);
Expand Down Expand Up @@ -203,7 +282,7 @@ int main() {
Pthread senderThread = StartSender(sender);
Require(sigwaitinfo_nid_postfix(&pipe, &info) == 13);
JoinSender(senderThread, sender);
Require(info.signo == 13 && info.code == 0 && info.pid == getpid_nid_postfix());
Require(info.signo == 13 && info.code == 0x10007 && info.pid == getpid_nid_postfix());
senderThread = StartSender(sender);
Require(sigtimedwait_nid_postfix(&pipe, nullptr, nullptr) == 13);
JoinSender(senderThread, sender);
Expand Down Expand Up @@ -232,5 +311,7 @@ int main() {
}
Require(_sigprocmask_nid_postfix(3, &previous, nullptr) == 0);
CheckTimeoutWithUnrelatedSignals();
CheckReportedCodeBelongsToConsumedSignal();
CheckRaiseInsideKillHandlerIsFromThread();
CheckShortTimeouts();
}
Loading
Loading