aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.posix/process.ccm
diff options
context:
space:
mode:
Diffstat (limited to 'nihil.posix/process.ccm')
-rw-r--r--nihil.posix/process.ccm132
1 files changed, 88 insertions, 44 deletions
diff --git a/nihil.posix/process.ccm b/nihil.posix/process.ccm
index 425deac..ee7de15 100644
--- a/nihil.posix/process.ccm
+++ b/nihil.posix/process.ccm
@@ -1,7 +1,4 @@
-/*
- * This source code is released into the public domain.
- */
-
+// This source code is released into the public domain.
module;
#include <expected>
@@ -9,7 +6,7 @@ module;
#include <system_error>
#include <utility>
-#include <sys/types.h>
+#include <sys/wait.h>
export module nihil.posix:process;
@@ -17,72 +14,119 @@ import nihil.error;
namespace nihil {
-/*
- * wait_result: the exit status of a process.
- */
-export struct wait_result final {
+// wait_result: the exit status of a process.
+export struct wait_result final
+{
// Return true if the process exited normally with an exit code of
// zero, otherwise false.
- [[nodiscard]] auto okay(this wait_result const &self) -> bool;
- [[nodiscard]] explicit operator bool(this wait_result const &self);
+ [[nodiscard]] auto okay(this wait_result const &self) -> bool
+ {
+ return self.status() == 0;
+ }
+
+ [[nodiscard]] explicit operator bool(this wait_result const &self)
+ {
+ return self.okay();
+ }
// Return the exit status, if any.
- [[nodiscard]] auto status(this wait_result const &self)
- -> std::optional<int>;
+ [[nodiscard]] auto status(this wait_result const &self) -> std::optional<int>
+ {
+ if (WIFEXITED(self.m_status))
+ return WEXITSTATUS(self.m_status);
+ return {};
+ }
// Return the exit signal, if any.
- [[nodiscard]] auto signal(this wait_result const &self)
- -> std::optional<int>;
+ [[nodiscard]] auto signal(this wait_result const &self) -> std::optional<int>
+ {
+ if (WIFSIGNALED(self.m_status))
+ return WTERMSIG(self.m_status);
+ return {};
+ }
private:
friend struct process;
- int _status;
+ int m_status;
// Construct a new wait_result from the output of waitpid().
- wait_result(int status);
+ explicit wait_result(int status)
+ : m_status(status)
+ {
+ }
};
-/*
- * process: represents a process we created, which can be waited for.
- */
-export struct process final {
+// Represents a process we created, which can be waited for.
+export struct process final
+{
process() = delete;
- /*
- * Create a new process from a pid, which must be a child of the
- * current process.
- */
- process(::pid_t pid);
+ // Create a new process from a pid, which must be a child of the
+ // current process.
+ explicit process(::pid_t pid)
+ : m_pid(pid)
+ {
+ }
// When destroyed, we automatically wait for the process to
// avoid creating zombie processes.
- ~process();
+ ~process()
+ {
+ if (m_pid == -1)
+ return;
+
+ auto status = int{};
+ std::ignore = ::waitpid(m_pid, &status, WEXITED);
+ }
// Movable.
- process(process &&) noexcept;
- auto operator=(this process &, process &&) noexcept -> process &;
+ process(process &&other) noexcept
+ : m_pid(std::exchange(other.m_pid, -1))
+ {}
+
+ auto operator=(this process &self, process &&other) noexcept -> process &
+ {
+ if (&self != &other) {
+ self.m_pid = std::exchange(other.m_pid, -1);
+ }
+
+ return self; // NOLINT
+ }
// Not copyable.
process(process const &) = delete;
auto operator=(this process &, process const &) -> process & = delete;
// Get the child's process id.
- [[nodiscard]] auto pid(this process const &self) noexcept -> ::pid_t;
-
- /*
- * Wait for this process to exit (by calling waitpid()) and return
- * its exit status. This destroys the process state, leaving this
- * object in a moved-from state.
- */
- [[nodiscard]] auto wait(this process &&self)
- -> std::expected<wait_result, error>;
-
- /*
- * Release this process so we won't try to wait for it when
- * destroying this object.
- */
- [[nodiscard]] auto release(this process &&self) -> ::pid_t;
+ [[nodiscard]] auto pid(this process const &self) noexcept -> ::pid_t
+ {
+ return self.m_pid;
+ }
+
+ // Wait for this process to exit (by calling waitpid()) and return
+ // its exit status. This destroys the process state, leaving this
+ // object in a moved-from state.
+ [[nodiscard]] auto wait(this process &&self) // NOLINT
+ -> std::expected<wait_result, error>
+ {
+ auto status = int{};
+ auto ret = waitpid(self.m_pid, &status, 0);
+ if (ret == -1)
+ return std::unexpected(error(std::errc(errno)));
+
+ return wait_result(status);
+ }
+
+ // Release this process so we won't try to wait for it when
+ // destroying this object. This will leave a zombie process
+ // unless the wait is done another way.
+ [[nodiscard]] auto release(this process &&self) -> ::pid_t // NOLINT
+ {
+ auto const ret = self.pid();
+ self.m_pid = -1;
+ return ret;
+ }
private:
::pid_t m_pid;