Connection registry
Now we can use leak sanitizer. Yay!
This commit is contained in:
83
src/connection_registry.cpp
Normal file
83
src/connection_registry.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "connection_registry.hpp"
|
||||
#include "connection.hpp"
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <unistd.h>
|
||||
|
||||
ConnectionRegistry::ConnectionRegistry() : connections_(nullptr), max_fds_(0) {
|
||||
// Get the process file descriptor limit
|
||||
struct rlimit rlim;
|
||||
if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) {
|
||||
throw std::runtime_error("Failed to get RLIMIT_NOFILE");
|
||||
}
|
||||
max_fds_ = rlim.rlim_cur;
|
||||
|
||||
// Allocate virtual address space using mmap
|
||||
// This reserves virtual memory but doesn't allocate physical pages until
|
||||
// touched
|
||||
connections_ = static_cast<Connection **>(
|
||||
mmap(nullptr, max_fds_ * sizeof(Connection *), PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
|
||||
if (connections_ == MAP_FAILED) {
|
||||
throw std::runtime_error("Failed to mmap for connection registry");
|
||||
}
|
||||
|
||||
// Initialize all pointers to null
|
||||
// This will cause physical pages to be allocated on-demand
|
||||
memset(connections_, 0, max_fds_ * sizeof(Connection *));
|
||||
}
|
||||
|
||||
ConnectionRegistry::~ConnectionRegistry() {
|
||||
if (connections_ != MAP_FAILED && connections_ != nullptr) {
|
||||
munmap(connections_, max_fds_ * sizeof(Connection *));
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionRegistry::store(int fd, std::unique_ptr<Connection> connection) {
|
||||
if (fd < 0 || static_cast<size_t>(fd) >= max_fds_) {
|
||||
return; // Invalid fd - silently ignore to avoid crashes
|
||||
}
|
||||
// Release ownership from unique_ptr and store raw pointer
|
||||
connections_[fd] = connection.release();
|
||||
}
|
||||
|
||||
bool ConnectionRegistry::has(int fd) const {
|
||||
if (fd < 0 || static_cast<size_t>(fd) >= max_fds_) {
|
||||
return false; // Invalid fd
|
||||
}
|
||||
return connections_[fd] != nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Connection> ConnectionRegistry::remove(int fd) {
|
||||
if (fd < 0 || static_cast<size_t>(fd) >= max_fds_) {
|
||||
return nullptr; // Invalid fd
|
||||
}
|
||||
|
||||
Connection *conn = connections_[fd];
|
||||
connections_[fd] = nullptr;
|
||||
// Transfer ownership back to unique_ptr
|
||||
return std::unique_ptr<Connection>(conn);
|
||||
}
|
||||
|
||||
void ConnectionRegistry::shutdown_cleanup() {
|
||||
// Iterate through all possible file descriptors and clean up any connections
|
||||
// Following the critical ordering: remove -> delete (destructor handles
|
||||
// close)
|
||||
size_t connections_found = 0;
|
||||
for (size_t fd = 0; fd < max_fds_; ++fd) {
|
||||
Connection *conn = connections_[fd];
|
||||
if (conn != nullptr) {
|
||||
connections_found++;
|
||||
// Step 1: Remove from registry (set to null)
|
||||
connections_[fd] = nullptr;
|
||||
|
||||
// Steps 2 & 3: Delete the connection object (destructor handles closing
|
||||
// fd)
|
||||
delete conn;
|
||||
}
|
||||
}
|
||||
|
||||
// Note: In normal shutdown, this should be 0 since all connections
|
||||
// should have been properly cleaned up during normal operation
|
||||
}
|
||||
Reference in New Issue
Block a user