Avoid exceptions

This commit is contained in:
2025-08-23 12:56:28 -04:00
parent 25ff489857
commit 2754f4cbe2
6 changed files with 42 additions and 42 deletions

View File

@@ -1,15 +1,16 @@
#include "connection_registry.hpp" #include "connection_registry.hpp"
#include "connection.hpp" #include "connection.hpp"
#include <atomic> #include <atomic>
#include <cstdlib>
#include <cstring> #include <cstring>
#include <stdexcept>
#include <unistd.h> #include <unistd.h>
ConnectionRegistry::ConnectionRegistry() : connections_(nullptr), max_fds_(0) { ConnectionRegistry::ConnectionRegistry() : connections_(nullptr), max_fds_(0) {
// Get the process file descriptor limit // Get the process file descriptor limit
struct rlimit rlim; struct rlimit rlim;
if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) { if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) {
throw std::runtime_error("Failed to get RLIMIT_NOFILE"); perror("getrlimit");
std::abort();
} }
max_fds_ = rlim.rlim_cur; max_fds_ = rlim.rlim_cur;
@@ -25,7 +26,8 @@ ConnectionRegistry::ConnectionRegistry() : connections_(nullptr), max_fds_(0) {
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
if (connections_ == MAP_FAILED) { if (connections_ == MAP_FAILED) {
throw std::runtime_error("Failed to mmap for connection registry"); perror("mmap");
std::abort();
} }
// Store aligned size for munmap // Store aligned size for munmap

View File

@@ -22,7 +22,7 @@ public:
* Initialize the connection registry. * Initialize the connection registry.
* Allocates virtual address space based on RLIMIT_NOFILE. * Allocates virtual address space based on RLIMIT_NOFILE.
* *
* @throws std::runtime_error if mmap fails or RLIMIT_NOFILE cannot be read * Aborts the process if mmap fails or RLIMIT_NOFILE cannot be read
*/ */
ConnectionRegistry(); ConnectionRegistry();

View File

@@ -37,7 +37,7 @@ std::vector<int> create_listen_sockets(const weaseldb::Config &config) {
int sfd = socket(AF_UNIX, SOCK_STREAM, 0); int sfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sfd == -1) { if (sfd == -1) {
perror("socket"); perror("socket");
throw std::runtime_error("Failed to create unix socket"); std::abort();
} }
// Remove existing socket file if it exists // Remove existing socket file if it exists
@@ -49,7 +49,8 @@ std::vector<int> create_listen_sockets(const weaseldb::Config &config) {
if (config.server.unix_socket_path.length() >= sizeof(addr.sun_path)) { if (config.server.unix_socket_path.length() >= sizeof(addr.sun_path)) {
close(sfd); close(sfd);
throw std::runtime_error("Unix socket path too long"); std::fprintf(stderr, "Unix socket path too long\n");
std::abort();
} }
strncpy(addr.sun_path, config.server.unix_socket_path.c_str(), strncpy(addr.sun_path, config.server.unix_socket_path.c_str(),
@@ -58,13 +59,13 @@ std::vector<int> create_listen_sockets(const weaseldb::Config &config) {
if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
perror("bind"); perror("bind");
close(sfd); close(sfd);
throw std::runtime_error("Failed to bind unix socket"); std::abort();
} }
if (listen(sfd, SOMAXCONN) == -1) { if (listen(sfd, SOMAXCONN) == -1) {
perror("listen"); perror("listen");
close(sfd); close(sfd);
throw std::runtime_error("Failed to listen on unix socket"); std::abort();
} }
listen_fds.push_back(sfd); listen_fds.push_back(sfd);
@@ -89,7 +90,7 @@ std::vector<int> create_listen_sockets(const weaseldb::Config &config) {
std::to_string(config.server.port).c_str(), &hints, &result); std::to_string(config.server.port).c_str(), &hints, &result);
if (s != 0) { if (s != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s)); fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
throw std::runtime_error("Failed to resolve bind address"); std::abort();
} }
int sfd = -1; int sfd = -1;
@@ -126,13 +127,14 @@ std::vector<int> create_listen_sockets(const weaseldb::Config &config) {
freeaddrinfo(result); freeaddrinfo(result);
if (rp == nullptr || sfd == -1) { if (rp == nullptr || sfd == -1) {
throw std::runtime_error("Could not bind to any address"); std::fprintf(stderr, "Could not bind to any address\n");
std::abort();
} }
if (listen(sfd, SOMAXCONN) == -1) { if (listen(sfd, SOMAXCONN) == -1) {
perror("listen"); perror("listen");
close(sfd); close(sfd);
throw std::runtime_error("Failed to listen on socket"); std::abort();
} }
listen_fds.push_back(sfd); listen_fds.push_back(sfd);
@@ -236,13 +238,7 @@ int main(int argc, char *argv[]) {
<< std::endl; << std::endl;
// Create listen sockets // Create listen sockets
std::vector<int> listen_fds; std::vector<int> listen_fds = create_listen_sockets(*config);
try {
listen_fds = create_listen_sockets(*config);
} catch (const std::exception &e) {
std::cerr << "Failed to create listen sockets: " << e.what() << std::endl;
return 1;
}
// Create handler and server // Create handler and server
HttpHandler http_handler; HttpHandler http_handler;

View File

@@ -9,7 +9,6 @@
#include <netdb.h> #include <netdb.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <pthread.h> #include <pthread.h>
#include <stdexcept>
#include <sys/epoll.h> #include <sys/epoll.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h> #include <sys/un.h>
@@ -39,12 +38,12 @@ Server::Server(const weaseldb::Config &config, ConnectionHandler &handler,
for (int fd : listen_fds_) { for (int fd : listen_fds_) {
int flags = fcntl(fd, F_GETFL, 0); int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1) { if (flags == -1) {
perror("fcntl F_GETFL on provided listen fd"); perror("fcntl F_GETFL");
throw std::runtime_error("Failed to get flags for provided listen fd"); std::abort();
} }
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
perror("fcntl F_SETFL O_NONBLOCK on provided listen fd"); perror("fcntl F_SETFL O_NONBLOCK");
throw std::runtime_error("Failed to set provided listen fd non-blocking"); std::abort();
} }
} }
@@ -179,14 +178,14 @@ int Server::createLocalConnection() {
int flags = fcntl(server_fd, F_GETFL, 0); int flags = fcntl(server_fd, F_GETFL, 0);
if (flags == -1) { if (flags == -1) {
perror("fcntl F_GETFL on provided listen fd"); std::fprintf(stderr,
throw std::runtime_error( "Server::createLocalConnection: fcntl F_GETFL failed\n");
"Failed to get flags for server side of local connection"); std::abort();
} }
if (fcntl(server_fd, F_SETFL, flags | O_NONBLOCK) == -1) { if (fcntl(server_fd, F_SETFL, flags | O_NONBLOCK) == -1) {
perror("fcntl F_SETFL O_NONBLOCK on provided listen fd"); std::fprintf(stderr,
throw std::runtime_error( "Server::createLocalConnection: fcntl F_SETFL failed\n");
"Failed to set server side of local connection to non-blocking"); std::abort();
} }
// Create sockaddr_storage for the connection // Create sockaddr_storage for the connection
@@ -226,14 +225,14 @@ int Server::createLocalConnection() {
void Server::setup_shutdown_pipe() { void Server::setup_shutdown_pipe() {
if (pipe(shutdown_pipe_) == -1) { if (pipe(shutdown_pipe_) == -1) {
perror("pipe"); perror("pipe");
throw std::runtime_error("Failed to create shutdown pipe"); std::abort();
} }
// Set both ends to close-on-exec // Set both ends to close-on-exec
if (fcntl(shutdown_pipe_[0], F_SETFD, FD_CLOEXEC) == -1 || if (fcntl(shutdown_pipe_[0], F_SETFD, FD_CLOEXEC) == -1 ||
fcntl(shutdown_pipe_[1], F_SETFD, FD_CLOEXEC) == -1) { fcntl(shutdown_pipe_[1], F_SETFD, FD_CLOEXEC) == -1) {
perror("fcntl FD_CLOEXEC"); perror("fcntl FD_CLOEXEC");
throw std::runtime_error("Failed to set close-on-exec for shutdown pipe"); std::abort();
} }
} }
@@ -244,8 +243,8 @@ void Server::create_epoll_instances() {
for (int i = 0; i < config_.server.epoll_instances; ++i) { for (int i = 0; i < config_.server.epoll_instances; ++i) {
epoll_fds_[i] = epoll_create1(EPOLL_CLOEXEC); epoll_fds_[i] = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fds_[i] == -1) { if (epoll_fds_[i] == -1) {
perror("epoll_create"); perror("epoll_create1");
throw std::runtime_error("Failed to create epoll instance"); std::abort();
} }
// Add shutdown pipe to each epoll instance // Add shutdown pipe to each epoll instance
@@ -256,7 +255,7 @@ void Server::create_epoll_instances() {
if (epoll_ctl(epoll_fds_[i], EPOLL_CTL_ADD, shutdown_pipe_[0], if (epoll_ctl(epoll_fds_[i], EPOLL_CTL_ADD, shutdown_pipe_[0],
&shutdown_event) == -1) { &shutdown_event) == -1) {
perror("epoll_ctl shutdown pipe"); perror("epoll_ctl shutdown pipe");
throw std::runtime_error("Failed to add shutdown pipe to epoll"); std::abort();
} }
// Add all listen sockets to each epoll instance with EPOLLEXCLUSIVE to // Add all listen sockets to each epoll instance with EPOLLEXCLUSIVE to
@@ -268,7 +267,7 @@ void Server::create_epoll_instances() {
if (epoll_ctl(epoll_fds_[i], EPOLL_CTL_ADD, listen_fd, &listen_event) == if (epoll_ctl(epoll_fds_[i], EPOLL_CTL_ADD, listen_fd, &listen_event) ==
-1) { -1) {
perror("epoll_ctl listen socket"); perror("epoll_ctl listen socket");
throw std::runtime_error("Failed to add listen socket to epoll"); std::abort();
} }
} }
} }

View File

@@ -66,7 +66,7 @@ struct Server : std::enable_shared_from_this<Server> {
* - Starts all worker threads * - Starts all worker threads
* - Blocks until shutdown() is called or an error occurs * - Blocks until shutdown() is called or an error occurs
* *
* @throws std::runtime_error on socket creation or configuration errors * Aborts the process on socket creation or configuration errors
*/ */
void run(); void run();

View File

@@ -301,8 +301,7 @@ arena.reset(); // Reset arena memory
### Error Reporting ### Error Reporting
- **Return codes** for expected errors - **Return codes** for expected errors
- **Exceptions** only for exceptional circumstances - **Avoid exceptions** - If we can't uphold the component's contract, perror/fprintf then abort. If we want to try to recover, change the component's contract to allow returning an error code.
- **fprintf + abort()** for unrecoverable errors
- **Error messages are for humans only** - never parse error message strings programmatically - **Error messages are for humans only** - never parse error message strings programmatically
- **Error codes are the contract** - use enums/codes for programmatic error handling - **Error codes are the contract** - use enums/codes for programmatic error handling
```cpp ```cpp
@@ -317,9 +316,13 @@ if (result == ParseResult::InvalidJson) {
// Bad: Don't test or parse error message strings // Bad: Don't test or parse error message strings
// CHECK(parser.get_error() == "Expected '}' at line 5"); // BRITTLE! // CHECK(parser.get_error() == "Expected '}' at line 5"); // BRITTLE!
if (!memory) { // System resource failures: abort immediately
std::fprintf(stderr, "ArenaAllocator: Failed to allocate memory\n"); void ArenaAllocator::allocate() {
std::abort(); void* memory = malloc(size);
if (!memory) {
perror("malloc");
std::abort(); // Process is likely in bad state
}
} }
``` ```