From 58ecfa933e42ea5f92d5b3f6db67854579d4a1cc Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Wed, 18 Dec 2024 13:27:52 -0800 Subject: [PATCH 1/3] GdbServer: Implement new netstream that can be interrupted A major limitation of iostream is that you can't have reads or writes with a safe interrupt. Instead rewrite the interface with Linux ppoll so that these can be safely interrupted with a signal and return early. --- .../LinuxSyscalls/GdbServer.cpp | 68 ++++---- .../LinuxEmulation/LinuxSyscalls/GdbServer.h | 12 +- .../LinuxSyscalls/NetStream.cpp | 151 ++++++------------ .../LinuxEmulation/LinuxSyscalls/NetStream.h | 40 ++++- 4 files changed, 125 insertions(+), 146 deletions(-) diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/GdbServer.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/GdbServer.cpp index 4265a562b2..474bf52260 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/GdbServer.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/GdbServer.cpp @@ -64,7 +64,7 @@ namespace FEX { #ifndef _WIN32 void GdbServer::Break(FEXCore::Core::InternalThreadState* Thread, int signal) { std::lock_guard lk(sendMutex); - if (!CommsStream) { + if (!CommsStream.HasSocket()) { return; } @@ -73,7 +73,7 @@ void GdbServer::Break(FEXCore::Core::InternalThreadState* Thread, int signal) { CurrentDebuggingThread = ThreadObject->ThreadInfo.TID.load(); const auto str = fextl::fmt::format("T{:02x}thread:{:x};", signal, CurrentDebuggingThread); - SendPacket(*CommsStream, str); + SendPacket(str); } void GdbServer::WaitForThreadWakeup() { @@ -178,7 +178,7 @@ static fextl::string encodeHex(std::string_view str) { // Takes a serial stream and reads a single packet // Un-escapes chars, checks the checksum and request a retransmit if it fails. // Once the checksum is validated, it acknowledges and returns the packet in a string -fextl::string GdbServer::ReadPacket(std::iostream& stream) { +fextl::string GdbServer::ReadPacket() { fextl::string packet {}; // The GDB "Remote Serial Protocal" was originally 7bit clean for use on serial ports. @@ -190,9 +190,9 @@ fextl::string GdbServer::ReadPacket(std::iostream& stream) { // where any $ or # in the packet body are escaped ('}' followed by the char XORed with 0x20) // The checksum is a single unsigned byte sum of the data, hex encoded. - int c; - while ((c = stream.get()) > 0) { - switch (c) { + std::optional c; + while ((c = CommsStream.get()).has_value() && !c->Hangup) { + switch (c->data) { case '$': // start of packet if (packet.size() != 0) { LogMan::Msg::EFmt("Dropping unexpected data: \"{}\"", packet); @@ -203,15 +203,18 @@ fextl::string GdbServer::ReadPacket(std::iostream& stream) { break; case '}': // escape char { - char escaped; - stream >> escaped; - packet.push_back(escaped ^ 0x20); + auto escaped = CommsStream.get(); + if (escaped.has_value() && !escaped->Hangup) { + packet.push_back(escaped->data ^ 0x20); + } else { + LogMan::Msg::EFmt("Received Invalid escape char: ${}", packet); + } break; } case '#': // end of packet { char hexString[3] = {0, 0, 0}; - stream.read(hexString, 2); + CommsStream.read(hexString, 2); int expected_checksum = std::strtoul(hexString, nullptr, 16); if (calculateChecksum(packet) == expected_checksum) { @@ -221,7 +224,7 @@ fextl::string GdbServer::ReadPacket(std::iostream& stream) { } break; } - default: packet.push_back((char)c); break; + default: packet.push_back(c->data); break; } } @@ -248,22 +251,22 @@ static fextl::string escapePacket(const fextl::string& packet) { return ss.str(); } -void GdbServer::SendPacket(std::ostream& stream, const fextl::string& packet) { +void GdbServer::SendPacket(const fextl::string& packet) { const auto escaped = escapePacket(packet); const auto str = fextl::fmt::format("${}#{:02x}", escaped, calculateChecksum(escaped)); - stream << str << std::flush; + CommsStream.SendPacket(str); } -void GdbServer::SendACK(std::ostream& stream, bool NACK) { +void GdbServer::SendACK(bool NACK) { if (NoAckMode) { return; } if (NACK) { - stream << "-" << std::flush; + CommsStream.SendPacket("-"); } else { - stream << "+" << std::flush; + CommsStream.SendPacket("+"); } if (SettingNoAckMode) { @@ -1341,16 +1344,16 @@ GdbServer::HandledPacketType GdbServer::ProcessPacket(const fextl::string& packe void GdbServer::SendPacketPair(const HandledPacketType& response) { std::lock_guard lk(sendMutex); if (response.TypeResponse == HandledPacketType::TYPE_ACK || response.TypeResponse == HandledPacketType::TYPE_ONLYACK) { - SendACK(*CommsStream, false); + SendACK(false); } else if (response.TypeResponse == HandledPacketType::TYPE_NACK || response.TypeResponse == HandledPacketType::TYPE_ONLYNACK) { - SendACK(*CommsStream, true); + SendACK(true); } if (response.TypeResponse == HandledPacketType::TYPE_UNKNOWN) { - SendPacket(*CommsStream, ""); + SendPacket(""); } else if (response.TypeResponse != HandledPacketType::TYPE_ONLYNACK && response.TypeResponse != HandledPacketType::TYPE_ONLYACK && response.TypeResponse != HandledPacketType::TYPE_NONE) { - SendPacket(*CommsStream, response.Response); + SendPacket(response.Response); } } @@ -1362,7 +1365,7 @@ GdbServer::WaitForConnectionResult GdbServer::WaitForConnection() { int Result = ppoll(&PollFD, 1, nullptr, nullptr); if (Result > 0) { if (PollFD.revents & POLLIN) { - CommsStream = OpenSocket(); + OpenSocket(); return WaitForConnectionResult::CONNECTION; } else if (PollFD.revents & (POLLHUP | POLLERR | POLLNVAL)) { // Listen socket error or shutting down @@ -1393,12 +1396,11 @@ void GdbServer::GdbServerLoop() { HandledPacketType response {}; // Outer server loop. Handles packet start, ACK/NAK and break - - int c; - while ((c = CommsStream->get()) >= 0) { - switch (c) { + std::optional c; + while (!CoreShuttingDown.load() && (c = CommsStream.get()).has_value() && !c->Hangup) { + switch (c->data) { case '$': { - auto packet = ReadPacket(*CommsStream); + auto packet = ReadPacket(); response = ProcessPacket(packet); SendPacketPair(response); if (response.TypeResponse == HandledPacketType::TYPE_UNKNOWN) { @@ -1413,7 +1415,7 @@ void GdbServer::GdbServerLoop() { // NAK, Resend requested { std::lock_guard lk(sendMutex); - SendPacket(*CommsStream, response.Response); + SendPacket(response.Response); } break; case '\x03': { // ASCII EOT @@ -1426,13 +1428,17 @@ void GdbServer::GdbServerLoop() { SendPacketPair({std::move(str), HandledPacketType::TYPE_ACK}); break; } - default: LogMan::Msg::DFmt("GdbServer: Unexpected byte {} ({:02x})", static_cast(c), c); + default: LogMan::Msg::DFmt("GdbServer: Unexpected byte {} ({:02x})", c->data, c->data); } } + if (c.has_value() && c->Hangup) { + break; + } + { std::lock_guard lk(sendMutex); - CommsStream.reset(); + CommsStream.InvalidateSocket(); } } @@ -1504,14 +1510,14 @@ void GdbServer::CloseListenSocket() { unlink(GdbUnixSocketPath.c_str()); } -fextl::unique_ptr GdbServer::OpenSocket() { +void GdbServer::OpenSocket() { // Block until a connection arrives struct sockaddr_storage their_addr {}; socklen_t addr_size {}; int new_fd = accept(ListenSocket, (struct sockaddr*)&their_addr, &addr_size); - return fextl::make_unique(new_fd); + CommsStream.OpenSocket(new_fd); } #endif diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/GdbServer.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/GdbServer.h index af749d8f89..fbf728e31d 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/GdbServer.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/GdbServer.h @@ -14,11 +14,11 @@ tags: glue|gdbserver #include #include -#include #include #include #include +#include "LinuxSyscalls/NetStream.h" #include "LinuxSyscalls/SignalDelegator.h" namespace FEX { @@ -45,12 +45,12 @@ class GdbServer { ERROR, }; WaitForConnectionResult WaitForConnection(); - fextl::unique_ptr OpenSocket(); + void OpenSocket(); void StartThread(); - fextl::string ReadPacket(std::iostream& stream); - void SendPacket(std::ostream& stream, const fextl::string& packet); + fextl::string ReadPacket(); + void SendPacket(const fextl::string& packet); - void SendACK(std::ostream& stream, bool NACK); + void SendACK(bool NACK); Event ThreadBreakEvent {}; void WaitForThreadWakeup(); @@ -147,7 +147,7 @@ class GdbServer { FEX::HLE::SyscallHandler* const SyscallHandler; FEX::HLE::SignalDelegator* SignalDelegation; fextl::unique_ptr gdbServerThread; - fextl::unique_ptr CommsStream; + FEX::Utils::NetStream CommsStream; std::mutex sendMutex; bool SettingNoAckMode {false}; bool NoAckMode {false}; diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/NetStream.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/NetStream.cpp index 31a9f52070..fa0a8adad9 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/NetStream.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/NetStream.cpp @@ -4,128 +4,73 @@ #include #include -#include #include -#include -#ifndef _WIN32 #include -#endif +#include #include -namespace FEXCore::Utils { -namespace { - class NetBuf final : public std::streambuf, public FEXCore::Allocator::FEXAllocOperators { - public: - explicit NetBuf(int socketfd) - : socket {socketfd} { - reset_output_buffer(); - } - ~NetBuf() override { - close(socket); - } - - private: - std::streamsize xsputn(const char* buffer, std::streamsize size) override; - - std::streambuf::int_type underflow() override; - std::streambuf::int_type overflow(std::streambuf::int_type ch) override; - int sync() override; - - void reset_output_buffer() { - // we always leave room for one extra char - setp(std::begin(output_buffer), std::end(output_buffer) - 1); - } - - int flushBuffer(const char* buffer, size_t size); - - int socket; - std::array output_buffer; - std::array input_buffer; // enough for a typical packet - }; - - int NetBuf::flushBuffer(const char* buffer, size_t size) { -#ifndef _WIN32 - size_t total = 0; - - // Send data - while (total < size) { - size_t sent = send(socket, (const void*)(buffer + total), size - total, MSG_NOSIGNAL); - if (sent == -1) { - // lets just assume all errors are end of file. - return -1; - } - total += sent; - } - - return 0; -#else - ERROR_AND_DIE_FMT("Unsupported"); -#endif +namespace FEX::Utils { +std::optional NetStream::get() { + if (read_offset != receive_buffer.size() && read_offset != receive_offset) { + auto Result = receive_buffer.at(read_offset); + ++read_offset; + return ReturnGet {Result, false}; } - std::streamsize NetBuf::xsputn(const char* buffer, std::streamsize size) { - size_t buf_remaining = epptr() - pptr(); + if (read_offset == receive_buffer.size()) { + read_offset = 0; + receive_offset = 0; + } - // Check if the string fits neatly in our buffer - if (size <= buf_remaining) { - ::memcpy(pptr(), buffer, size); - pbump(size); - return size; - } + struct pollfd pfd { + .fd = socketfd, .events = POLLIN, .revents = 0, + }; - // Otherwise, flush the buffer first - if (sync() < 0) { - return traits_type::eof(); + auto Result = ppoll(&pfd, 1, nullptr, nullptr); + if (Result > 0) { + if (pfd.revents & POLLHUP) { + return ReturnGet {'\0', true}; } - if (size > sizeof(output_buffer) / 2) { - // If we have a large string, bypass the buffer - flushBuffer(buffer, size); - return size; - } else { - return xsputn(buffer, size); + const auto remaining_size = receive_buffer.size() - receive_offset; + Result = ::recv(socketfd, &receive_buffer.at(receive_offset), remaining_size, 0); + if (Result > 0) { + receive_offset += Result; + auto Result = receive_buffer.at(read_offset); + ++read_offset; + return ReturnGet {Result, false}; } } - std::streambuf::int_type NetBuf::overflow(std::streambuf::int_type ch) { - // we always leave room for one extra char - *pptr() = (char)ch; - pbump(1); - return sync(); - } + return std::nullopt; +} - int NetBuf::sync() { - // Flush and reset output buffer to zero - if (flushBuffer(pbase(), pptr() - pbase()) < 0) { - return -1; +size_t NetStream::read(char* buf, size_t size) { + size_t Read {}; + while (Read < size) { + auto Result = get(); + if (Result.has_value() && !Result->Hangup) { + buf[Read] = Result->data; + ++Read; + } else { + return Read; } - reset_output_buffer(); - return 0; } + return Read; +} - std::streambuf::int_type NetBuf::underflow() { -#ifndef _WIN32 - ssize_t size = recv(socket, (void*)std::begin(input_buffer), sizeof(input_buffer), 0); - - if (size <= 0) { - setg(nullptr, nullptr, nullptr); - return traits_type::eof(); +bool NetStream::SendPacket(const fextl::string& packet) { + size_t Total {}; + while (Total < packet.size()) { + size_t Remaining = packet.size() - Total; + size_t sent = ::send(socketfd, &packet.at(Total), Remaining, MSG_NOSIGNAL); + if (sent == -1) { + return false; } - - setg(&input_buffer[0], &input_buffer[0], &input_buffer[size]); - - return traits_type::to_int_type(*gptr()); -#else - ERROR_AND_DIE_FMT("Unsupported"); -#endif + Total += sent; } -} // Anonymous namespace - -NetStream::NetStream(int socketfd) - : std::iostream(new NetBuf(socketfd)) {} -NetStream::~NetStream() { - delete rdbuf(); + return Total == packet.size(); } -} // namespace FEXCore::Utils +} // namespace FEX::Utils diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/NetStream.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/NetStream.h index 5002962727..5d1430a943 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/NetStream.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/NetStream.h @@ -2,13 +2,41 @@ #pragma once #include +#include +#include -#include +#include -namespace FEXCore::Utils { -class FEX_DEFAULT_VISIBILITY NetStream : public std::iostream { +namespace FEX::Utils { +class NetStream final { public: - explicit NetStream(int socketfd); - ~NetStream() override; + NetStream() + : receive_buffer(1500) {} + + void OpenSocket(int _socketfd) { + socketfd = _socketfd; + } + + void InvalidateSocket() { + socketfd = -1; + } + + bool HasSocket() const { + return socketfd != -1; + } + + struct ReturnGet { + char data; + bool Hangup; + }; + std::optional get(); + size_t read(char* buf, size_t size); + + bool SendPacket(const fextl::string& packet); +private: + int socketfd {-1}; + size_t read_offset {}; + size_t receive_offset {}; + fextl::vector receive_buffer; }; -} // namespace FEXCore::Utils +} // namespace FEX::Utils From 83ce2d8d643bdb5fd1b6c9fcfa697bd9155d40b3 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Fri, 10 Jan 2025 10:05:49 -0800 Subject: [PATCH 2/3] Netstream: Use a std::variant --- .../LinuxSyscalls/GdbServer.cpp | 22 +++++++++---------- .../LinuxSyscalls/NetStream.cpp | 14 ++++++------ .../LinuxEmulation/LinuxSyscalls/NetStream.h | 16 ++++++++++---- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/GdbServer.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/GdbServer.cpp index 474bf52260..22e7829fe4 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/GdbServer.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/GdbServer.cpp @@ -190,9 +190,9 @@ fextl::string GdbServer::ReadPacket() { // where any $ or # in the packet body are escaped ('}' followed by the char XORed with 0x20) // The checksum is a single unsigned byte sum of the data, hex encoded. - std::optional c; - while ((c = CommsStream.get()).has_value() && !c->Hangup) { - switch (c->data) { + Utils::NetStream::ReturnGet c; + while ((c = CommsStream.get()).HasData()) { + switch (c.GetData()) { case '$': // start of packet if (packet.size() != 0) { LogMan::Msg::EFmt("Dropping unexpected data: \"{}\"", packet); @@ -204,8 +204,8 @@ fextl::string GdbServer::ReadPacket() { case '}': // escape char { auto escaped = CommsStream.get(); - if (escaped.has_value() && !escaped->Hangup) { - packet.push_back(escaped->data ^ 0x20); + if (escaped.HasData()) { + packet.push_back(escaped.GetData() ^ 0x20); } else { LogMan::Msg::EFmt("Received Invalid escape char: ${}", packet); } @@ -224,7 +224,7 @@ fextl::string GdbServer::ReadPacket() { } break; } - default: packet.push_back(c->data); break; + default: packet.push_back(c.GetData()); break; } } @@ -1396,9 +1396,9 @@ void GdbServer::GdbServerLoop() { HandledPacketType response {}; // Outer server loop. Handles packet start, ACK/NAK and break - std::optional c; - while (!CoreShuttingDown.load() && (c = CommsStream.get()).has_value() && !c->Hangup) { - switch (c->data) { + Utils::NetStream::ReturnGet c; + while (!CoreShuttingDown.load() && (c = CommsStream.get()).HasData()) { + switch (c.GetData()) { case '$': { auto packet = ReadPacket(); response = ProcessPacket(packet); @@ -1428,11 +1428,11 @@ void GdbServer::GdbServerLoop() { SendPacketPair({std::move(str), HandledPacketType::TYPE_ACK}); break; } - default: LogMan::Msg::DFmt("GdbServer: Unexpected byte {} ({:02x})", c->data, c->data); + default: LogMan::Msg::DFmt("GdbServer: Unexpected byte {} ({:02x})", c.GetData(), c.GetData()); } } - if (c.has_value() && c->Hangup) { + if (c.HasHangup()) { break; } diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/NetStream.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/NetStream.cpp index fa0a8adad9..498eb4e144 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/NetStream.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/NetStream.cpp @@ -10,11 +10,11 @@ #include namespace FEX::Utils { -std::optional NetStream::get() { +NetStream::ReturnGet NetStream::get() { if (read_offset != receive_buffer.size() && read_offset != receive_offset) { auto Result = receive_buffer.at(read_offset); ++read_offset; - return ReturnGet {Result, false}; + return NetStream::ReturnGet {Result}; } if (read_offset == receive_buffer.size()) { @@ -29,7 +29,7 @@ std::optional NetStream::get() { auto Result = ppoll(&pfd, 1, nullptr, nullptr); if (Result > 0) { if (pfd.revents & POLLHUP) { - return ReturnGet {'\0', true}; + return NetStream::ReturnGet {true}; } const auto remaining_size = receive_buffer.size() - receive_offset; @@ -38,19 +38,19 @@ std::optional NetStream::get() { receive_offset += Result; auto Result = receive_buffer.at(read_offset); ++read_offset; - return ReturnGet {Result, false}; + return NetStream::ReturnGet {Result}; } } - return std::nullopt; + return NetStream::ReturnGet {false}; } size_t NetStream::read(char* buf, size_t size) { size_t Read {}; while (Read < size) { auto Result = get(); - if (Result.has_value() && !Result->Hangup) { - buf[Read] = Result->data; + if (Result.HasData()) { + buf[Read] = Result.GetData(); ++Read; } else { return Read; diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/NetStream.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/NetStream.h index 5d1430a943..7730db6e17 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/NetStream.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/NetStream.h @@ -6,6 +6,7 @@ #include #include +#include namespace FEX::Utils { class NetStream final { @@ -25,11 +26,18 @@ class NetStream final { return socketfd != -1; } - struct ReturnGet { - char data; - bool Hangup; + struct ReturnGet final : public std::variant { + bool HasHangup() const { + return std::holds_alternative(*this) && std::get(*this); + } + bool HasData() const { + return std::holds_alternative(*this); + } + char GetData() const { + return std::get(*this); + } }; - std::optional get(); + ReturnGet get(); size_t read(char* buf, size_t size); bool SendPacket(const fextl::string& packet); From d5990409f5f587122a57c0298b694b763054e3fa Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Mon, 13 Jan 2025 08:52:57 -0800 Subject: [PATCH 3/3] review --- .../LinuxSyscalls/GdbServer.cpp | 79 ++++++++++--------- .../LinuxSyscalls/NetStream.cpp | 61 ++++++++++---- .../LinuxEmulation/LinuxSyscalls/NetStream.h | 9 ++- 3 files changed, 95 insertions(+), 54 deletions(-) diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/GdbServer.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/GdbServer.cpp index 22e7829fe4..fcef9081b1 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/GdbServer.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/GdbServer.cpp @@ -203,7 +203,12 @@ fextl::string GdbServer::ReadPacket() { break; case '}': // escape char { - auto escaped = CommsStream.get(); + Utils::NetStream::ReturnGet escaped; + + do { + escaped = CommsStream.get(); + } while (!escaped.HasData() && !escaped.HasHangup()); + if (escaped.HasData()) { packet.push_back(escaped.GetData() ^ 0x20); } else { @@ -214,7 +219,7 @@ fextl::string GdbServer::ReadPacket() { case '#': // end of packet { char hexString[3] = {0, 0, 0}; - CommsStream.read(hexString, 2); + CommsStream.read(hexString, 2, true); int expected_checksum = std::strtoul(hexString, nullptr, 16); if (calculateChecksum(packet) == expected_checksum) { @@ -1395,45 +1400,47 @@ void GdbServer::GdbServerLoop() { HandledPacketType response {}; - // Outer server loop. Handles packet start, ACK/NAK and break - Utils::NetStream::ReturnGet c; - while (!CoreShuttingDown.load() && (c = CommsStream.get()).HasData()) { - switch (c.GetData()) { - case '$': { - auto packet = ReadPacket(); - response = ProcessPacket(packet); - SendPacketPair(response); - if (response.TypeResponse == HandledPacketType::TYPE_UNKNOWN) { - LogMan::Msg::DFmt("Unknown packet {}", packet); + while (!CoreShuttingDown.load()) { + // Outer server loop. Handles packet start, ACK/NAK and break + Utils::NetStream::ReturnGet c; + while ((c = CommsStream.get()).HasData()) { + switch (c.GetData()) { + case '$': { + auto packet = ReadPacket(); + response = ProcessPacket(packet); + SendPacketPair(response); + if (response.TypeResponse == HandledPacketType::TYPE_UNKNOWN) { + LogMan::Msg::DFmt("Unknown packet {}", packet); + } + break; } - break; - } - case '+': - // ACK, do nothing. - break; - case '-': - // NAK, Resend requested - { - std::lock_guard lk(sendMutex); - SendPacket(response.Response); + case '+': + // ACK, do nothing. + break; + case '-': + // NAK, Resend requested + { + std::lock_guard lk(sendMutex); + SendPacket(response.Response); + } + break; + case '\x03': { // ASCII EOT + SyscallHandler->TM.Pause(); + fextl::string str = fextl::fmt::format("T02thread:{:02x};", getpid()); + if (LibraryMapChanged) { + // If libraries have changed then let gdb know + str += "library:1;"; + } + SendPacketPair({std::move(str), HandledPacketType::TYPE_ACK}); + break; } - break; - case '\x03': { // ASCII EOT - SyscallHandler->TM.Pause(); - fextl::string str = fextl::fmt::format("T02thread:{:02x};", getpid()); - if (LibraryMapChanged) { - // If libraries have changed then let gdb know - str += "library:1;"; + default: LogMan::Msg::DFmt("GdbServer: Unexpected byte {} ({:02x})", c.GetData(), c.GetData()); } - SendPacketPair({std::move(str), HandledPacketType::TYPE_ACK}); - break; - } - default: LogMan::Msg::DFmt("GdbServer: Unexpected byte {} ({:02x})", c.GetData(), c.GetData()); } - } - if (c.HasHangup()) { - break; + if (c.HasHangup()) { + break; + } } { diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/NetStream.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/NetStream.cpp index 498eb4e144..afa762f16a 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/NetStream.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/NetStream.cpp @@ -5,11 +5,18 @@ #include #include +#include #include #include #include namespace FEX::Utils { + +NetStream::NetStream() + : receive_buffer(1500) { + eventfd = ::eventfd(0, EFD_CLOEXEC); +} + NetStream::ReturnGet NetStream::get() { if (read_offset != receive_buffer.size() && read_offset != receive_offset) { auto Result = receive_buffer.at(read_offset); @@ -22,43 +29,67 @@ NetStream::ReturnGet NetStream::get() { receive_offset = 0; } - struct pollfd pfd { - .fd = socketfd, .events = POLLIN, .revents = 0, + struct pollfd pfds[2] = { + { + .fd = socketfd, + .events = POLLIN, + .revents = 0, + }, + + { + .fd = eventfd, + .events = POLLIN, + .revents = 0, + }, }; - auto Result = ppoll(&pfd, 1, nullptr, nullptr); + auto Result = poll(pfds, sizeof(pfds), -1); if (Result > 0) { - if (pfd.revents & POLLHUP) { - return NetStream::ReturnGet {true}; - } + for (auto& pfd : pfds) { + if (pfd.fd == eventfd && (pfd.revents & POLLIN)) { + // Interrupted by eventfd. + uint64_t data; + ::read(pfd.fd, &data, sizeof(data)); + break; + } + + if (pfd.revents & POLLHUP) { + return NetStream::ReturnGet {true}; + } - const auto remaining_size = receive_buffer.size() - receive_offset; - Result = ::recv(socketfd, &receive_buffer.at(receive_offset), remaining_size, 0); - if (Result > 0) { - receive_offset += Result; - auto Result = receive_buffer.at(read_offset); - ++read_offset; - return NetStream::ReturnGet {Result}; + const auto remaining_size = receive_buffer.size() - receive_offset; + Result = ::recv(socketfd, &receive_buffer.at(receive_offset), remaining_size, 0); + if (Result > 0) { + receive_offset += Result; + auto Result = receive_buffer.at(read_offset); + ++read_offset; + return NetStream::ReturnGet {Result}; + } } } return NetStream::ReturnGet {false}; } -size_t NetStream::read(char* buf, size_t size) { +size_t NetStream::read(char* buf, size_t size, bool ContinueOnInterrupt) { size_t Read {}; while (Read < size) { auto Result = get(); if (Result.HasData()) { buf[Read] = Result.GetData(); ++Read; - } else { + } else if ((!Result.HasData() && !ContinueOnInterrupt) || Result.HasHangup()) { return Read; } } return Read; } +void NetStream::InterruptReader() { + uint64_t data {1}; + ::write(eventfd, &data, sizeof(data)); +} + bool NetStream::SendPacket(const fextl::string& packet) { size_t Total {}; while (Total < packet.size()) { diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/NetStream.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/NetStream.h index 7730db6e17..a8b5a845ce 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/NetStream.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/NetStream.h @@ -11,8 +11,7 @@ namespace FEX::Utils { class NetStream final { public: - NetStream() - : receive_buffer(1500) {} + NetStream(); void OpenSocket(int _socketfd) { socketfd = _socketfd; @@ -37,12 +36,16 @@ class NetStream final { return std::get(*this); } }; + ReturnGet get(); - size_t read(char* buf, size_t size); + size_t read(char* buf, size_t size, bool ContinueOnInterrupt); + + void InterruptReader(); bool SendPacket(const fextl::string& packet); private: int socketfd {-1}; + int eventfd {-1}; size_t read_offset {}; size_t receive_offset {}; fextl::vector receive_buffer;