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
|
add_executable(nanosm
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
|
|
||||||
src/WordExp.cpp
|
# glue code
|
||||||
|
|
||||||
src/EventLoop.cpp
|
src/EventLoop.cpp
|
||||||
|
src/Timer.cpp
|
||||||
|
src/WordExp.cpp
|
||||||
|
src/Process.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
nanosm_target(nanosm)
|
nanosm_target(nanosm)
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
#include <sys/epoll.h>
|
#include <sys/epoll.h>
|
||||||
|
|
||||||
#include <deque>
|
#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 <ctime>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -6,171 +6,17 @@
|
||||||
#include "EventLoop.hpp"
|
#include "EventLoop.hpp"
|
||||||
#include "WordExp.hpp"
|
#include "WordExp.hpp"
|
||||||
|
|
||||||
|
#include "Timer.hpp"
|
||||||
|
#include "Process.hpp"
|
||||||
|
|
||||||
nanosm::EventLoop ev;
|
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 :)
|
// 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() {
|
void test() {
|
||||||
auto process = std::make_shared<TestProcess>(ev);
|
|
||||||
|
|
||||||
// Do magic
|
// Do magic
|
||||||
process->SetExitCallback([p = process](int exitCode) {
|
process->SetExitCallback([p = process](int exitCode) {
|
||||||
|
|
Loading…
Reference in New Issue