Add safety comments
This commit is contained in:
136
src/main.cpp
136
src/main.cpp
@@ -31,6 +31,7 @@ void signal_handler(int sig) {
|
||||
if (sig == SIGTERM || sig == SIGINT) {
|
||||
if (shutdown_eventfd != -1) {
|
||||
uint64_t val = 1;
|
||||
// write() is async-signal-safe per POSIX - safe to use in signal handler
|
||||
if (write(shutdown_eventfd, &val, sizeof(val)) == -1) {
|
||||
abort(); // Critical failure - can't signal shutdown
|
||||
}
|
||||
@@ -281,7 +282,11 @@ int main(int argc, char *argv[]) {
|
||||
struct epoll_event shutdown_event;
|
||||
shutdown_event.events = EPOLLIN;
|
||||
shutdown_event.data.fd = shutdown_eventfd;
|
||||
epoll_ctl(network_epollfd, EPOLL_CTL_ADD, shutdown_eventfd, &shutdown_event);
|
||||
if (epoll_ctl(network_epollfd, EPOLL_CTL_ADD, shutdown_eventfd,
|
||||
&shutdown_event) == -1) {
|
||||
perror("epoll_ctl add shutdown event");
|
||||
abort();
|
||||
}
|
||||
|
||||
std::atomic<int64_t> connectionId{0};
|
||||
|
||||
@@ -289,73 +294,72 @@ int main(int argc, char *argv[]) {
|
||||
int networkThreads = config->server.network_threads;
|
||||
|
||||
for (int i = 0; i < networkThreads; ++i) {
|
||||
threads.emplace_back(
|
||||
[network_epollfd, i,
|
||||
max_request_size = config->server.max_request_size_bytes,
|
||||
event_batch_size = config->server.event_batch_size]() {
|
||||
pthread_setname_np(pthread_self(),
|
||||
("network-" + std::to_string(i)).c_str());
|
||||
std::vector<struct epoll_event> events(event_batch_size);
|
||||
for (;;) {
|
||||
int eventCount = epoll_wait(network_epollfd, events.data(),
|
||||
event_batch_size, -1 /* no timeout */);
|
||||
if (eventCount == -1) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
perror("epoll_wait");
|
||||
abort();
|
||||
}
|
||||
threads.emplace_back([network_epollfd, i,
|
||||
max_request_size =
|
||||
config->server.max_request_size_bytes,
|
||||
event_batch_size =
|
||||
config->server.event_batch_size]() {
|
||||
pthread_setname_np(pthread_self(),
|
||||
("network-" + std::to_string(i)).c_str());
|
||||
std::vector<struct epoll_event> events(event_batch_size);
|
||||
for (;;) {
|
||||
int eventCount = epoll_wait(network_epollfd, events.data(),
|
||||
event_batch_size, -1 /* no timeout */);
|
||||
if (eventCount == -1) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
perror("epoll_wait");
|
||||
abort();
|
||||
}
|
||||
|
||||
for (int i = 0; i < eventCount; ++i) {
|
||||
// Check for shutdown event
|
||||
if (events[i].data.fd == shutdown_eventfd) {
|
||||
// Don't read - let other threads see it too
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < eventCount; ++i) {
|
||||
// Check for shutdown event
|
||||
if (events[i].data.fd == shutdown_eventfd) {
|
||||
// Don't read eventfd - all threads need to see shutdown signal
|
||||
return;
|
||||
}
|
||||
|
||||
// Take ownership from epoll: raw pointer -> unique_ptr
|
||||
std::unique_ptr<Connection> conn{
|
||||
static_cast<Connection *>(events[i].data.ptr)};
|
||||
conn->tsan_acquire();
|
||||
events[i].data.ptr =
|
||||
nullptr; // Clear epoll pointer (we own it now)
|
||||
const int fd = conn->fd;
|
||||
// Take ownership from epoll: raw pointer -> unique_ptr
|
||||
std::unique_ptr<Connection> conn{
|
||||
static_cast<Connection *>(events[i].data.ptr)};
|
||||
conn->tsan_acquire();
|
||||
events[i].data.ptr = nullptr; // Clear epoll pointer (we own it now)
|
||||
const int fd = conn->fd;
|
||||
|
||||
if (events[i].events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)) {
|
||||
// Connection closed or error occurred - unique_ptr destructor
|
||||
// cleans up
|
||||
continue;
|
||||
}
|
||||
if (events[i].events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)) {
|
||||
// Connection closed or error occurred - unique_ptr destructor
|
||||
// cleans up
|
||||
continue;
|
||||
}
|
||||
|
||||
if (events[i].events & EPOLLIN) {
|
||||
conn->readBytes(max_request_size);
|
||||
}
|
||||
if (events[i].events & EPOLLIN) {
|
||||
conn->readBytes(max_request_size);
|
||||
}
|
||||
|
||||
if (events[i].events & EPOLLOUT) {
|
||||
bool done = conn->writeBytes();
|
||||
if (done) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (conn->tasks.empty()) {
|
||||
events[i].events = EPOLLIN | EPOLLONESHOT | EPOLLRDHUP;
|
||||
} else {
|
||||
events[i].events = EPOLLOUT | EPOLLONESHOT | EPOLLRDHUP;
|
||||
}
|
||||
// Transfer ownership back to epoll: unique_ptr -> raw pointer
|
||||
conn->tsan_release();
|
||||
events[i].data.ptr =
|
||||
conn.release(); // epoll now owns the connection
|
||||
int e = epoll_ctl(network_epollfd, EPOLL_CTL_MOD, fd, &events[i]);
|
||||
if (e == -1) {
|
||||
perror("epoll_ctl");
|
||||
abort(); // Process termination - OS cleans up leaked connection
|
||||
}
|
||||
if (events[i].events & EPOLLOUT) {
|
||||
bool done = conn->writeBytes();
|
||||
if (done) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (conn->tasks.empty()) {
|
||||
events[i].events = EPOLLIN | EPOLLONESHOT | EPOLLRDHUP;
|
||||
} else {
|
||||
events[i].events = EPOLLOUT | EPOLLONESHOT | EPOLLRDHUP;
|
||||
}
|
||||
// Transfer ownership back to epoll: unique_ptr -> raw pointer
|
||||
conn->tsan_release();
|
||||
events[i].data.ptr = conn.release(); // epoll now owns the connection
|
||||
int e = epoll_ctl(network_epollfd, EPOLL_CTL_MOD, fd, &events[i]);
|
||||
if (e == -1) {
|
||||
perror("epoll_ctl");
|
||||
abort(); // Connection leaked on abort() is acceptable - OS cleanup
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Accept threads from configuration
|
||||
@@ -403,7 +407,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
for (int j = 0; j < ready; ++j) {
|
||||
if (events[j].data.fd == shutdown_eventfd) {
|
||||
// Don't read - let other threads see it too
|
||||
// Don't read eventfd - all threads need to see shutdown signal
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -425,7 +429,8 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
// Check connection limit (0 means unlimited). Limiting
|
||||
// connections is best effort but it's the best we can do.
|
||||
// connections is best effort - race condition between check and
|
||||
// increment is acceptable for this use case
|
||||
if (max_connections > 0 &&
|
||||
activeConnections.load(std::memory_order_relaxed) >=
|
||||
max_connections) {
|
||||
@@ -447,14 +452,13 @@ int main(int argc, char *argv[]) {
|
||||
int e = epoll_ctl(network_epollfd, EPOLL_CTL_ADD, fd, &event);
|
||||
if (e == -1) {
|
||||
perror("epoll_ctl");
|
||||
abort();
|
||||
abort(); // Connection leaked on abort() is acceptable - OS
|
||||
// cleanup
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close(accept_epollfd);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user