Avoid exceptions
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
24
src/main.cpp
24
src/main.cpp
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
15
style.md
15
style.md
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user