mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-18 00:40:30 +02:00
Socket waits now block in select() on the socket plus a wake or cancel socket (Common/Net/Cancel), so a stop or cancel ends them at once. - HTTP: cancel flags become net::CancelToken; Connect waits once on all addresses, receives wait instead of spinning, no DNS retry sleep. - Adhoc: the friend finder and adhoc server block on their sockets with a wake socket and a deadline; matching runs on a CoreTiming tick instead of two threads (savestate section bumped). - sceNetInet select/poll check without blocking and park the HLE thread, re-checked from a CoreTiming event until ready or the emulated timeout. - Vendor the aemu_postoffice client (was a submodule) with no sleeps: waits on the socket, close via shutdown and an eventcount, cancelable connects, and lock-free session slot claims. - PortManager waits without a timeout; PSP_InitWait replaces the headless boot poll. - TimeUtil: sleep_us/sleep_ms only outside the core; sleep_precise, sleep_random, yield and the Windows timer period are gone. - Unit test for the socket waits.
80 lines
2.5 KiB
C++
80 lines
2.5 KiB
C++
// Socket waits: a wake or a cancel ends one at once, data ends one, and a timeout still does.
|
|
|
|
#include "Common/Net/SocketCompat.h"
|
|
|
|
#include <thread>
|
|
|
|
#include "Common/Net/Cancel.h"
|
|
#include "Common/Net/HTTPClient.h"
|
|
#include "Common/Net/Resolve.h"
|
|
#include "Common/TimeUtil.h"
|
|
|
|
#include "UnitTest.h"
|
|
|
|
static uintptr_t LoopbackUDP(sockaddr_in *addr) {
|
|
uintptr_t s = (uintptr_t)socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
memset(addr, 0, sizeof(*addr));
|
|
addr->sin_family = AF_INET;
|
|
addr->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
socklen_t len = sizeof(*addr);
|
|
bind(s, (sockaddr *)addr, sizeof(*addr));
|
|
getsockname(s, (sockaddr *)addr, &len);
|
|
return s;
|
|
}
|
|
|
|
bool TestNetWait() {
|
|
net::Init();
|
|
|
|
// A wake ends a wait that has no socket and no timeout, whether it comes first or second.
|
|
{
|
|
net::WakeSocket wake;
|
|
EXPECT_TRUE(wake.Fd() != -1);
|
|
std::thread waker([&] { wake.Wake(); });
|
|
EXPECT_EQ_INT((int)net::WaitSocketsOrWake(nullptr, nullptr, 0, false, -1.0, &wake), (int)net::WaitResult::CANCELLED);
|
|
waker.join();
|
|
wake.Drain();
|
|
// Drained, so nothing is pending: a zero timeout times out.
|
|
EXPECT_EQ_INT((int)net::WaitSocketsOrWake(nullptr, nullptr, 0, false, 0.0, &wake), (int)net::WaitResult::TIMEOUT);
|
|
}
|
|
|
|
// Data ends a wait; a quiet socket times out.
|
|
{
|
|
sockaddr_in addrA, addrB;
|
|
uintptr_t a = LoopbackUDP(&addrA);
|
|
uintptr_t b = LoopbackUDP(&addrB);
|
|
EXPECT_EQ_INT((int)net::WaitSocket(a, false, 0.05, nullptr), (int)net::WaitResult::TIMEOUT);
|
|
const char x = 'x';
|
|
sendto(b, &x, 1, 0, (sockaddr *)&addrA, sizeof(addrA));
|
|
EXPECT_EQ_INT((int)net::WaitSocket(a, false, 5.0, nullptr), (int)net::WaitResult::READY);
|
|
closesocket(a);
|
|
closesocket(b);
|
|
}
|
|
|
|
// A cancelled token ends every wait on it, before or during, and stays cancelled.
|
|
{
|
|
sockaddr_in addr;
|
|
uintptr_t a = LoopbackUDP(&addr);
|
|
net::CancelToken cancel;
|
|
std::thread canceller([&] { cancel.Cancel(); });
|
|
EXPECT_EQ_INT((int)net::WaitSocket(a, false, -1.0, &cancel), (int)net::WaitResult::CANCELLED);
|
|
canceller.join();
|
|
EXPECT_EQ_INT((int)net::WaitSocket(a, false, -1.0, &cancel), (int)net::WaitResult::CANCELLED);
|
|
closesocket(a);
|
|
}
|
|
|
|
// A cancel ends a connect that would otherwise wait out its timeout (nothing answers here).
|
|
{
|
|
http::Client client(nullptr);
|
|
if (client.Resolve("10.255.255.1", 9)) {
|
|
net::CancelToken cancel;
|
|
const double start = time_now_d();
|
|
std::thread canceller([&] { cancel.Cancel(); });
|
|
EXPECT_FALSE(client.Connect(1, 30.0, &cancel));
|
|
canceller.join();
|
|
EXPECT_TRUE(time_now_d() - start < 5.0);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|