Fix some issues with transferring conn back to server

This commit is contained in:
2025-08-22 10:51:24 -04:00
parent 1d86f48d5e
commit ba3258ab16
4 changed files with 33 additions and 22 deletions

View File

@@ -9,18 +9,19 @@
std::unique_ptr<Connection> std::unique_ptr<Connection>
Connection::createForServer(struct sockaddr_storage addr, int fd, int64_t id, Connection::createForServer(struct sockaddr_storage addr, int fd, int64_t id,
ConnectionHandler *handler, size_t epoll_index, ConnectionHandler *handler,
std::weak_ptr<Server> server) { std::weak_ptr<Server> server) {
// Use unique_ptr constructor with private access (friend function) // Use unique_ptr constructor with private access (friend function)
// We can't use make_unique here because constructor is private // We can't use make_unique here because constructor is private
return std::unique_ptr<Connection>( return std::unique_ptr<Connection>(
new Connection(addr, fd, id, handler, server)); new Connection(addr, fd, id, epoll_index, handler, server));
} }
Connection::Connection(struct sockaddr_storage addr, int fd, int64_t id, Connection::Connection(struct sockaddr_storage addr, int fd, int64_t id,
ConnectionHandler *handler, std::weak_ptr<Server> server) size_t epoll_index, ConnectionHandler *handler,
: fd_(fd), id_(id), addr_(addr), arena_(), handler_(handler), std::weak_ptr<Server> server)
server_(server) { : fd_(fd), id_(id), epoll_index_(epoll_index), addr_(addr), arena_(),
handler_(handler), server_(server) {
if (auto server_ptr = server_.lock()) { if (auto server_ptr = server_.lock()) {
server_ptr->active_connections_.fetch_add(1, std::memory_order_relaxed); server_ptr->active_connections_.fetch_add(1, std::memory_order_relaxed);
} }

View File

@@ -323,7 +323,8 @@ private:
* @param server Weak reference to the server for safe cleanup * @param server Weak reference to the server for safe cleanup
*/ */
Connection(struct sockaddr_storage addr, int fd, int64_t id, Connection(struct sockaddr_storage addr, int fd, int64_t id,
ConnectionHandler *handler, std::weak_ptr<Server> server); size_t epoll_index, ConnectionHandler *handler,
std::weak_ptr<Server> server);
/** /**
* @brief Server-only factory method for creating connections. * @brief Server-only factory method for creating connections.
@@ -345,7 +346,8 @@ private:
*/ */
static std::unique_ptr<Connection> static std::unique_ptr<Connection>
createForServer(struct sockaddr_storage addr, int fd, int64_t id, createForServer(struct sockaddr_storage addr, int fd, int64_t id,
ConnectionHandler *handler, std::weak_ptr<Server> server); size_t epoll_index, ConnectionHandler *handler,
std::weak_ptr<Server> server);
// Networking interface - only accessible by Server // Networking interface - only accessible by Server
int readBytes(char *buf, size_t buffer_size); int readBytes(char *buf, size_t buffer_size);
@@ -357,8 +359,10 @@ private:
int getFd() const { return fd_; } int getFd() const { return fd_; }
bool hasMessages() const { return !messages_.empty(); } bool hasMessages() const { return !messages_.empty(); }
bool shouldClose() const { return closeConnection_; } bool shouldClose() const { return closeConnection_; }
size_t getEpollIndex() const { return epoll_index_; }
const int fd_; const int fd_;
const int64_t id_; const int64_t id_;
const size_t epoll_index_; // Index of the epoll instance this connection uses
struct sockaddr_storage addr_; // sockaddr_storage handles IPv4/IPv6 struct sockaddr_storage addr_; // sockaddr_storage handles IPv4/IPv6
ArenaAllocator arena_; ArenaAllocator arena_;
ConnectionHandler *handler_; ConnectionHandler *handler_;

View File

@@ -100,16 +100,19 @@ void Server::releaseBackToServer(std::unique_ptr<Connection> connection) {
// Try to get the server from the connection's weak_ptr // Try to get the server from the connection's weak_ptr
if (auto server = connection->server_.lock()) { if (auto server = connection->server_.lock()) {
// Server still exists - release raw pointer and let server take over // Server still exists - pass unique_ptr directly
Connection *raw_conn = connection.release(); server->receiveConnectionBack(std::move(connection));
server->receiveConnectionBack(raw_conn);
} }
// If server is gone, connection will be automatically cleaned up when // If server is gone, connection will be automatically cleaned up when
// unique_ptr destructs // unique_ptr destructs
} }
void Server::receiveConnectionBack(Connection *connection) { void Server::receiveConnectionBack(std::unique_ptr<Connection> connection) {
if (!connection) {
return; // Nothing to process
}
// Re-add the connection to epoll for continued processing // Re-add the connection to epoll for continued processing
struct epoll_event event{}; struct epoll_event event{};
@@ -120,17 +123,19 @@ void Server::receiveConnectionBack(Connection *connection) {
} }
connection->tsan_release(); connection->tsan_release();
event.data.fd = connection->getFd(); int fd = connection->getFd();
event.data.fd = fd;
// Distribute connections round-robin across epoll instances // Store connection in registry before adding to epoll
size_t epoll_index = // This mirrors the pattern used in process_connection_batch
connection_distribution_counter_.fetch_add(1, std::memory_order_relaxed) % size_t epoll_index = connection->getEpollIndex();
epoll_fds_.size();
int epollfd = epoll_fds_[epoll_index]; int epollfd = epoll_fds_[epoll_index];
connection_registry_.store(fd, std::move(connection));
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, connection->getFd(), &event) == -1) { if (epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event) == -1) {
perror("epoll_ctl ADD in receiveConnectionBack"); perror("epoll_ctl MOD in receiveConnectionBack");
delete connection; // Clean up on failure // Remove from registry and clean up on failure
(void)connection_registry_.remove(fd);
} }
} }
@@ -417,10 +422,11 @@ void Server::start_io_threads(std::vector<std::thread> &threads) {
} }
// Transfer ownership from registry to batch processing // Transfer ownership from registry to batch processing
size_t epoll_index = thread_id % epoll_fds_.size();
batch[batch_count] = std::unique_ptr<Connection>(new Connection( batch[batch_count] = std::unique_ptr<Connection>(new Connection(
addr, fd, addr, fd,
connection_id_.fetch_add(1, std::memory_order_relaxed), connection_id_.fetch_add(1, std::memory_order_relaxed),
&handler_, weak_from_this())); epoll_index, &handler_, weak_from_this()));
batch_events[batch_count] = batch_events[batch_count] =
EPOLLIN; // New connections always start with read EPOLLIN; // New connections always start with read
batch_count++; batch_count++;

View File

@@ -142,9 +142,9 @@ private:
* This method is thread-safe and can be called from any thread. * This method is thread-safe and can be called from any thread.
* The connection will be re-added to the epoll for continued processing. * The connection will be re-added to the epoll for continued processing.
* *
* @param connection Raw pointer to the connection being released back * @param connection Unique pointer to the connection being released back
*/ */
void receiveConnectionBack(Connection *connection); void receiveConnectionBack(std::unique_ptr<Connection> connection);
// Make non-copyable and non-movable // Make non-copyable and non-movable
Server(const Server &) = delete; Server(const Server &) = delete;