fix(libkernel): report /dev/urandom descriptors ready in select
select passed every descriptor it was given to the libSceNet socket poller and aborted on any it did not know. A title that selects on its /dev/urandom descriptor (Dungeons, PPSA16064, aborts right after opening it) stopped with "select on descriptors that are not libSceNet sockets". On FreeBSD the random device is always readable and writable, so these descriptors are now answered without the poller, and a ready one makes the socket poll return at once. Claude-Session: https://claude.ai/code/session_012SLQbhAiexxW6TtjYToW8a
This commit is contained in:
parent
03a84d02a9
commit
1f50b91280
|
|
@ -2,6 +2,7 @@
|
|||
#include <cstddef>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libkernel/File/include/RandomDevice.hpp"
|
||||
#include "prx/libkernel/Socket/include/SocketPoll.hpp"
|
||||
#include "prx/libkernel/Socket/include/SocketSelectDeadline.hpp"
|
||||
|
||||
|
|
@ -59,13 +60,19 @@ int APS5_VABI select_nid_postfix(int nfds, void* readfds, void* writefds, void*
|
|||
return -1;
|
||||
}
|
||||
std::vector<KernelSocketPoll::Entry> entries;
|
||||
std::vector<KernelSocketPoll::Entry> devices;
|
||||
for (int descriptor = 0; descriptor < nfds; ++descriptor) {
|
||||
short events = 0;
|
||||
if (DescriptorSet(readfds, descriptor)) events |= KernelSocketPoll::Readable;
|
||||
if (DescriptorSet(writefds, descriptor)) events |= KernelSocketPoll::Writable;
|
||||
if (DescriptorSet(exceptfds, descriptor)) events |= KernelSocketPoll::Urgent;
|
||||
if (events != 0) entries.push_back({descriptor, events, 0});
|
||||
if (events == 0) continue;
|
||||
if (File::IsRandomDevice(descriptor)) devices.push_back({descriptor, events, static_cast<short>(events & (KernelSocketPoll::Readable | KernelSocketPoll::Writable))});
|
||||
else entries.push_back({descriptor, events, 0});
|
||||
}
|
||||
const auto sockets = entries.size();
|
||||
const bool devicesReady = std::any_of(devices.begin(), devices.end(), [](const auto& entry) { return entry.revents != 0; });
|
||||
entries.insert(entries.end(), devices.begin(), devices.end());
|
||||
const auto start = std::chrono::steady_clock::now();
|
||||
const auto deadline = limit == nullptr ? std::chrono::steady_clock::time_point::max()
|
||||
: KernelSocketSelect::DeadlineAfter(start, limit->seconds, limit->microseconds);
|
||||
|
|
@ -76,34 +83,35 @@ int APS5_VABI select_nid_postfix(int nfds, void* readfds, void* writefds, void*
|
|||
const auto roundedMilliseconds = remaining > 0 ? remaining / 1000 + (remaining % 1000 != 0) : 0;
|
||||
waitMilliseconds = static_cast<int>(std::clamp<std::int64_t>(roundedMilliseconds, 0, 100));
|
||||
}
|
||||
if (!entries.empty()) {
|
||||
if (devicesReady) waitMilliseconds = 0;
|
||||
if (sockets != 0) {
|
||||
const auto poller = g_socketPoller.load();
|
||||
if (poller == nullptr) NotImplemented_nid_no_patch("select on descriptors that are not libSceNet sockets");
|
||||
for (auto& entry : entries) entry.revents = 0;
|
||||
const int result = poller(entries.data(), static_cast<int>(entries.size()), waitMilliseconds);
|
||||
for (std::size_t index = 0; index < sockets; ++index) entries[index].revents = 0;
|
||||
const int result = poller(entries.data(), static_cast<int>(sockets), waitMilliseconds);
|
||||
if (result < 0) {
|
||||
*__error_nid_postfix() = -result;
|
||||
return -1;
|
||||
}
|
||||
int ready = 0;
|
||||
for (const auto& entry : entries) {
|
||||
if ((entry.revents & KernelSocketPoll::Unknown) != 0) NotImplemented_nid_no_patch("select on descriptors that are not libSceNet sockets");
|
||||
const bool readable = (entry.events & KernelSocketPoll::Readable) != 0 && (entry.revents & (KernelSocketPoll::Readable | KernelSocketPoll::HangUp | KernelSocketPoll::Error)) != 0;
|
||||
const bool writable = (entry.events & KernelSocketPoll::Writable) != 0 && (entry.revents & (KernelSocketPoll::Writable | KernelSocketPoll::Error)) != 0;
|
||||
const bool urgent = (entry.events & KernelSocketPoll::Urgent) != 0 && (entry.revents & KernelSocketPoll::Urgent) != 0;
|
||||
ready += static_cast<int>(readable) + static_cast<int>(writable) + static_cast<int>(urgent);
|
||||
}
|
||||
if (ready > 0) {
|
||||
for (const auto& entry : entries) {
|
||||
if ((entry.revents & (KernelSocketPoll::Readable | KernelSocketPoll::HangUp | KernelSocketPoll::Error)) == 0) ClearDescriptor(readfds, entry.descriptor);
|
||||
if ((entry.revents & (KernelSocketPoll::Writable | KernelSocketPoll::Error)) == 0) ClearDescriptor(writefds, entry.descriptor);
|
||||
if ((entry.revents & KernelSocketPoll::Urgent) == 0) ClearDescriptor(exceptfds, entry.descriptor);
|
||||
}
|
||||
return ready;
|
||||
}
|
||||
} else if (waitMilliseconds > 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(waitMilliseconds));
|
||||
}
|
||||
int ready = 0;
|
||||
for (const auto& entry : entries) {
|
||||
if ((entry.revents & KernelSocketPoll::Unknown) != 0) NotImplemented_nid_no_patch("select on descriptors that are not libSceNet sockets");
|
||||
const bool readable = (entry.events & KernelSocketPoll::Readable) != 0 && (entry.revents & (KernelSocketPoll::Readable | KernelSocketPoll::HangUp | KernelSocketPoll::Error)) != 0;
|
||||
const bool writable = (entry.events & KernelSocketPoll::Writable) != 0 && (entry.revents & (KernelSocketPoll::Writable | KernelSocketPoll::Error)) != 0;
|
||||
const bool urgent = (entry.events & KernelSocketPoll::Urgent) != 0 && (entry.revents & KernelSocketPoll::Urgent) != 0;
|
||||
ready += static_cast<int>(readable) + static_cast<int>(writable) + static_cast<int>(urgent);
|
||||
}
|
||||
if (ready > 0) {
|
||||
for (const auto& entry : entries) {
|
||||
if ((entry.revents & (KernelSocketPoll::Readable | KernelSocketPoll::HangUp | KernelSocketPoll::Error)) == 0) ClearDescriptor(readfds, entry.descriptor);
|
||||
if ((entry.revents & (KernelSocketPoll::Writable | KernelSocketPoll::Error)) == 0) ClearDescriptor(writefds, entry.descriptor);
|
||||
if ((entry.revents & KernelSocketPoll::Urgent) == 0) ClearDescriptor(exceptfds, entry.descriptor);
|
||||
}
|
||||
return ready;
|
||||
}
|
||||
if (limit != nullptr && std::chrono::steady_clock::now() >= deadline) {
|
||||
for (const auto& entry : entries) {
|
||||
ClearDescriptor(readfds, entry.descriptor);
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ int APS5_VABI sceNetResolverDestroy(int);
|
|||
int APS5_VABI sceNetResolverGetError(int, int*);
|
||||
int APS5_VABI sceNetCtlGetState(int*);
|
||||
int APS5_VABI select_nid_postfix(int, void*, void*, void*, const void*);
|
||||
int APS5_VABI open_nid_postfix(const char*, int, int);
|
||||
int APS5_VABI close_nid_postfix(int);
|
||||
int* APS5_VABI __error_nid_postfix();
|
||||
int APS5_VABI sceNetInetPton(int, const char*, void*);
|
||||
const char* APS5_VABI sceNetInetNtop(int, const void*, char*, std::uint32_t);
|
||||
|
|
@ -422,6 +424,18 @@ int main() {
|
|||
const std::int64_t bad_timeout[2]{0, 1000000};
|
||||
Require(select_nid_postfix(udp_receiver + 1, readable, nullptr, nullptr, bad_timeout) == -1 && *__error_nid_postfix() == 22);
|
||||
Require(select_nid_postfix(1025, nullptr, nullptr, nullptr, poll_now) == -1 && *__error_nid_postfix() == 22);
|
||||
const int random = open_nid_postfix("/dev/urandom", 0, 0);
|
||||
Require(random >= 0 && random < 1024);
|
||||
std::uint64_t random_readable[16]{};
|
||||
std::uint64_t random_urgent[16]{};
|
||||
random_readable[random / 64] |= std::uint64_t{1} << (random % 64);
|
||||
random_readable[udp_receiver / 64] |= std::uint64_t{1} << (udp_receiver % 64);
|
||||
random_urgent[random / 64] |= std::uint64_t{1} << (random % 64);
|
||||
Require(select_nid_postfix(std::max(random, udp_receiver) + 1, random_readable, nullptr, random_urgent, nullptr) == 1);
|
||||
Require(((random_readable[random / 64] >> (random % 64)) & 1u) != 0);
|
||||
Require(((random_readable[udp_receiver / 64] >> (udp_receiver % 64)) & 1u) == 0);
|
||||
Require(random_urgent[random / 64] == 0);
|
||||
Require(close_nid_postfix(random) == 0);
|
||||
CheckMessages(udp_receiver, udp_sender, address);
|
||||
Require(sceNetSocketClose(udp_sender) == 0);
|
||||
Require(sceNetSocketClose(udp_receiver) == 0);
|
||||
|
|
|
|||
Loading…
Reference in New Issue