Merge pull request #8813 from nbouteme/master
Make SO_POLL complete asynchronously in IOS_NET SO
This commit is contained in:
commit
502ab789d9
|
@ -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
|
||||
WiiSockMan& sm = WiiSockMan::GetInstance();
|
||||
|
||||
if (!request.buffer_in || !request.buffer_out)
|
||||
return GetDefaultReply(-SO_EINVAL);
|
||||
|
||||
// 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)
|
||||
{
|
||||
int native;
|
||||
int wii;
|
||||
} mapping[] = {
|
||||
{POLLRDNORM, 0x0001}, {POLLRDBAND, 0x0002}, {POLLPRI, 0x0004}, {POLLWRNORM, 0x0008},
|
||||
{POLLWRBAND, 0x0010}, {POLLERR, 0x0020}, {POLLHUP, 0x0040}, {POLLNVAL, 0x0080},
|
||||
};
|
||||
|
||||
u32 unknown = Memory::Read_U32(request.buffer_in);
|
||||
u32 timeout = Memory::Read_U32(request.buffer_in + 4);
|
||||
|
||||
int nfds = request.buffer_out_size / 0xc;
|
||||
if (nfds == 0)
|
||||
ERROR_LOG(IOS_NET, "Hidden POLL");
|
||||
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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue