mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2025-07-01 15:26:59 +00:00
Download Refactoring (#356)
Limits download RAM usage (to zero) on Linux by use of `sendfile(2)`
This commit is contained in:
commit
ce5f2e666d
@ -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
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "Common.h"
|
||||
#include "LuaAPI.h"
|
||||
#include "TLuaEngine.h"
|
||||
#include "TScopedTimer.h"
|
||||
#include "nlohmann/json.hpp"
|
||||
#include <CustomAssert.h>
|
||||
#include <Http.h>
|
||||
@ -785,7 +786,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([&] {
|
||||
@ -853,41 +855,74 @@ 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 <cerrno>
|
||||
#include <cstring>
|
||||
#include <sys/sendfile.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#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) {
|
||||
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();
|
||||
|
||||
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
|
||||
std::ifstream f(Name.c_str(), std::ios::binary);
|
||||
uint32_t Split = 125 * MB;
|
||||
std::vector<uint8_t> 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<char*>(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<char*>(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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user