From 2754f4cbe236d615577c2fe9622e22ad115a6d30 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Sat, 23 Aug 2025 12:56:28 -0400 Subject: [PATCH] Avoid exceptions --- src/connection_registry.cpp | 8 +++++--- src/connection_registry.hpp | 2 +- src/main.cpp | 24 ++++++++++-------------- src/server.cpp | 33 ++++++++++++++++----------------- src/server.hpp | 2 +- style.md | 15 +++++++++------ 6 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/connection_registry.cpp b/src/connection_registry.cpp index 5ff013f..19037da 100644 --- a/src/connection_registry.cpp +++ b/src/connection_registry.cpp @@ -1,15 +1,16 @@ #include "connection_registry.hpp" #include "connection.hpp" #include +#include #include -#include #include 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"); + perror("getrlimit"); + std::abort(); } max_fds_ = rlim.rlim_cur; @@ -25,7 +26,8 @@ ConnectionRegistry::ConnectionRegistry() : connections_(nullptr), max_fds_(0) { MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); if (connections_ == MAP_FAILED) { - throw std::runtime_error("Failed to mmap for connection registry"); + perror("mmap"); + std::abort(); } // Store aligned size for munmap diff --git a/src/connection_registry.hpp b/src/connection_registry.hpp index 12f6c6f..03ae0ef 100644 --- a/src/connection_registry.hpp +++ b/src/connection_registry.hpp @@ -22,7 +22,7 @@ public: * Initialize the connection registry. * 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(); diff --git a/src/main.cpp b/src/main.cpp index 7a686f7..336642f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -37,7 +37,7 @@ std::vector create_listen_sockets(const weaseldb::Config &config) { int sfd = socket(AF_UNIX, SOCK_STREAM, 0); if (sfd == -1) { perror("socket"); - throw std::runtime_error("Failed to create unix socket"); + std::abort(); } // Remove existing socket file if it exists @@ -49,7 +49,8 @@ std::vector create_listen_sockets(const weaseldb::Config &config) { if (config.server.unix_socket_path.length() >= sizeof(addr.sun_path)) { 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(), @@ -58,13 +59,13 @@ std::vector create_listen_sockets(const weaseldb::Config &config) { if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { perror("bind"); close(sfd); - throw std::runtime_error("Failed to bind unix socket"); + std::abort(); } if (listen(sfd, SOMAXCONN) == -1) { perror("listen"); close(sfd); - throw std::runtime_error("Failed to listen on unix socket"); + std::abort(); } listen_fds.push_back(sfd); @@ -89,7 +90,7 @@ std::vector create_listen_sockets(const weaseldb::Config &config) { std::to_string(config.server.port).c_str(), &hints, &result); if (s != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s)); - throw std::runtime_error("Failed to resolve bind address"); + std::abort(); } int sfd = -1; @@ -126,13 +127,14 @@ std::vector create_listen_sockets(const weaseldb::Config &config) { freeaddrinfo(result); 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) { perror("listen"); close(sfd); - throw std::runtime_error("Failed to listen on socket"); + std::abort(); } listen_fds.push_back(sfd); @@ -236,13 +238,7 @@ int main(int argc, char *argv[]) { << std::endl; // Create listen sockets - std::vector listen_fds; - 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; - } + std::vector listen_fds = create_listen_sockets(*config); // Create handler and server HttpHandler http_handler; diff --git a/src/server.cpp b/src/server.cpp index 1da047b..f38c13d 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -39,12 +38,12 @@ Server::Server(const weaseldb::Config &config, ConnectionHandler &handler, for (int fd : listen_fds_) { int flags = fcntl(fd, F_GETFL, 0); if (flags == -1) { - perror("fcntl F_GETFL on provided listen fd"); - throw std::runtime_error("Failed to get flags for provided listen fd"); + perror("fcntl F_GETFL"); + std::abort(); } if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { - perror("fcntl F_SETFL O_NONBLOCK on provided listen fd"); - throw std::runtime_error("Failed to set provided listen fd non-blocking"); + perror("fcntl F_SETFL O_NONBLOCK"); + std::abort(); } } @@ -179,14 +178,14 @@ int Server::createLocalConnection() { int flags = fcntl(server_fd, F_GETFL, 0); if (flags == -1) { - perror("fcntl F_GETFL on provided listen fd"); - throw std::runtime_error( - "Failed to get flags for server side of local connection"); + std::fprintf(stderr, + "Server::createLocalConnection: fcntl F_GETFL failed\n"); + std::abort(); } if (fcntl(server_fd, F_SETFL, flags | O_NONBLOCK) == -1) { - perror("fcntl F_SETFL O_NONBLOCK on provided listen fd"); - throw std::runtime_error( - "Failed to set server side of local connection to non-blocking"); + std::fprintf(stderr, + "Server::createLocalConnection: fcntl F_SETFL failed\n"); + std::abort(); } // Create sockaddr_storage for the connection @@ -226,14 +225,14 @@ int Server::createLocalConnection() { void Server::setup_shutdown_pipe() { if (pipe(shutdown_pipe_) == -1) { perror("pipe"); - throw std::runtime_error("Failed to create shutdown pipe"); + std::abort(); } // Set both ends to close-on-exec if (fcntl(shutdown_pipe_[0], F_SETFD, FD_CLOEXEC) == -1 || fcntl(shutdown_pipe_[1], F_SETFD, FD_CLOEXEC) == -1) { 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) { epoll_fds_[i] = epoll_create1(EPOLL_CLOEXEC); if (epoll_fds_[i] == -1) { - perror("epoll_create"); - throw std::runtime_error("Failed to create epoll instance"); + perror("epoll_create1"); + std::abort(); } // 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], &shutdown_event) == -1) { 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 @@ -268,7 +267,7 @@ void Server::create_epoll_instances() { if (epoll_ctl(epoll_fds_[i], EPOLL_CTL_ADD, listen_fd, &listen_event) == -1) { perror("epoll_ctl listen socket"); - throw std::runtime_error("Failed to add listen socket to epoll"); + std::abort(); } } } diff --git a/src/server.hpp b/src/server.hpp index 62a2164..d69f490 100644 --- a/src/server.hpp +++ b/src/server.hpp @@ -66,7 +66,7 @@ struct Server : std::enable_shared_from_this { * - Starts all worker threads * - 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(); diff --git a/style.md b/style.md index 52ebe2b..48f6251 100644 --- a/style.md +++ b/style.md @@ -301,8 +301,7 @@ arena.reset(); // Reset arena memory ### Error Reporting - **Return codes** for expected errors -- **Exceptions** only for exceptional circumstances -- **fprintf + abort()** for unrecoverable errors +- **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. - **Error messages are for humans only** - never parse error message strings programmatically - **Error codes are the contract** - use enums/codes for programmatic error handling ```cpp @@ -317,9 +316,13 @@ if (result == ParseResult::InvalidJson) { // Bad: Don't test or parse error message strings // CHECK(parser.get_error() == "Expected '}' at line 5"); // BRITTLE! -if (!memory) { - std::fprintf(stderr, "ArenaAllocator: Failed to allocate memory\n"); - std::abort(); +// System resource failures: abort immediately +void ArenaAllocator::allocate() { + void* memory = malloc(size); + if (!memory) { + perror("malloc"); + std::abort(); // Process is likely in bad state + } } ``` @@ -480,4 +483,4 @@ cmake .. -G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON - **Static analysis** tools for code quality - **Address sanitizer** for memory safety testing -This style guide reflects the existing codebase patterns and should be followed for all new code contributions to maintain consistency and readability. \ No newline at end of file +This style guide reflects the existing codebase patterns and should be followed for all new code contributions to maintain consistency and readability.