diff --git a/src/EventLoop.cpp b/src/EventLoop.cpp index bde4652..32274cc 100644 --- a/src/EventLoop.cpp +++ b/src/EventLoop.cpp @@ -8,12 +8,15 @@ namespace nanosm { epollFd = epoll_create1(EPOLL_CLOEXEC); if(epollFd == -1) { - perror("You Banned From Epoll, Rules"); + throw std::system_error(std::make_error_code(static_cast(errno))); + return; } } EventLoop::~EventLoop() { - close(epollFd); + if(epollFd == -1) { + close(epollFd); + } } void EventLoop::Post(PostFn func) { @@ -56,25 +59,27 @@ namespace nanosm { UniqueArray events { 16 }; while(!shouldStop) { - auto nevents = epoll_wait(epollFd, events.Get(), events.Size(), 10); - if(nevents == -1) { - perror("epoll_wait"); - break; + // The timeout here is larger than it probably would be if e.g: this was dealing with sockets, + // mostly because nanosm is designed to only really use CPU if it has to (i.e: a process died, timer elapsed, etc) + auto nrEvents = epoll_wait(epollFd, events.Get(), events.Size(), 50); + if(nrEvents == -1) { + throw std::system_error(std::make_error_code(static_cast(errno))); } - // All OK, let's check for events now - - for(int i = 0; i < nevents; ++i) { - for(auto pollable : ioObjects) { - if(auto fd = pollable->GetFD(); fd != -1) { - // Signal any events that occur for this - if(events[i].data.fd == fd) - pollable->OnReady(events[i].events); + if(nrEvents != 0) { + for(int i = 0; i < nrEvents; ++i) { + for(auto pollable : ioObjects) { + if(auto fd = pollable->GetFD(); fd != -1) { + // Signal any events that occur for this + if(events[i].data.fd == fd) + 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()) { auto& frontCallback = postCallbacks.front(); frontCallback(); diff --git a/src/Timer.cpp b/src/Timer.cpp index e395d18..922b32b 100644 --- a/src/Timer.cpp +++ b/src/Timer.cpp @@ -7,8 +7,9 @@ namespace nanosm { Timer::Timer(nanosm::EventLoop& ev) : nanosm::EventLoop::IoObject(ev) { timerFd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); - if(timerFd < 0) - perror("timerfd_create"); + if(timerFd < 0) { + throw std::system_error(std::make_error_code(static_cast(errno))); + } } Timer::~Timer() { diff --git a/src/main.cpp b/src/main.cpp index 593faa9..89f9540 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "EventLoop.hpp" @@ -82,7 +83,12 @@ struct NanoSm { 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; } @@ -105,51 +111,55 @@ fs::path GetConfigPath() { } int main(int argc, char** argv) { - NanoSm nanosm; - - auto configPath = GetConfigPath(); - try { - auto tomlTable = toml::parse_file(configPath.string()); + NanoSm nanosm; - if(tomlTable["nanosm"].is_table() && tomlTable["nanosm"]["apps"].is_table()) { - auto restartptr = tomlTable["nanosm"]["restart-time"].as_integer(); + auto configPath = GetConfigPath(); - if(!restartptr) { - nanosm::log::Warn("NanoSm", "No restart time provided in nanosm.toml. Defaulting to 1 second."); - nanosm.SetRestartTime(1); + try { + auto tomlTable = toml::parse_file(configPath.string()); + + 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(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 { - nanosm.SetRestartTime(static_cast(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()) { - 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 { - nanosm::log::Error("NanoSm", "Configuration file {} doesn't have required values. Exiting with failure.", configPath.string()); + return nanosm.Run(); + } catch(toml::parse_error& pe) { + std::fprintf(stderr, "Error parsing config file \"%s\": %s\n", configPath.c_str(), pe.description().data()); return 1; } - - } catch(toml::parse_error& pe) { - std::fprintf(stderr, "Error parsing config file \"%s\": %s\n", configPath.c_str(), pe.description().data()); + } catch(std::exception& ec) { + nanosm::log::Error("NanoSm", "Error initalizing NanoSm: {}", ec.what()); return 1; } - - return nanosm.Run(); }