From bd76e28ca67310b0561045be00d2dba8c2cb735d Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Sun, 14 Jul 2024 15:33:26 +0200 Subject: [PATCH 1/5] use sendfile to send mods on linux --- src/TNetwork.cpp | 49 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index ddecf69..f622f13 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -21,6 +21,7 @@ #include "Common.h" #include "LuaAPI.h" #include "TLuaEngine.h" +#include "TScopedTimer.h" #include "nlohmann/json.hpp" #include #include @@ -741,7 +742,8 @@ void TNetwork::SendFile(TClient& c, const std::string& UnsafeName) { return; } - size_t Size = size_t(std::filesystem::file_size(FileName)), MSize = Size / 2; + size_t Size = size_t(std::filesystem::file_size(FileName)); + size_t MSize = Size / 2; std::thread SplitThreads[2] { std::thread([&] { @@ -809,41 +811,68 @@ const uint8_t* /* end ptr */ TNetwork::SendSplit(TClient& c, ip::tcp::socket& So } } -void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std::string& Name) { +#if defined(BEAMMP_LINUX) +#include +#include +#include +#include +#endif +void TNetwork::SplitLoad(TClient& c, size_t Offset, size_t End, bool D, const std::string& Name) { + TScopedTimer timer(fmt::format("Download of {}-{} for '{}'", Offset, End, Name)); +#if defined(BEAMMP_LINUX) + // on linux, we can use sendfile(2)! + int fd = ::open(Name.c_str(), O_RDONLY); + if (fd < 0) { + beammp_errorf("Failed to open mod '{}' for sending, error: {}", Name, std::strerror(errno)); + return; + } + // native handle, needed in order to make native syscalls with it + int socket = D ? c.GetDownSock().native_handle() : c.GetTCPSock().native_handle(); + + auto SysOffset = off_t(Offset); + + ssize_t ret = sendfile(socket, fd, &SysOffset, End - Offset); + if (ret < 0) { + beammp_errorf("Failed to send mod '{}' to client {}: {}", Name, c.GetID(), std::strerror(errno)); + return; + } + +#else std::ifstream f(Name.c_str(), std::ios::binary); uint32_t Split = 125 * MB; std::vector Data; - if (Size > Split) + if (End > Split) Data.resize(Split); else - Data.resize(Size); + Data.resize(End); ip::tcp::socket* TCPSock { nullptr }; if (D) TCPSock = &c.GetDownSock(); else TCPSock = &c.GetTCPSock(); - while (!c.IsDisconnected() && Sent < Size) { - size_t Diff = Size - Sent; + while (!c.IsDisconnected() && Offset < End) { + size_t Diff = End - Offset; if (Diff > Split) { - f.seekg(Sent, std::ios_base::beg); + f.seekg(Offset, std::ios_base::beg); f.read(reinterpret_cast(Data.data()), Split); if (!TCPSendRaw(c, *TCPSock, Data.data(), Split)) { if (!c.IsDisconnected()) c.Disconnect("TCPSendRaw failed in mod download (1)"); break; } - Sent += Split; + Offset += Split; } else { - f.seekg(Sent, std::ios_base::beg); + f.seekg(Offset, std::ios_base::beg); f.read(reinterpret_cast(Data.data()), Diff); if (!TCPSendRaw(c, *TCPSock, Data.data(), int32_t(Diff))) { if (!c.IsDisconnected()) c.Disconnect("TCPSendRaw failed in mod download (2)"); break; } - Sent += Diff; + Offset += Diff; } } +#endif } bool TNetwork::TCPSendRaw(TClient& C, ip::tcp::socket& socket, const uint8_t* Data, size_t Size) { From 5baeaa72c253c5bf3cdde2b6494dc571c1252ca0 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Sun, 14 Jul 2024 16:03:08 +0200 Subject: [PATCH 2/5] clarify RAM requirements --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6037470..c59427b 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Feel free to ask any questions via the following channels: These values are guesstimated and are subject to change with each release. -* RAM: 50+ MiB usable (not counting OS overhead) +* RAM: 30-100 MiB usable (not counting OS overhead) * CPU: >1GHz, preferably multicore * OS: Windows, Linux (theoretically any POSIX) * GPU: None From b097acfd4abca9a895e93078efed8cb7fd7a749a Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Sun, 14 Jul 2024 16:17:39 +0200 Subject: [PATCH 3/5] use sendfile64 --- src/TNetwork.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index f622f13..d86a22e 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -831,7 +831,7 @@ void TNetwork::SplitLoad(TClient& c, size_t Offset, size_t End, bool D, const st auto SysOffset = off_t(Offset); - ssize_t ret = sendfile(socket, fd, &SysOffset, End - Offset); + ssize_t ret = sendfile64(socket, fd, &SysOffset, End - Offset); if (ret < 0) { beammp_errorf("Failed to send mod '{}' to client {}: {}", Name, c.GetID(), std::strerror(errno)); return; From 8b753ab6ead9dfa45a3de9727bbfa93e2a72218d Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Sun, 14 Jul 2024 17:01:08 +0200 Subject: [PATCH 4/5] ignore SIGPIPE in sendfile() implementation of mod sending --- src/TNetwork.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index d86a22e..431e859 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -816,10 +816,12 @@ const uint8_t* /* end ptr */ TNetwork::SendSplit(TClient& c, ip::tcp::socket& So #include #include #include +#include #endif void TNetwork::SplitLoad(TClient& c, size_t Offset, size_t End, bool D, const std::string& Name) { TScopedTimer timer(fmt::format("Download of {}-{} for '{}'", Offset, End, Name)); #if defined(BEAMMP_LINUX) + signal(SIGPIPE, SIG_IGN); // on linux, we can use sendfile(2)! int fd = ::open(Name.c_str(), O_RDONLY); if (fd < 0) { From 82a6d4af609013391c0a92506d96c7b5b763c77a Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Sun, 14 Jul 2024 17:01:24 +0200 Subject: [PATCH 5/5] repeat sendfile() until all data is sent --- src/TNetwork.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index 431e859..6ea08a7 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -831,12 +831,16 @@ void TNetwork::SplitLoad(TClient& c, size_t Offset, size_t End, bool D, const st // native handle, needed in order to make native syscalls with it int socket = D ? c.GetDownSock().native_handle() : c.GetTCPSock().native_handle(); - auto SysOffset = off_t(Offset); - - ssize_t ret = sendfile64(socket, fd, &SysOffset, End - Offset); - if (ret < 0) { - beammp_errorf("Failed to send mod '{}' to client {}: {}", Name, c.GetID(), std::strerror(errno)); - return; + ssize_t ret = 0; + auto ToSendTotal = End - Offset; + auto Start = Offset; + while (ret < ssize_t(ToSendTotal)) { + auto SysOffset = off_t(Start + size_t(ret)); + ret = sendfile(socket, fd, &SysOffset, ToSendTotal - size_t(ret)); + if (ret < 0) { + beammp_errorf("Failed to send mod '{}' to client {}: {}", Name, c.GetID(), std::strerror(errno)); + return; + } } #else