Skip to content

Commit

Permalink
Fix closing UdpServers
Browse files Browse the repository at this point in the history
  • Loading branch information
Dregu committed Sep 11, 2024
1 parent 03e04fc commit 0a6f418
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 24 deletions.
12 changes: 5 additions & 7 deletions src/game_api/script/usertypes/socket_lua.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,13 @@ namespace NSocket
void register_usertypes(sol::state& lua)
{
lua.new_usertype<UdpServer>(
"UdpServer",
sol::no_constructor);
"UdpServer", sol::no_constructor, "close", &UdpServer::close, "open", &UdpServer::open, "error", &UdpServer::error);

/// Start an UDP server on specified address and run callback when data arrives. Return a string from the callback to reply. Requires unsafe mode.
/// The server will be closed once the handle is released.
lua["udp_listen"] = [](std::string host, in_port_t port, sol::function cb) -> UdpServer*
/// The server will be closed lazily by garbage collection once the handle is released, or immediately by calling close().
lua["udp_listen"] = [](std::string host, in_port_t port, sol::function cb)
{
// TODO: change the return to std::unique_ptr after fixing the dead lock with the destroctor
UdpServer* server = new UdpServer(std::move(host), std::move(port), make_safe_cb<UdpServer::SocketCb>(std::move(cb)));
return server;
return std::unique_ptr<UdpServer>{new UdpServer(std::move(host), std::move(port), make_safe_cb<UdpServer::SocketCb>(std::move(cb)))};
};

/// Send data to specified UDP address. Requires unsafe mode.
Expand Down
49 changes: 33 additions & 16 deletions src/game_api/socket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <Windows.h> // for GetModuleHandleA, GetProcAddress
#include <algorithm> // for max
#include <chrono> // for chrono
#include <detours.h> // for DetourAttach, DetourTransactionBegin
#include <exception> // for exception
#include <new> // for operator new
Expand Down Expand Up @@ -56,39 +57,55 @@ void dump_network()
void udp_data(sockpp::udp_socket socket, UdpServer* server)
{
ssize_t n;
char buf[500];
char buf[1024];
sockpp::inet_address src;
while (server->kill_thr.test(std::memory_order_acquire) && (n = socket.recv_from(buf, sizeof(buf), &src)) > 0)
while (server->kill_thr.test(std::memory_order_acquire) && socket.is_open())
{
std::optional<std::string> ret = server->cb(std::string(buf, n));
if (ret)
while ((n = socket.recv_from(buf, sizeof(buf), &src)) > 0)
{
socket.send_to(ret.value(), src);
std::optional<std::string> ret = server->cb(std::string(buf, n));
if (ret)
{
socket.send_to(ret.value(), src);
}
}
std::this_thread::sleep_for(std::chrono::microseconds(1));
}
}

UdpServer::UdpServer(std::string host_, in_port_t port_, std::function<SocketCb> cb_)
: host(host_), port(port_), cb(cb_)
{
sock.bind(sockpp::inet_address(host, port));
kill_thr.test_and_set();
thr = std::thread(udp_data, std::move(sock), this);
if (sock.bind(sockpp::inet_address(host, port)))
{
is_opened = true;
sock.set_non_blocking();
kill_thr.test_and_set();
thr = std::thread(udp_data, std::move(sock), this);
}
}
void UdpServer::clear() // TODO: fix and expose: this and the destructor causes deadlock
void UdpServer::close()
{
is_closed = true;
kill_thr.clear(std::memory_order_release);
thr.join();
sock.close();
sock.shutdown();
if (thr.joinable())
thr.join();
}
bool UdpServer::open()
{
return is_opened && !is_closed && sock.last_error() == 0;
}
std::string UdpServer::error()
{
auto err = sock.error_str(sock.last_error());
return err.substr(0, err.size() - 2);
}
UdpServer::~UdpServer()
{
if (thr.joinable())
{
kill_thr.clear(std::memory_order_release);
thr.join();
}
close();
}

bool http_get(const char* sURL, std::string& out, std::string& err)
{
const int BUFFER_SIZE = 32768;
Expand Down
12 changes: 11 additions & 1 deletion src/game_api/socket.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,24 @@ class UdpServer

UdpServer(std::string host, in_port_t port, std::function<SocketCb> cb);
~UdpServer();
void clear();

/// Closes the server.
void close();

/// Returns true if the port was opened successfully and hasn't been closed yet.
bool open();

/// Returns a string explaining the last error, at least if open() returned false.
std::string error();

std::string host;
in_port_t port;
std::function<SocketCb> cb;
std::thread thr;
std::atomic_flag kill_thr;
sockpp::udp_socket sock;
bool is_opened{false};
bool is_closed{false};
};

class HttpRequest
Expand Down

0 comments on commit 0a6f418

Please sign in to comment.