summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authornbouteme <nbouteme@users.noreply.github.com>2020-05-17 01:00:35 +0200
committernbouteme <nbouteme@users.noreply.github.com>2020-06-24 12:59:41 +0200
commitb68ab560dc541b4894ecb5eccb9dfd8c5efed308 (patch)
tree4c5a3dbbf3f9a2d144716fb7f7169583372db4fa /Source/Core
parenta1a107dc08e8b36f908ac901273f082bb3e00612 (diff)
Make SO_POLL complete asynchronously
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Core/IOS/Network/IP/Top.cpp91
-rw-r--r--Source/Core/Core/IOS/Network/IP/Top.h8
-rw-r--r--Source/Core/Core/IOS/Network/Socket.cpp154
-rw-r--r--Source/Core/Core/IOS/Network/Socket.h23
-rw-r--r--Source/Core/Core/State.cpp2
5 files changed, 213 insertions, 65 deletions
diff --git a/Source/Core/Core/IOS/Network/IP/Top.cpp b/Source/Core/Core/IOS/Network/IP/Top.cpp
index 7fb436dbb8..5f8ee3282a 100644
--- a/Source/Core/Core/IOS/Network/IP/Top.cpp
+++ b/Source/Core/Core/IOS/Network/IP/Top.cpp
@@ -50,13 +50,6 @@
#include <unistd.h>
#endif
-// WSAPoll doesn't support POLLPRI and POLLWRBAND flags
-#ifdef _WIN32
-#define UNSUPPORTED_WSAPOLL POLLPRI | POLLWRBAND
-#else
-#define UNSUPPORTED_WSAPOLL 0
-#endif
-
namespace IOS::HLE::Device
{
enum SOResultCode : s32
@@ -80,6 +73,12 @@ NetIPTop::~NetIPTop()
#endif
}
+void NetIPTop::DoState(PointerWrap& p)
+{
+ DoStateShared(p);
+ WiiSockMan::GetInstance().DoState(p);
+}
+
static constexpr u32 inet_addr(u8 a, u8 b, u8 c, u8 d)
{
return (static_cast<u32>(a) << 24) | (static_cast<u32>(b) << 16) | (static_cast<u32>(c) << 8) | d;
@@ -599,77 +598,45 @@ IPCCommandResult NetIPTop::HandleInetNToPRequest(const IOCtlRequest& request)
IPCCommandResult NetIPTop::HandlePollRequest(const IOCtlRequest& request)
{
- // Map Wii/native poll events types
- struct
- {
- int native;
- int wii;
- } mapping[] = {
- {POLLRDNORM, 0x0001}, {POLLRDBAND, 0x0002}, {POLLPRI, 0x0004}, {POLLWRNORM, 0x0008},
- {POLLWRBAND, 0x0010}, {POLLERR, 0x0020}, {POLLHUP, 0x0040}, {POLLNVAL, 0x0080},
- };
+ WiiSockMan& sm = WiiSockMan::GetInstance();
- u32 unknown = Memory::Read_U32(request.buffer_in);
- u32 timeout = Memory::Read_U32(request.buffer_in + 4);
+ if (!request.buffer_in || !request.buffer_out)
+ return GetDefaultReply(-SO_EINVAL);
- int nfds = request.buffer_out_size / 0xc;
- if (nfds == 0)
- ERROR_LOG(IOS_NET, "Hidden POLL");
+ // Negative timeout indicates wait forever
+ const s64 timeout = static_cast<s64>(Memory::Read_U64(request.buffer_in));
+
+ const u32 nfds = request.buffer_out_size / 0xc;
+ if (nfds == 0 || nfds > WII_SOCKET_FD_MAX)
+ {
+ ERROR_LOG(IOS_NET, "IOCTL_SO_POLL failed: Invalid array size %d, ret=%d", nfds, -SO_EINVAL);
+ return GetDefaultReply(-SO_EINVAL);
+ }
std::vector<pollfd_t> ufds(nfds);
- for (int i = 0; i < nfds; ++i)
+ for (u32 i = 0; i < nfds; ++i)
{
- s32 wii_fd = Memory::Read_U32(request.buffer_out + 0xc * i);
- ufds[i].fd = WiiSockMan::GetInstance().GetHostSocket(wii_fd); // fd
- int events = Memory::Read_U32(request.buffer_out + 0xc * i + 4); // events
- ufds[i].revents = Memory::Read_U32(request.buffer_out + 0xc * i + 8); // revents
+ const s32 wii_fd = Memory::Read_U32(request.buffer_out + 0xc * i);
+ ufds[i].fd = sm.GetHostSocket(wii_fd); // fd
+ const int events = Memory::Read_U32(request.buffer_out + 0xc * i + 4); // events
+ ufds[i].revents = 0;
// Translate Wii to native events
- int unhandled_events = events;
- ufds[i].events = 0;
- for (auto& map : mapping)
- {
- if (events & map.wii)
- ufds[i].events |= map.native;
- unhandled_events &= ~map.wii;
- }
+ ufds[i].events = WiiSockMan::ConvertEvents(events, WiiSockMan::ConvertDirection::WiiToNative);
DEBUG_LOG(IOS_NET,
"IOCTL_SO_POLL(%d) "
- "Sock: %08x, Unknown: %08x, Events: %08x, "
+ "Sock: %08x, Events: %08x, "
"NativeEvents: %08x",
- i, wii_fd, unknown, events, ufds[i].events);
+ i, wii_fd, events, ufds[i].events);
// Do not pass return-only events to the native poll
ufds[i].events &= ~(POLLERR | POLLHUP | POLLNVAL | UNSUPPORTED_WSAPOLL);
-
- if (unhandled_events)
- ERROR_LOG(IOS_NET, "SO_POLL: unhandled Wii event types: %04x", unhandled_events);
}
- int ret = poll(ufds.data(), nfds, timeout);
- ret = WiiSockMan::GetNetErrorCode(ret, "SO_POLL", false);
-
- for (int i = 0; i < nfds; ++i)
- {
- // Translate native to Wii events
- int revents = 0;
- for (auto& map : mapping)
- {
- if (ufds[i].revents & map.native)
- revents |= map.wii;
- }
-
- // No need to change fd or events as they are input only.
- // Memory::Write_U32(ufds[i].fd, request.buffer_out + 0xc*i); //fd
- // Memory::Write_U32(events, request.buffer_out + 0xc*i + 4); //events
- Memory::Write_U32(revents, request.buffer_out + 0xc * i + 8); // revents
-
- DEBUG_LOG(IOS_NET, "IOCTL_SO_POLL socket %d wevents %08X events %08X revents %08X", i, revents,
- ufds[i].events, ufds[i].revents);
- }
-
- return GetDefaultReply(ret);
+ // Prevents blocking emulation on a blocking poll
+ sm.AddPollCommand({request.address, request.buffer_out, std::move(ufds), timeout});
+ return GetNoReply();
}
IPCCommandResult NetIPTop::HandleGetHostByNameRequest(const IOCtlRequest& request)
diff --git a/Source/Core/Core/IOS/Network/IP/Top.h b/Source/Core/Core/IOS/Network/IP/Top.h
index 06fe19234f..9a36021e6d 100644
--- a/Source/Core/Core/IOS/Network/IP/Top.h
+++ b/Source/Core/Core/IOS/Network/IP/Top.h
@@ -13,6 +13,13 @@
#include <ws2tcpip.h>
#endif
+// WSAPoll doesn't support POLLPRI and POLLWRBAND flags
+#ifdef _WIN32
+constexpr int UNSUPPORTED_WSAPOLL = POLLPRI | POLLWRBAND;
+#else
+constexpr int UNSUPPORTED_WSAPOLL = 0;
+#endif
+
namespace IOS::HLE
{
enum NET_IOCTL
@@ -62,6 +69,7 @@ public:
NetIPTop(Kernel& ios, const std::string& device_name);
virtual ~NetIPTop();
+ void DoState(PointerWrap& p) override;
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
diff --git a/Source/Core/Core/IOS/Network/Socket.cpp b/Source/Core/Core/IOS/Network/Socket.cpp
index b2ff4c6795..313895a43f 100644
--- a/Source/Core/Core/IOS/Network/Socket.cpp
+++ b/Source/Core/Core/IOS/Network/Socket.cpp
@@ -6,6 +6,8 @@
#include "Core/IOS/Network/Socket.h"
#include <algorithm>
+#include <numeric>
+
#include <mbedtls/error.h>
#ifndef _WIN32
#include <arpa/inet.h>
@@ -697,7 +699,7 @@ void WiiSockMan::Update()
while (socket_iter != end_socks)
{
- WiiSocket& sock = socket_iter->second;
+ const WiiSocket& sock = socket_iter->second;
if (sock.IsValid())
{
FD_SET(sock.fd, &read_fds);
@@ -712,7 +714,8 @@ void WiiSockMan::Update()
socket_iter = WiiSockets.erase(socket_iter);
}
}
- s32 ret = select(nfds, &read_fds, &write_fds, &except_fds, &t);
+
+ const s32 ret = select(nfds, &read_fds, &write_fds, &except_fds, &t);
if (ret >= 0)
{
@@ -730,6 +733,87 @@ void WiiSockMan::Update()
elem.second.Update(false, false, false);
}
}
+ UpdatePollCommands();
+}
+
+void WiiSockMan::UpdatePollCommands()
+{
+ static constexpr int error_event = (POLLHUP | POLLERR);
+
+ if (pending_polls.empty())
+ return;
+
+ const auto now = std::chrono::high_resolution_clock::now();
+ const auto elapsed_d = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_time);
+ const auto elapsed = elapsed_d.count();
+ last_time = now;
+
+ for (auto& pcmd : pending_polls)
+ {
+ // Don't touch negative timeouts
+ if (pcmd.timeout > 0)
+ pcmd.timeout = std::max<s64>(0, pcmd.timeout - elapsed);
+ }
+
+ pending_polls.erase(
+ std::remove_if(
+ pending_polls.begin(), pending_polls.end(),
+ [this](auto& pcmd) {
+ const auto request = Request(pcmd.request_addr);
+ auto& pfds = pcmd.wii_fds;
+ int ret = 0;
+
+ // Happens only on savestate load
+ if (pfds[0].revents & error_event)
+ {
+ ret = static_cast<int>(pfds.size());
+ }
+ else
+ {
+ // Make the behavior of poll consistent across platforms by not passing:
+ // - Set with invalid fds, revents is set to 0 (Linux) or POLLNVAL (Windows)
+ // - Set without a valid socket, raises an error on Windows
+ std::vector<int> original_order(pfds.size());
+ std::iota(original_order.begin(), original_order.end(), 0);
+ // Select indices with valid fds
+ auto mid = std::partition(original_order.begin(), original_order.end(), [&](auto i) {
+ return GetHostSocket(Memory::Read_U32(pcmd.buffer_out + 0xc * i)) >= 0;
+ });
+ const auto n_valid = std::distance(original_order.begin(), mid);
+
+ // Move all the valid pollfds to the front of the vector
+ for (auto i = 0; i < n_valid; ++i)
+ std::swap(pfds[i], pfds[original_order[i]]);
+
+ if (n_valid > 0)
+ ret = poll(pfds.data(), n_valid, 0);
+ if (ret < 0)
+ ret = GetNetErrorCode(ret, "UpdatePollCommands", false);
+
+ // Move everything back to where they were
+ for (auto i = 0; i < n_valid; ++i)
+ std::swap(pfds[i], pfds[original_order[i]]);
+ }
+
+ if (ret == 0 && pcmd.timeout)
+ return false;
+
+ // Translate native to Wii events,
+ for (u32 i = 0; i < pfds.size(); ++i)
+ {
+ const int revents = ConvertEvents(pfds[i].revents, ConvertDirection::NativeToWii);
+
+ // No need to change fd or events as they are input only.
+ // Memory::Write_U32(ufds[i].fd, request.buffer_out + 0xc*i); //fd
+ // Memory::Write_U32(events, request.buffer_out + 0xc*i + 4); //events
+ Memory::Write_U32(revents, pcmd.buffer_out + 0xc * i + 8); // revents
+ DEBUG_LOG(IOS_NET, "IOCTL_SO_POLL socket %d wevents %08X events %08X revents %08X", i,
+ revents, pfds[i].events, pfds[i].revents);
+ }
+ GetIOS()->EnqueueIPCReply(request, ret);
+ return true;
+ }),
+ pending_polls.end());
}
void WiiSockMan::Convert(WiiSockAddrIn const& from, sockaddr_in& to)
@@ -739,6 +823,43 @@ void WiiSockMan::Convert(WiiSockAddrIn const& from, sockaddr_in& to)
to.sin_port = from.port;
}
+s32 WiiSockMan::ConvertEvents(s32 events, ConvertDirection dir)
+{
+ constexpr struct
+ {
+ int native;
+ int wii;
+ } mapping[] = {
+ {POLLRDNORM, 0x0001}, {POLLRDBAND, 0x0002}, {POLLPRI, 0x0004}, {POLLWRNORM, 0x0008},
+ {POLLWRBAND, 0x0010}, {POLLERR, 0x0020}, {POLLHUP, 0x0040}, {POLLNVAL, 0x0080},
+ };
+
+ s32 converted_events = 0;
+ s32 unhandled_events = 0;
+
+ if (dir == ConvertDirection::NativeToWii)
+ {
+ for (const auto& map : mapping)
+ {
+ if (events & map.native)
+ converted_events |= map.wii;
+ }
+ }
+ else
+ {
+ unhandled_events = events;
+ for (const auto& map : mapping)
+ {
+ if (events & map.wii)
+ converted_events |= map.native;
+ unhandled_events &= ~map.wii;
+ }
+ }
+ if (unhandled_events)
+ ERROR_LOG(IOS_NET, "SO_POLL: unhandled Wii event types: %04x", unhandled_events);
+ return converted_events;
+}
+
void WiiSockMan::Convert(sockaddr_in const& from, WiiSockAddrIn& to, s32 addrlen)
{
to.addr.addr = from.sin_addr.s_addr;
@@ -750,6 +871,35 @@ void WiiSockMan::Convert(sockaddr_in const& from, WiiSockAddrIn& to, s32 addrlen
to.len = addrlen;
}
+void WiiSockMan::DoState(PointerWrap& p)
+{
+ bool saving =
+ p.mode == PointerWrap::Mode::MODE_WRITE || p.mode == PointerWrap::Mode::MODE_MEASURE;
+ auto size = pending_polls.size();
+ p.Do(size);
+ if (!saving)
+ pending_polls.resize(size);
+ for (auto& pcmd : pending_polls)
+ {
+ p.Do(pcmd.request_addr);
+ p.Do(pcmd.buffer_out);
+ p.Do(pcmd.wii_fds);
+ }
+
+ if (saving)
+ return;
+ for (auto& pcmd : pending_polls)
+ {
+ for (auto& wfd : pcmd.wii_fds)
+ wfd.revents = (POLLHUP | POLLERR);
+ }
+}
+
+void WiiSockMan::AddPollCommand(const PollCommand& cmd)
+{
+ pending_polls.push_back(cmd);
+}
+
void WiiSockMan::UpdateWantDeterminism(bool want)
{
// If we switched into movie recording, kill existing sockets.
diff --git a/Source/Core/Core/IOS/Network/Socket.h b/Source/Core/Core/IOS/Network/Socket.h
index 615560f305..99171a4e78 100644
--- a/Source/Core/Core/IOS/Network/Socket.h
+++ b/Source/Core/Core/IOS/Network/Socket.h
@@ -212,6 +212,20 @@ private:
class WiiSockMan
{
public:
+ enum class ConvertDirection
+ {
+ WiiToNative,
+ NativeToWii
+ };
+
+ struct PollCommand
+ {
+ u32 request_addr;
+ u32 buffer_out;
+ std::vector<pollfd_t> wii_fds;
+ s64 timeout;
+ };
+
static s32 GetNetErrorCode(s32 ret, const char* caller, bool isRW);
static char* DecodeError(s32 ErrorCode);
@@ -223,6 +237,10 @@ public:
void Update();
static void Convert(WiiSockAddrIn const& from, sockaddr_in& to);
static void Convert(sockaddr_in const& from, WiiSockAddrIn& to, s32 addrlen = -1);
+ static s32 ConvertEvents(s32 events, ConvertDirection dir);
+
+ void DoState(PointerWrap& p);
+ void AddPollCommand(const PollCommand& cmd);
// NON-BLOCKING FUNCTIONS
s32 NewSocket(s32 af, s32 type, s32 protocol);
s32 AddSocket(s32 fd, bool is_rw);
@@ -256,7 +274,12 @@ private:
WiiSockMan(WiiSockMan&&) = delete;
WiiSockMan& operator=(WiiSockMan&&) = delete;
+ void UpdatePollCommands();
+
std::unordered_map<s32, WiiSocket> WiiSockets;
s32 errno_last;
+ std::vector<PollCommand> pending_polls;
+ std::chrono::time_point<std::chrono::high_resolution_clock> last_time =
+ std::chrono::high_resolution_clock::now();
};
} // namespace IOS::HLE
diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp
index 1a2b74c00b..9c01be2d72 100644
--- a/Source/Core/Core/State.cpp
+++ b/Source/Core/Core/State.cpp
@@ -74,7 +74,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
static std::thread g_save_thread;
// Don't forget to increase this after doing changes on the savestate system
-constexpr u32 STATE_VERSION = 117; // Last changed in PR 7396
+constexpr u32 STATE_VERSION = 118; // Last changed in PR 8813
// Maps savestate versions to Dolphin versions.
// Versions after 42 don't need to be added to this list,