Skip to content

Commit efce8b0

Browse files
committed
add process group extensions and PidFd
1 parent ba4090b commit efce8b0

12 files changed

Lines changed: 678 additions & 14 deletions

File tree

‎src/std/CMakeLists.txt‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,10 @@ target_sources(
171171
src/os/handle.cppm
172172
src/os/socket.cppm
173173
src/os/unix/ffi.cppm
174+
src/os/unix/process.cppm
175+
src/os/linux/process.cppm
176+
src/os/linux/pidfd.cppm
177+
src/sys/process/linux.cppm
174178
src/os/windows/ffi.cppm
175179
src/sys/io/stdio.cppm
176180
src/sys/mod.cppm
@@ -280,6 +284,7 @@ target_sources(
280284
src/fs/mod.cpp
281285
src/env.cpp
282286
src/os/fd.cpp
287+
src/os/linux/process.cpp
283288
src/os/handle.cpp
284289
src/os/socket.cpp
285290
${rstd_std_platform_sources})

‎src/std/src/os/linux/pidfd.cppm‎

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
module;
2+
#include <rstd/macro.hpp>
3+
export module rstd:os.linux.pidfd;
4+
export import :os.fd;
5+
export import :process.exit_status;
6+
export import :io.error;
7+
8+
#if RSTD_OS_LINUX
9+
export namespace rstd::os::linux::process
10+
{
11+
12+
/// An owned process descriptor. Destruction closes it without killing or reaping.
13+
class PidFd {
14+
fd::OwnedFd descriptor_;
15+
explicit PidFd(fd::OwnedFd descriptor): descriptor_(rstd::move(descriptor)) {}
16+
17+
public:
18+
PidFd(PidFd&&) noexcept = default;
19+
auto operator=(PidFd&&) noexcept -> PidFd& = default;
20+
auto as_raw_fd() const noexcept -> fd::RawFd { return descriptor_.as_raw_fd(); }
21+
auto as_fd() const noexcept [[clang::lifetimebound]] -> fd::BorrowedFd {
22+
return descriptor_.as_fd();
23+
}
24+
auto into_raw_fd() && noexcept -> fd::RawFd { return rstd::move(descriptor_).into_raw_fd(); }
25+
static auto from_raw_fd(fd::RawFd descriptor) -> PidFd {
26+
return PidFd(fd::OwnedFd::from_raw_fd(descriptor));
27+
}
28+
static auto from_owned_fd(fd::OwnedFd descriptor) -> PidFd {
29+
return PidFd(rstd::move(descriptor));
30+
}
31+
auto into_owned_fd() && -> fd::OwnedFd { return rstd::move(descriptor_); }
32+
auto kill() const -> io::Result<empty>;
33+
/// Reaps without closing stdin. Repeated waits can fail on older kernels.
34+
auto wait() const -> io::Result<rstd::process::ExitStatus>;
35+
auto try_wait() const -> io::Result<Option<rstd::process::ExitStatus>>;
36+
};
37+
38+
} // namespace rstd::os::linux::process
39+
40+
namespace rstd
41+
{
42+
43+
template<>
44+
struct Impl<os::fd::IntoRawFd, os::linux::process::PidFd> : ImplBase<os::linux::process::PidFd> {
45+
auto into_raw_fd() noexcept -> os::fd::RawFd { return rstd::move(this->self()).into_raw_fd(); }
46+
};
47+
48+
template<>
49+
struct Impl<os::fd::FromRawFd, os::linux::process::PidFd> {
50+
static auto from_raw_fd(os::fd::RawFd fd) -> os::linux::process::PidFd {
51+
return os::linux::process::PidFd::from_raw_fd(fd);
52+
}
53+
};
54+
55+
} // namespace rstd
56+
#endif

‎src/std/src/os/linux/process.cpp‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
module;
2+
#include <rstd/macro.hpp>
3+
module rstd;
4+
#if RSTD_OS_LINUX
5+
import :sys.process.linux;
6+
import :sys.libc;
7+
8+
using namespace rstd::prelude;
9+
10+
auto pidfd_status(int fd, bool nonblocking) -> rstd::io::Result<Option<rstd::process::ExitStatus>> {
11+
auto result = rstd::sys::process::pidfd_wait(fd, nonblocking);
12+
if (result.error) return Err(rstd::io::error::Error::from_raw_os_error(i32(result.error)));
13+
if (! result.available) return Ok(None());
14+
return Ok(Some(result.signaled ? rstd::process::ExitStatus::from_signal(i32(result.status))
15+
: rstd::process::ExitStatus::from_code(i32(result.status))));
16+
}
17+
18+
auto rstd::os::linux::process::PidFd::kill() const -> io::Result<empty> {
19+
auto error = sys::process::pidfd_send_signal(as_raw_fd(), sys::libc::SIGKILL, false);
20+
if (error) return Err(io::error::Error::from_raw_os_error(i32(error)));
21+
return Ok(empty {});
22+
}
23+
24+
auto rstd::os::linux::process::PidFd::wait() const -> io::Result<rstd::process::ExitStatus> {
25+
auto result = pidfd_status(as_raw_fd(), false);
26+
if (result.is_err()) return Err(rstd::move(result).unwrap_err());
27+
return Ok(result.unwrap().unwrap());
28+
}
29+
30+
auto rstd::os::linux::process::PidFd::try_wait() const
31+
-> io::Result<Option<rstd::process::ExitStatus>> {
32+
return pidfd_status(as_raw_fd(), true);
33+
}
34+
#endif

‎src/std/src/os/linux/process.cppm‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
module;
2+
#include <rstd/macro.hpp>
3+
export module rstd:os.linux.process;
4+
export import :process.command;
5+
#if RSTD_OS_LINUX
6+
export import :os.linux.pidfd;
7+
8+
export namespace rstd::os::linux::process
9+
{
10+
11+
struct CommandExt {
12+
/// Requests a race-free pidfd at spawn. Absence does not fail spawning.
13+
static auto create_pidfd(rstd::process::Command& command, bool value)
14+
-> rstd::process::Command& {
15+
command.create_pidfd_ = value;
16+
return command;
17+
}
18+
};
19+
20+
struct ChildExt {
21+
static auto pidfd(const rstd::process::Child& child [[clang::lifetimebound]])
22+
-> io::Result<ref<PidFd>> {
23+
if (child.pidfd_.is_some())
24+
return Ok(ref<PidFd>::from_raw_parts(rstd::addressof(*child.pidfd_)));
25+
return Err(
26+
io::error::Error::from_kind(io::error::ErrorKind { io::error::ErrorKind::Other }));
27+
}
28+
/// Consumes Child on success; returns the original Child when no pidfd exists.
29+
static auto into_pidfd(rstd::process::Child child) -> Result<PidFd, rstd::process::Child> {
30+
if (child.pidfd_.is_none()) return Err(rstd::move(child));
31+
return Ok(child.pidfd_.take().unwrap());
32+
}
33+
};
34+
35+
} // namespace rstd::os::linux::process
36+
#endif

‎src/std/src/os/mod.cppm‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ export import :os.fd;
33
export import :os.handle;
44
export import :os.socket;
55
export import :os.unix.ffi;
6+
export import :os.unix.process;
7+
export import :os.linux.process;
68
export import :os.windows.ffi;

‎src/std/src/os/unix/process.cppm‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
module;
2+
#include <rstd/macro.hpp>
3+
export module rstd:os.unix.process;
4+
export import :process.command;
5+
6+
#if RSTD_OS_UNIX
7+
export namespace rstd::os::unix::process
8+
{
9+
10+
struct CommandExt {
11+
/// Zero makes the child the leader of a new process group.
12+
static auto process_group(rstd::process::Command& command, i32 pgroup)
13+
-> rstd::process::Command& {
14+
command.process_group_ = Some(pgroup);
15+
return command;
16+
}
17+
};
18+
19+
struct ChildExt {
20+
static auto send_signal(const rstd::process::Child& child, i32 signal) -> io::Result<empty> {
21+
return child.send_signal(signal, false);
22+
}
23+
/// For the PID fallback, the child must be its process group's leader.
24+
static auto send_process_group_signal(const rstd::process::Child& child, i32 signal)
25+
-> io::Result<empty> {
26+
return child.send_signal(signal, true);
27+
}
28+
static auto kill_process_group(rstd::process::Child& child) -> io::Result<empty>;
29+
};
30+
31+
} // namespace rstd::os::unix::process
32+
#endif

‎src/std/src/process/command.cppm‎

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,27 @@ export module rstd:process.command;
55
export import :process.exit_status;
66
export import :io;
77
export import :path;
8+
export import :os.fd;
89
export import rstd.alloc;
910
import :sys.io.stdio;
11+
#if RSTD_OS_LINUX
12+
export import :os.linux.pidfd;
13+
#endif
14+
15+
#if RSTD_OS_UNIX
16+
export namespace rstd::os::unix::process
17+
{
18+
struct CommandExt;
19+
struct ChildExt;
20+
} // namespace rstd::os::unix::process
21+
#endif
22+
#if RSTD_OS_LINUX
23+
export namespace rstd::os::linux::process
24+
{
25+
struct CommandExt;
26+
struct ChildExt;
27+
} // namespace rstd::os::linux::process
28+
#endif
1029

1130
using ::alloc::ffi::CString;
1231
using ::alloc::string::String;
@@ -39,6 +58,12 @@ struct ChildStdin {
3958
ChildStdin& operator=(ChildStdin&&) = delete;
4059
ChildStdin() = default;
4160
explicit ChildStdin(int f): fd(f) {}
61+
#if RSTD_OS_UNIX
62+
auto as_raw_fd() const noexcept -> os::fd::RawFd { return fd; }
63+
auto as_fd() const noexcept [[clang::lifetimebound]] -> os::fd::BorrowedFd {
64+
return os::fd::BorrowedFd::borrow_raw(fd);
65+
}
66+
#endif
4267
};
4368

4469
/// A handle to a child process's standard output (read end of pipe).
@@ -49,6 +74,12 @@ struct ChildStdout {
4974
ChildStdout& operator=(ChildStdout&&) = delete;
5075
ChildStdout() = default;
5176
explicit ChildStdout(int f): fd(f) {}
77+
#if RSTD_OS_UNIX
78+
auto as_raw_fd() const noexcept -> os::fd::RawFd { return fd; }
79+
auto as_fd() const noexcept [[clang::lifetimebound]] -> os::fd::BorrowedFd {
80+
return os::fd::BorrowedFd::borrow_raw(fd);
81+
}
82+
#endif
5283
};
5384

5485
/// A handle to a child process's standard error (read end of pipe).
@@ -59,6 +90,12 @@ struct ChildStderr {
5990
ChildStderr& operator=(ChildStderr&&) = delete;
6091
ChildStderr() = default;
6192
explicit ChildStderr(int f): fd(f) {}
93+
#if RSTD_OS_UNIX
94+
auto as_raw_fd() const noexcept -> os::fd::RawFd { return fd; }
95+
auto as_fd() const noexcept [[clang::lifetimebound]] -> os::fd::BorrowedFd {
96+
return os::fd::BorrowedFd::borrow_raw(fd);
97+
}
98+
#endif
6299
};
63100

64101
} // namespace rstd::process
@@ -106,6 +143,20 @@ export namespace rstd::process
106143

107144
/// A child process handle.
108145
struct Child {
146+
private:
147+
friend sys::process_impl::Spawn;
148+
#if RSTD_OS_UNIX
149+
friend os::unix::process::ChildExt;
150+
#endif
151+
#if RSTD_OS_LINUX
152+
friend os::linux::process::ChildExt;
153+
Option<os::linux::process::PidFd> pidfd_;
154+
#endif
155+
#if RSTD_OS_UNIX
156+
auto send_signal(i32 signal, bool group) const -> io::Result<empty>;
157+
#endif
158+
159+
public:
109160
int pid { -1 };
110161
#if RSTD_OS_WINDOWS
111162
void* process_handle { nullptr };
@@ -143,7 +194,11 @@ struct Child {
143194

144195
~Child();
145196
Child(Child&& o) noexcept
146-
: pid(o.pid),
197+
:
198+
#if RSTD_OS_LINUX
199+
pidfd_(o.pidfd_.take()),
200+
#endif
201+
pid(o.pid),
147202
#if RSTD_OS_WINDOWS
148203
process_handle(o.process_handle),
149204
#endif
@@ -171,6 +226,14 @@ class Command {
171226
Stdio cfg_stdout_ { Stdio::inherit() };
172227
Stdio cfg_stderr_ { Stdio::inherit() };
173228
bool env_clear_ { false };
229+
#if RSTD_OS_UNIX
230+
Option<i32> process_group_;
231+
friend os::unix::process::CommandExt;
232+
#endif
233+
#if RSTD_OS_LINUX
234+
bool create_pidfd_ { false };
235+
friend os::linux::process::CommandExt;
236+
#endif
174237

175238
friend sys::process_impl::Spawn;
176239

0 commit comments

Comments
 (0)