Files
ppsspp/Common/Net/NetBuffer.cpp
T
libretroadmin 5f0f72bfda Remove sleeps and timed polling from networking
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.
2026-10-08 17:58:46 +02:00

117 lines
2.9 KiB
C++

#include "ppsspp_config.h"
#include <algorithm>
#include <cstring>
#include "Common/Net/SocketCompat.h"
#if _MSC_VER
#pragma warning(disable:4267)
#endif
#include "Common/File/FileDescriptor.h"
#include "Common/Log.h"
#include "Common/Net/NetBuffer.h"
#include "Common/TimeUtil.h"
namespace net {
void RequestProgress::Update(int64_t downloaded, int64_t totalBytes, bool done) {
if (totalBytes) {
progress = (double)downloaded / (double)totalBytes;
} else {
progress = 0.01f;
}
if (callback) {
callback(downloaded, totalBytes, done);
}
}
bool Buffer::FlushSocket(uintptr_t sock, double timeout, const CancelToken *cancel) {
data_.iterate_blocks([&](const char *data, size_t size) {
for (size_t pos = 0, end = size; pos < end; ) {
// The timeout applies to each send, not the whole flush.
const WaitResult wait = WaitSocket(sock, true, timeout, cancel);
if (wait == WaitResult::TIMEOUT) {
ERROR_LOG(Log::IO, "FlushSocket timed out");
return false;
} else if (wait != WaitResult::READY) {
return false;
}
int sent = send(sock, &data[pos], end - pos, MSG_NOSIGNAL);
// TODO: Do we need some retry logic here, instead of just giving up?
if (sent < 0) {
ERROR_LOG(Log::IO, "FlushSocket failed to send: errno=%d", errno);
return false;
}
pos += sent;
}
return true;
});
data_.clear();
return true;
}
bool Buffer::ReadAllWithProgress(int fd, int knownSize, RequestProgress *progress) {
std::vector<char> buf;
// We're non-blocking and reading from an OS buffer, so try to read as much as we can at a time.
if (knownSize >= 65536 * 16) {
buf.resize(65536);
} else if (knownSize >= 1024 * 16) {
buf.resize(knownSize / 16);
} else {
buf.resize(1024);
}
double st = time_now_d();
int total = 0;
while (true) {
// Block until there's data, the end of the stream, or a cancel.
const WaitResult wait = WaitSocket(fd, false, -1.0, progress ? progress->cancel : nullptr);
if (wait != WaitResult::READY)
return false;
int retval = recv(fd, &buf[0], buf.size(), MSG_NOSIGNAL);
if (retval == 0) {
return true;
} else if (retval < 0) {
if (socket_errno != EWOULDBLOCK) {
ERROR_LOG(Log::IO, "Error reading from buffer: %i", retval);
return false;
}
// Just try again on a would block error, not a real error.
continue;
}
char *p = Append((size_t)retval);
memcpy(p, &buf[0], retval);
total += retval;
if (progress) {
progress->Update(total, knownSize, false);
progress->kBps = (float)(total / (time_now_d() - st)) / 1024.0f;
}
}
return true;
}
int Buffer::Read(int fd, size_t sz) {
char buf[4096];
int retval;
size_t received = 0;
while ((retval = recv(fd, buf, std::min(sz, sizeof(buf)), MSG_NOSIGNAL)) > 0) {
if (retval < 0) {
return retval;
}
char *p = Append((size_t)retval);
memcpy(p, buf, retval);
sz -= retval;
received += retval;
if (sz == 0)
return 0;
}
return (int)received;
}
} // namespace