#ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include static int InterceptPoll(WSAPOLLFD* descriptors, ULONG count, INT timeout); #define WSAPoll InterceptPoll #else #include static int InterceptPoll(pollfd* descriptors, nfds_t count, int timeout); #define poll InterceptPoll #endif #include "../prx/libSceNet/Export.cpp" #ifdef _WIN32 #undef WSAPoll #else #undef poll #endif #include #include static void Require(bool value, const std::source_location location = std::source_location::current()) { if (value) return; std::fprintf(stderr, "requirement failed at %s:%u\n", location.file_name(), location.line()); std::abort(); } struct PollGate { std::promise arrived; std::future released; int result = 0; }; static std::atomic pollGate{nullptr}; #ifdef _WIN32 static int InterceptPoll(WSAPOLLFD* descriptors, ULONG count, INT timeout) { const int result = ::WSAPoll(descriptors, count, timeout); #else static int InterceptPoll(pollfd* descriptors, nfds_t count, int timeout) { const int result = ::poll(descriptors, count, timeout); #endif if (auto* gate = pollGate.exchange(nullptr)) { gate->result = result; gate->arrived.set_value(); gate->released.wait(); } return result; } class ReuseSlot { public: #ifndef _WIN32 ReuseSlot() : descriptor(::open("/dev/null", O_RDONLY | O_CLOEXEC)) { Require(descriptor >= 0); } ~ReuseSlot() { if (descriptor >= 0) ::close(descriptor); } void Release() { Require(::close(descriptor) == 0); descriptor = -1; } private: int descriptor; #else void Release() {} #endif }; static constexpr std::uint64_t OldData = 0x123456789abcdef0; static constexpr std::uint64_t NewData = 0xfedcba9876543210; static int Socket() { const int descriptor = sceNetSocket("epoll-lifetime", 2, 2, 17); Require(descriptor >= 0); return descriptor; } static std::array Bind(int descriptor) { std::array address{16, 2, 0, 0, 127, 0, 0, 1}; Require(sceNetBind_nid_postfix(descriptor, address.data(), address.size()) == 0); std::uint32_t size = address.size(); Require(sceNetGetsockname(descriptor, address.data(), &size) == 0 && size == address.size()); return address; } static void Send(int sender, const std::array& address) { const char payload[] = "epoll-lifetime"; Require(sceNetSendto(sender, payload, sizeof(payload), 0, address.data(), address.size()) == sizeof(payload)); } static void Register(int epoll, int descriptor, int operation, std::uint64_t data, std::uint32_t events = 1) { NetEpollEvent event{}; event.events = events; event.ident = 0xaaaaaaaaaaaaaaaa; event.reserved = 0x55555555; event.data.u64 = data; Require(sceNetEpollControl(epoll, operation, descriptor, &event) == 0); } static void CheckEvent(const NetEpollEvent& event, int descriptor, std::uint64_t data, std::uint32_t events = 1) { Require(event.ident == static_cast(descriptor)); Require(event.data.u64 == data && event.events == events && event.reserved == 0); } static void CheckReuse() { ReuseSlot slot; const int original = Socket(); const auto retained = g_socks.at(original).native; const int sender = Socket(); std::array epolls{sceNetEpollCreate("reuse-a", 0), sceNetEpollCreate("reuse-b", 0)}; for (const int epoll : epolls) { Require(epoll >= 0); Register(epoll, original, 1, OldData); } Require(sceNetSocketClose(original) == 0); slot.Release(); const int replacement = Socket(); Require(replacement == original); Send(sender, Bind(replacement)); for (const int epoll : epolls) { NetEpollEvent event{}; Require(sceNetEpollWait(epoll, &event, 1, 0) == 0); Register(epoll, replacement, 1, NewData); Require(sceNetEpollWait(epoll, &event, 1, 1000000) == 1); CheckEvent(event, replacement, NewData); Require(sceNetEpollDestroy(epoll) == 0); } Require(sceNetSocketClose(replacement) == 0 && sceNetSocketClose(sender) == 0); } static void CheckControl() { const int descriptor = Socket(); const int epoll = sceNetEpollCreate("control", 0); Require(epoll >= 0); Require(sceNetEpollControl(epoll, 3, descriptor, nullptr) == static_cast(0x80410102)); Require(*sceNetErrnoLoc() == 2); Register(epoll, descriptor, 1, OldData); Require(sceNetSocketClose(descriptor) == 0); NetEpollEvent event{}; event.events = 1; for (const int closed : {descriptor, -1}) { for (const int operation : {3, 1, 2}) { Require(sceNetEpollControl(epoll, operation, closed, operation == 3 ? nullptr : &event) == static_cast(0x80410109)); Require(*sceNetErrnoLoc() == 9); } } Require(sceNetEpollDestroy(epoll) == 0); } struct WaitResult { int count = -1; int error = 0; NetEpollEvent event{}; }; static void CheckWait(const std::string& operation) { ReuseSlot slot; int receiver = Socket(); const int sender = Socket(); Send(sender, Bind(receiver)); const int epoll = sceNetEpollCreate("inflight", 0); Require(epoll >= 0); Register(epoll, receiver, 1, OldData); std::promise release; PollGate gate{{}, release.get_future()}; auto arrived = gate.arrived.get_future(); pollGate.store(&gate); auto waiter = std::async(std::launch::async, [epoll] { WaitResult result; result.count = sceNetEpollWait(epoll, &result.event, 1, 200000); result.error = *sceNetErrnoLoc(); return result; }); Require(arrived.wait_for(std::chrono::seconds(2)) == std::future_status::ready); Require(gate.result == 1); if (operation == "close") { const int original = receiver; Require(sceNetSocketClose(original) == 0); slot.Release(); receiver = Socket(); Require(receiver == original); Send(sender, Bind(receiver)); Register(epoll, receiver, 1, NewData); } else if (operation == "delete" || operation == "replace") { Require(sceNetEpollControl(epoll, 3, receiver, nullptr) == 0); if (operation == "replace") Register(epoll, receiver, 1, NewData); } else if (operation == "modify") { Register(epoll, receiver, 2, NewData, 2); } else if (operation == "abort") { Require(sceNetEpollAbort(epoll, 0) == 0); } else { Require(operation == "destroy"); Require(sceNetEpollDestroy(epoll) == 0); } release.set_value(); Require(waiter.wait_for(std::chrono::seconds(2)) == std::future_status::ready); const auto result = waiter.get(); if (operation == "delete") { Require(result.count == 0); } else if (operation == "abort" || operation == "destroy") { Require(result.count < 0 && result.error == (operation == "abort" ? 53 : 9)); } else { Require(result.count == 1); CheckEvent(result.event, receiver, NewData, operation == "modify" ? 2 : 1); } if (operation != "destroy") Require(sceNetEpollDestroy(epoll) == 0); Require(sceNetSocketClose(receiver) == 0 && sceNetSocketClose(sender) == 0); } int main(int argc, char** argv) { Require(argc == 2); Require(sceNetInit_nid_postfix() == 0); for (int iteration = 0; iteration != 3; ++iteration) { if (std::string(argv[1]) == "reuse") CheckReuse(); else if (std::string(argv[1]) == "control") CheckControl(); else CheckWait(argv[1]); } std::printf("epoll lifetime %s: 3 rounds passed\n", argv[1]); }