diff --git a/core/libs/prx/libkernel/Socket/src/Socket.cpp b/core/libs/prx/libkernel/Socket/src/Socket.cpp index 95da4bf78..63db392e0 100644 --- a/core/libs/prx/libkernel/Socket/src/Socket.cpp +++ b/core/libs/prx/libkernel/Socket/src/Socket.cpp @@ -2,6 +2,7 @@ #include #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 entries; + std::vector 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(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(std::clamp(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(entries.size()), waitMilliseconds); + for (std::size_t index = 0; index < sockets; ++index) entries[index].revents = 0; + const int result = poller(entries.data(), static_cast(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(readable) + static_cast(writable) + static_cast(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(readable) + static_cast(writable) + static_cast(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); diff --git a/core/libs/tests/GuestSceNet.cpp b/core/libs/tests/GuestSceNet.cpp index c4b8c8004..42e336604 100644 --- a/core/libs/tests/GuestSceNet.cpp +++ b/core/libs/tests/GuestSceNet.cpp @@ -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);