slightly more robust error handling

This commit is contained in:
Lily Tsuru 2024-02-02 20:26:27 -05:00
parent 8196f711bc
commit ee1ef5906d
3 changed files with 71 additions and 55 deletions

View File

@ -8,12 +8,15 @@ namespace nanosm {
epollFd = epoll_create1(EPOLL_CLOEXEC); epollFd = epoll_create1(EPOLL_CLOEXEC);
if(epollFd == -1) { if(epollFd == -1) {
perror("You Banned From Epoll, Rules"); throw std::system_error(std::make_error_code(static_cast<std::errc>(errno)));
return;
} }
} }
EventLoop::~EventLoop() { EventLoop::~EventLoop() {
close(epollFd); if(epollFd == -1) {
close(epollFd);
}
} }
void EventLoop::Post(PostFn func) { void EventLoop::Post(PostFn func) {
@ -56,25 +59,27 @@ namespace nanosm {
UniqueArray<epoll_event> events { 16 }; UniqueArray<epoll_event> events { 16 };
while(!shouldStop) { while(!shouldStop) {
auto nevents = epoll_wait(epollFd, events.Get(), events.Size(), 10); // The timeout here is larger than it probably would be if e.g: this was dealing with sockets,
if(nevents == -1) { // mostly because nanosm is designed to only really use CPU if it has to (i.e: a process died, timer elapsed, etc)
perror("epoll_wait"); auto nrEvents = epoll_wait(epollFd, events.Get(), events.Size(), 50);
break; if(nrEvents == -1) {
throw std::system_error(std::make_error_code(static_cast<std::errc>(errno)));
} }
// All OK, let's check for events now if(nrEvents != 0) {
for(int i = 0; i < nrEvents; ++i) {
for(int i = 0; i < nevents; ++i) { for(auto pollable : ioObjects) {
for(auto pollable : ioObjects) { if(auto fd = pollable->GetFD(); fd != -1) {
if(auto fd = pollable->GetFD(); fd != -1) { // Signal any events that occur for this
// Signal any events that occur for this if(events[i].data.fd == fd)
if(events[i].data.fd == fd) pollable->OnReady(events[i].events);
pollable->OnReady(events[i].events); }
} }
} }
} }
// Run the topmost callback once every event loop iteration. // Run the topmost callback once every event loop iteration,
// even if epoll never signaled readiness.
if(!postCallbacks.empty()) { if(!postCallbacks.empty()) {
auto& frontCallback = postCallbacks.front(); auto& frontCallback = postCallbacks.front();
frontCallback(); frontCallback();

View File

@ -7,8 +7,9 @@ namespace nanosm {
Timer::Timer(nanosm::EventLoop& ev) Timer::Timer(nanosm::EventLoop& ev)
: nanosm::EventLoop::IoObject(ev) { : nanosm::EventLoop::IoObject(ev) {
timerFd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); timerFd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
if(timerFd < 0) if(timerFd < 0) {
perror("timerfd_create"); throw std::system_error(std::make_error_code(static_cast<std::errc>(errno)));
}
} }
Timer::~Timer() { Timer::~Timer() {

View File

@ -2,6 +2,7 @@
#include <format> #include <format>
#include <map> #include <map>
#include <memory> #include <memory>
#include <system_error>
#include <toml++/toml.hpp> #include <toml++/toml.hpp>
#include "EventLoop.hpp" #include "EventLoop.hpp"
@ -82,7 +83,12 @@ struct NanoSm {
this->SpawnAllApps(); this->SpawnAllApps();
}); });
ev.Run(); try {
ev.Run();
} catch(std::exception& ec) {
nanosm::log::Error("NanoSm", "Unhandled exception in event loop: {}", ec.what());
return 1;
}
return 0; return 0;
} }
@ -105,51 +111,55 @@ fs::path GetConfigPath() {
} }
int main(int argc, char** argv) { int main(int argc, char** argv) {
NanoSm nanosm;
auto configPath = GetConfigPath();
try { try {
auto tomlTable = toml::parse_file(configPath.string()); NanoSm nanosm;
if(tomlTable["nanosm"].is_table() && tomlTable["nanosm"]["apps"].is_table()) { auto configPath = GetConfigPath();
auto restartptr = tomlTable["nanosm"]["restart-time"].as_integer();
if(!restartptr) { try {
nanosm::log::Warn("NanoSm", "No restart time provided in nanosm.toml. Defaulting to 1 second."); auto tomlTable = toml::parse_file(configPath.string());
nanosm.SetRestartTime(1);
if(tomlTable["nanosm"].is_table() && tomlTable["nanosm"]["apps"].is_table()) {
auto restartptr = tomlTable["nanosm"]["restart-time"].as_integer();
if(!restartptr) {
nanosm::log::Warn("NanoSm", "No restart time provided in nanosm.toml. Defaulting to 1 second.");
nanosm.SetRestartTime(1);
} else {
nanosm.SetRestartTime(static_cast<u32>(restartptr->get()));
}
for(auto [key, value] : *tomlTable["nanosm"]["apps"].as_table()) {
if(!value.is_table()) {
nanosm::log::Warn("NanoSm", "Ignoring invalid app \"{}\" in configuration file", key.str());
continue;
}
auto& appTable = *value.as_table();
if(!appTable.get("command")->is_string()) {
nanosm::log::Warn("NanoSm", "Ignoring invalid app \"{}\" in configuration file", key.str());
continue;
}
auto cmd = appTable.get("command")->as_string()->get();
nanosm::log::Info("NanoSm", "Adding app \"{}\" with command \"{}\"", key.str(), cmd);
nanosm.AddApp(std::string(key.data(), key.length()), std::string(cmd.data(), cmd.length()));
}
} else { } else {
nanosm.SetRestartTime(static_cast<u32>(restartptr->get())); nanosm::log::Error("NanoSm", "Configuration file {} doesn't have required values. Exiting with failure.", configPath.string());
return 1;
} }
for(auto [key, value] : *tomlTable["nanosm"]["apps"].as_table()) { return nanosm.Run();
if(!value.is_table()) { } catch(toml::parse_error& pe) {
nanosm::log::Warn("NanoSm", "Ignoring invalid app \"{}\" in configuration file", key.str()); std::fprintf(stderr, "Error parsing config file \"%s\": %s\n", configPath.c_str(), pe.description().data());
continue;
}
auto& appTable = *value.as_table();
if(!appTable.get("command")->is_string()) {
nanosm::log::Warn("NanoSm", "Ignoring invalid app \"{}\" in configuration file", key.str());
continue;
}
auto cmd = appTable.get("command")->as_string()->get();
nanosm::log::Info("NanoSm", "Adding app \"{}\" with command \"{}\"", key.str(), cmd);
nanosm.AddApp(std::string(key.data(), key.length()), std::string(cmd.data(), cmd.length()));
}
} else {
nanosm::log::Error("NanoSm", "Configuration file {} doesn't have required values. Exiting with failure.", configPath.string());
return 1; return 1;
} }
} catch(std::exception& ec) {
} catch(toml::parse_error& pe) { nanosm::log::Error("NanoSm", "Error initalizing NanoSm: {}", ec.what());
std::fprintf(stderr, "Error parsing config file \"%s\": %s\n", configPath.c_str(), pe.description().data());
return 1; return 1;
} }
return nanosm.Run();
} }