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
}
} }
``` ```
@@ -480,4 +483,4 @@ cmake .. -G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
- **Static analysis** tools for code quality - **Static analysis** tools for code quality
- **Address sanitizer** for memory safety testing - **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. This style guide reflects the existing codebase patterns and should be followed for all new code contributions to maintain consistency and readability.