Files
AnyPS5/core/libs/tests/GuestKernelSysPselect.cpp
T
harisibrar1andClaude Opus 5.5 c7cd45c0da fix(libkernel): return EINTR from pselect when its mask unblocks a pending signal
FreeBSD's kern_pselect installs the mask and kern_select sleeps with
PCATCH, so a pending signal that the new mask unblocks makes the call
return EINTR once the descriptors have been scanned. Here the handler ran
inside the mask swap and select then waited the full timeout, or forever
with no timeout. The swap now reports whether a handler ran, and pselect
then scans the descriptors once without waiting: a ready descriptor, a
zero timeout or an invalid argument keep select's answer, and otherwise
the call fails with EINTR and leaves the descriptor sets unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 00:39:02 -04:00

178 lines
6.3 KiB
C++

#include "SceTypes.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include <array>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <source_location>
extern "C" {
int* APS5_VABI __error_nid_postfix();
int APS5_VABI pipe_nid_postfix(int* descriptors);
int APS5_VABI close_nid_postfix(int descriptor);
std::int64_t APS5_VABI write_nid_postfix(int descriptor, const char* buffer, std::int64_t size);
int APS5_VABI sigprocmask_nid_postfix(int how, const void* set, void* previousSet);
int APS5_VABI pselect_nid_postfix(int nfds, void* readfds, void* writefds, void* exceptfds, const void* timeout, const void* sigmask);
using Handler = void (APS5_VABI *)(int);
Handler APS5_VABI signal_nid_postfix(int guest, Handler handler);
int APS5_VABI raise_nid_postfix(int guest);
}
namespace {
constexpr int Eintr = 4;
constexpr int Einval = 22;
constexpr int SigBlock = 1;
constexpr int SigSetmask = 3;
constexpr int SigUsr1 = 30;
struct Timespec {
std::int64_t seconds;
std::int64_t nanoseconds;
};
struct SignalSet {
std::uint32_t bits[4]{};
};
struct DescriptorSet {
std::array<std::uint64_t, 1024 / 64> words{};
void Add(int descriptor) { words[descriptor / 64] |= std::uint64_t{1} << (descriptor % 64); }
bool Has(int descriptor) const { return ((words[descriptor / 64] >> (descriptor % 64)) & 1u) != 0; }
};
void Require(bool condition, std::source_location location = std::source_location::current()) {
if (condition) return;
std::fprintf(stderr, "check failed at line %u\n", static_cast<unsigned>(location.line()));
std::exit(1);
}
int Error() {
return *__error_nid_postfix();
}
SignalSet CurrentMask() {
SignalSet current{};
Require(sigprocmask_nid_postfix(SigBlock, nullptr, &current) == 0);
return current;
}
bool Same(const SignalSet& left, const SignalSet& right) {
return std::memcmp(left.bits, right.bits, sizeof(left.bits)) == 0;
}
int caught = 0;
void APS5_VABI Count(int) {
++caught;
}
void RaiseBlocked(const SignalSet& blocked) {
Require(sigprocmask_nid_postfix(SigBlock, &blocked, nullptr) == 0);
Require(raise_nid_postfix(SigUsr1) == 0 && caught == 0);
}
}
int main() {
int descriptors[2]{-1, -1};
Require(pipe_nid_postfix(descriptors) == 0);
const int reader = descriptors[0];
const int writer = descriptors[1];
SignalSet blocked{};
blocked.bits[(SigUsr1 - 1) / 32] |= 1u << ((SigUsr1 - 1) % 32);
const SignalSet before = CurrentMask();
{
DescriptorSet read{};
read.Add(reader);
const Timespec limit{0, 200000000};
const auto start = std::chrono::steady_clock::now();
Require(pselect_nid_postfix(reader + 1, read.words.data(), nullptr, nullptr, &limit, &blocked) == 0);
Require(std::chrono::steady_clock::now() - start >= std::chrono::milliseconds(150));
Require(!read.Has(reader));
Require(Same(CurrentMask(), before));
}
Require(write_nid_postfix(writer, "x", 1) == 1);
{
DescriptorSet read{};
read.Add(reader);
const Timespec limit{0, 999999999};
Require(pselect_nid_postfix(reader + 1, read.words.data(), nullptr, nullptr, &limit, nullptr) == 1);
Require(read.Has(reader));
}
{
DescriptorSet read{};
read.Add(reader);
Require(pselect_nid_postfix(reader + 1, read.words.data(), nullptr, nullptr, nullptr, &blocked) == 1);
Require(read.Has(reader));
Require(Same(CurrentMask(), before));
}
{
const Timespec invalid{0, 1000000000};
Require(pselect_nid_postfix(0, nullptr, nullptr, nullptr, &invalid, &blocked) == -1 && Error() == Einval);
Require(Same(CurrentMask(), before));
const Timespec zero{0, 0};
Require(pselect_nid_postfix(-1, nullptr, nullptr, nullptr, &zero, nullptr) == -1 && Error() == Einval);
}
Require(signal_nid_postfix(SigUsr1, Count) != reinterpret_cast<Handler>(static_cast<std::uintptr_t>(-1)));
const SignalSet empty{};
{
RaiseBlocked(blocked);
const SignalSet withUsr1 = CurrentMask();
DescriptorSet read{};
read.Add(writer);
const DescriptorSet original = read;
const Timespec limit{0, 300000000};
const auto start = std::chrono::steady_clock::now();
Require(pselect_nid_postfix(writer + 1, read.words.data(), nullptr, nullptr, &limit, &empty) == -1 && Error() == Eintr);
Require(std::chrono::steady_clock::now() - start < std::chrono::milliseconds(150));
Require(caught == 1);
Require(read.words == original.words);
Require(Same(CurrentMask(), withUsr1));
caught = 0;
}
{
RaiseBlocked(blocked);
Require(pselect_nid_postfix(0, nullptr, nullptr, nullptr, nullptr, &empty) == -1 && Error() == Eintr && caught == 1);
caught = 0;
}
{
RaiseBlocked(blocked);
DescriptorSet read{};
read.Add(reader);
const Timespec limit{0, 300000000};
Require(pselect_nid_postfix(reader + 1, read.words.data(), nullptr, nullptr, &limit, &empty) == 1 && caught == 1);
Require(read.Has(reader));
caught = 0;
}
{
RaiseBlocked(blocked);
const Timespec zero{0, 0};
DescriptorSet read{};
read.Add(writer);
Require(pselect_nid_postfix(writer + 1, read.words.data(), nullptr, nullptr, &zero, &empty) == 0 && caught == 1);
caught = 0;
}
{
RaiseBlocked(blocked);
const Timespec invalid{0, 1000000000};
Require(pselect_nid_postfix(0, nullptr, nullptr, nullptr, &invalid, &empty) == -1 && Error() == Einval && caught == 1);
caught = 0;
}
{
RaiseBlocked(blocked);
const Timespec limit{0, 200000000};
const auto start = std::chrono::steady_clock::now();
Require(pselect_nid_postfix(0, nullptr, nullptr, nullptr, &limit, &blocked) == 0 && caught == 0);
Require(std::chrono::steady_clock::now() - start >= std::chrono::milliseconds(150));
Require(sigprocmask_nid_postfix(SigSetmask, &empty, nullptr) == 0 && caught == 1);
caught = 0;
}
Require(sigprocmask_nid_postfix(SigSetmask, &before, nullptr) == 0);
Require(close_nid_postfix(reader) == 0 && close_nid_postfix(writer) == 0);
std::puts("pselect passed");
return 0;
}