move process/timer io objects into seperate TUs
This commit is contained in:
parent
833f2054a4
commit
507e0c88a8
|
@ -14,9 +14,11 @@ include(CompilerFlags)
|
|||
add_executable(nanosm
|
||||
src/main.cpp
|
||||
|
||||
src/WordExp.cpp
|
||||
|
||||
# glue code
|
||||
src/EventLoop.cpp
|
||||
src/Timer.cpp
|
||||
src/WordExp.cpp
|
||||
src/Process.cpp
|
||||
)
|
||||
|
||||
nanosm_target(nanosm)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <sys/epoll.h>
|
||||
|
||||
#include <deque>
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
#include "Process.hpp"
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "WordExp.hpp"
|
||||
|
||||
namespace nanosm {
|
||||
|
||||
inline static pid_t Clone3(const clone_args& args) {
|
||||
return syscall(SYS_clone3, &args, sizeof(clone_args));
|
||||
}
|
||||
|
||||
Process::~Process() {
|
||||
Kill();
|
||||
Reset();
|
||||
}
|
||||
|
||||
int Process::GetFD() const {
|
||||
return pidFd;
|
||||
}
|
||||
|
||||
int Process::InterestedEvents() const {
|
||||
/// Pidfd only returns EPOLLIN.
|
||||
return EPOLLIN;
|
||||
}
|
||||
|
||||
void Process::Spawn(const std::string& commandLine) {
|
||||
commLine = commandLine;
|
||||
Respawn();
|
||||
}
|
||||
|
||||
void Process::Respawn() {
|
||||
pid = Clone3({
|
||||
.flags = CLONE_PIDFD,
|
||||
.pidfd = std::bit_cast<u64>(&pidFd),
|
||||
.exit_signal = SIGCHLD,
|
||||
});
|
||||
|
||||
if(pid < 0)
|
||||
perror("Clone3()");
|
||||
|
||||
if(pid == 0) {
|
||||
// Forked from the parent successfully, execute the given thing
|
||||
auto exp = nanosm::WordExp::Expand(commLine);
|
||||
std::vector<char*> argv(exp.words.size());
|
||||
for(usize i = 0; i < exp.words.size(); ++i)
|
||||
argv[i] = exp.words[i].data();
|
||||
|
||||
execvp(exp.words[0].data(), argv.data());
|
||||
} else {
|
||||
// Parent: monitor FD
|
||||
eventLoop.AddObject(IoObject::Ptr(shared_from_this()));
|
||||
}
|
||||
}
|
||||
|
||||
void Process::OnReady(int eventMask) {
|
||||
// In our case, any readiness signaled by the pidfd means the process exited
|
||||
// so this will never block (or really, wait).
|
||||
waitid(P_PIDFD, pidFd, &siginfo, WNOHANG);
|
||||
|
||||
// Post a callback to call the user's exit callback (if one exists)
|
||||
eventLoop.Post([self = shared_from_this()]() {
|
||||
// Prepare for re-attaching, or etc.
|
||||
self->eventLoop.RemoveObject(self);
|
||||
self->Reset();
|
||||
|
||||
if(self->onProcessExit)
|
||||
self->onProcessExit(self->siginfo.si_status);
|
||||
});
|
||||
}
|
||||
|
||||
void Process::Kill() {
|
||||
if(pid != -1)
|
||||
kill(pid, SIGTERM);
|
||||
}
|
||||
|
||||
void Process::SetExitCallback(std::function<void(int)> f) {
|
||||
onProcessExit = f;
|
||||
}
|
||||
|
||||
void Process::Reset() {
|
||||
if(pidFd != -1) {
|
||||
close(pidFd);
|
||||
pidFd = -1;
|
||||
}
|
||||
|
||||
pid = -1;
|
||||
siginfo = {};
|
||||
}
|
||||
|
||||
} // namespace nanosm
|
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "EventLoop.hpp"
|
||||
|
||||
namespace nanosm {
|
||||
|
||||
struct Process : nanosm::EventLoop::IoObject, std::enable_shared_from_this<Process> {
|
||||
// bring ctor in
|
||||
using nanosm::EventLoop::IoObject::IoObject;
|
||||
|
||||
virtual ~Process();
|
||||
|
||||
int GetFD() const override;
|
||||
|
||||
int InterestedEvents() const override;
|
||||
|
||||
void Spawn(const std::string& commandLine);
|
||||
|
||||
void Respawn();
|
||||
|
||||
void OnReady(int eventMask) override;
|
||||
|
||||
void Kill();
|
||||
|
||||
void SetExitCallback(std::function<void(int)> f);
|
||||
|
||||
private:
|
||||
void Reset();
|
||||
|
||||
int pidFd { -1 };
|
||||
pid_t pid { -1 };
|
||||
siginfo_t siginfo {};
|
||||
std::string commLine;
|
||||
|
||||
std::function<void(int)> onProcessExit;
|
||||
};
|
||||
|
||||
} // namespace nanosm
|
|
@ -0,0 +1,55 @@
|
|||
#include "Timer.hpp"
|
||||
|
||||
#include <sys/timerfd.h>
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
Timer::~Timer() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Timer::Reset() {
|
||||
if(timerFd != -1)
|
||||
close(timerFd);
|
||||
}
|
||||
|
||||
int Timer::GetFD() const {
|
||||
return timerFd;
|
||||
}
|
||||
|
||||
int Timer::InterestedEvents() const {
|
||||
/// TimerFD only returns EPOLLIN.
|
||||
return EPOLLIN;
|
||||
}
|
||||
|
||||
void Timer::OnReady(int eventMask) {
|
||||
u64 expiryCount {};
|
||||
|
||||
// Read the amount of timer expires so it stops screaming.
|
||||
static_cast<void>(read(timerFd, &expiryCount, sizeof(expiryCount)));
|
||||
|
||||
// Post the expiry callback directly into the event loop
|
||||
if(cb)
|
||||
eventLoop.Post(cb);
|
||||
}
|
||||
|
||||
void Timer::SetExpiryCallback(std::function<void()> expire) {
|
||||
cb = expire;
|
||||
}
|
||||
|
||||
void Timer::Arm(u32 durationSeconds) {
|
||||
itimerspec spec {};
|
||||
spec.it_value.tv_sec = durationSeconds;
|
||||
|
||||
// TODO: validate.
|
||||
timerfd_settime(timerFd, 0, &spec, nullptr);
|
||||
}
|
||||
|
||||
} // namespace nanosm
|
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
#include "EventLoop.hpp"
|
||||
|
||||
namespace nanosm {
|
||||
|
||||
struct Timer : nanosm::EventLoop::IoObject, std::enable_shared_from_this<Timer> {
|
||||
Timer(nanosm::EventLoop& ev);
|
||||
|
||||
virtual ~Timer();
|
||||
|
||||
void Reset();
|
||||
int GetFD() const override;
|
||||
|
||||
int InterestedEvents() const override;
|
||||
|
||||
void OnReady(int eventMask) override;
|
||||
|
||||
void SetExpiryCallback(std::function<void()> expire);
|
||||
void Arm(u32 durationSeconds);
|
||||
private:
|
||||
int timerFd { -1 };
|
||||
std::function<void()> cb;
|
||||
};
|
||||
|
||||
} // namespace nanosm
|
166
src/main.cpp
166
src/main.cpp
|
@ -1,4 +1,4 @@
|
|||
#include <bits/types/struct_itimerspec.h>
|
||||
|
||||
|
||||
#include <ctime>
|
||||
#include <memory>
|
||||
|
@ -6,171 +6,17 @@
|
|||
#include "EventLoop.hpp"
|
||||
#include "WordExp.hpp"
|
||||
|
||||
#include "Timer.hpp"
|
||||
#include "Process.hpp"
|
||||
|
||||
nanosm::EventLoop ev;
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
struct TestProcess : nanosm::EventLoop::IoObject, std::enable_shared_from_this<TestProcess> {
|
||||
// bring ctor in
|
||||
using nanosm::EventLoop::IoObject::IoObject;
|
||||
|
||||
virtual ~TestProcess() {
|
||||
Kill();
|
||||
Reset();
|
||||
}
|
||||
|
||||
int GetFD() const override {
|
||||
return pidfdFd;
|
||||
}
|
||||
|
||||
int InterestedEvents() const override {
|
||||
/// Pidfd only returns EPOLLIN.
|
||||
return EPOLLIN;
|
||||
}
|
||||
|
||||
void Spawn(const std::string& commandLine) {
|
||||
commLine = commandLine;
|
||||
Respawn();
|
||||
}
|
||||
|
||||
void Respawn() {
|
||||
pid = Clone3({
|
||||
.flags = CLONE_PIDFD,
|
||||
.pidfd = std::bit_cast<u64>(&pidfdFd),
|
||||
.exit_signal = SIGCHLD,
|
||||
});
|
||||
|
||||
if(pid < 0)
|
||||
perror("Error cloning");
|
||||
|
||||
if(pid == 0) {
|
||||
// Forked from the parent successfully, execute the given thing
|
||||
auto exp = nanosm::WordExp::Expand(commLine);
|
||||
std::vector<char*> argv(exp.words.size());
|
||||
for(usize i = 0; i < exp.words.size(); ++i)
|
||||
argv[i] = exp.words[i].data();
|
||||
|
||||
execvp(exp.words[0].data(), argv.data());
|
||||
} else {
|
||||
// Parent: monitor FD
|
||||
eventLoop.AddObject(IoObject::Ptr(shared_from_this()));
|
||||
}
|
||||
}
|
||||
|
||||
void OnReady(int eventMask) override {
|
||||
// In our case, any readiness signaled by the pidfd means the process exited
|
||||
// so this will never block (or really, wait).
|
||||
waitid(P_PIDFD, pidfdFd, &siginfo, WNOHANG);
|
||||
|
||||
// Post a callback to call the user's
|
||||
eventLoop.Post([self = shared_from_this()]() {
|
||||
// Prepare for re-attaching, or etc.
|
||||
self->eventLoop.RemoveObject(self);
|
||||
self->Reset();
|
||||
|
||||
if(self->onProcessExit)
|
||||
self->onProcessExit(self->siginfo.si_status);
|
||||
});
|
||||
}
|
||||
|
||||
void Kill() {
|
||||
if(pid != -1)
|
||||
kill(pid, SIGTERM);
|
||||
}
|
||||
|
||||
void SetExitCallback(std::function<void(int)> f) {
|
||||
onProcessExit = f;
|
||||
}
|
||||
|
||||
private:
|
||||
static pid_t Clone3(const clone_args& args) {
|
||||
return syscall(SYS_clone3, &args, sizeof(clone_args));
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
if(pidfdFd != -1) {
|
||||
close(pidfdFd);
|
||||
pidfdFd = -1;
|
||||
}
|
||||
|
||||
pid = -1;
|
||||
siginfo = {};
|
||||
}
|
||||
|
||||
int pidfdFd { -1 };
|
||||
pid_t pid { -1 };
|
||||
siginfo_t siginfo {};
|
||||
std::string commLine;
|
||||
|
||||
std::function<void(int)> onProcessExit;
|
||||
};
|
||||
|
||||
#include <sys/timerfd.h>
|
||||
|
||||
struct TestTimer : nanosm::EventLoop::IoObject, std::enable_shared_from_this<TestTimer> {
|
||||
TestTimer(nanosm::EventLoop& ev)
|
||||
: nanosm::EventLoop::IoObject(ev) {
|
||||
timerFd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
|
||||
if(timerFd < 0)
|
||||
perror("timerfd_create");
|
||||
}
|
||||
|
||||
virtual ~TestTimer() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
if(timerFd != -1)
|
||||
close(timerFd);
|
||||
}
|
||||
|
||||
int GetFD() const override {
|
||||
return timerFd;
|
||||
}
|
||||
|
||||
int InterestedEvents() const override {
|
||||
/// TimerFD only returns EPOLLIN.
|
||||
return EPOLLIN;
|
||||
}
|
||||
|
||||
void OnReady(int eventMask) override {
|
||||
u64 expiryCount {};
|
||||
|
||||
// Read the amount of timer expires so it stops screaming.
|
||||
read(timerFd, &expiryCount, sizeof(expiryCount));
|
||||
|
||||
// Post the expiry callback directly into the event loop
|
||||
if(cb)
|
||||
eventLoop.Post(cb);
|
||||
}
|
||||
|
||||
void SetExpiryCallback(std::function<void()> expire) {
|
||||
cb = expire;
|
||||
}
|
||||
|
||||
void Arm(u32 durationSeconds) {
|
||||
itimerspec spec {};
|
||||
spec.it_value.tv_sec = durationSeconds;
|
||||
|
||||
// TODO: validate.
|
||||
timerfd_settime(timerFd, 0, &spec, nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
int timerFd { -1 };
|
||||
std::function<void()> cb;
|
||||
};
|
||||
|
||||
// tests stuff :)
|
||||
|
||||
auto timer = std::make_shared<TestTimer>(ev);
|
||||
auto timer = std::make_shared<nanosm::Timer>(ev);
|
||||
auto process = std::make_shared<nanosm::Process>(ev);
|
||||
|
||||
void test() {
|
||||
auto process = std::make_shared<TestProcess>(ev);
|
||||
|
||||
// Do magic
|
||||
process->SetExitCallback([p = process](int exitCode) {
|
||||
|
|
Loading…
Reference in New Issue