mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-29 12:15:39 +00:00
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]>
178 lines
6.3 KiB
C++
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, ¤t) == 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;
|
|
}
|