mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 00:22:57 -05:00
213 lines
7.5 KiB
C++
213 lines
7.5 KiB
C++
#ifdef _WIN32
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <winsock2.h>
|
|
static int InterceptPoll(WSAPOLLFD* descriptors, ULONG count, INT timeout);
|
|
#define WSAPoll InterceptPoll
|
|
#else
|
|
#include <poll.h>
|
|
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 <future>
|
|
#include <source_location>
|
|
|
|
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<void> arrived;
|
|
std::future<void> released;
|
|
int result = 0;
|
|
};
|
|
|
|
static std::atomic<PollGate*> 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<std::uint8_t, 16> Bind(int descriptor) {
|
|
std::array<std::uint8_t, 16> 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<std::uint8_t, 16>& 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<std::uint64_t>(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<int, 2> 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<int>(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<int>(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<void> 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]);
|
|
}
|