diff options
| author | Stefan Boberg <[email protected]> | 2026-03-24 15:47:23 +0100 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2026-03-24 15:47:23 +0100 |
| commit | 21c2abb1bde697c31bee562465cb986a0429a299 (patch) | |
| tree | 3734d235e79a8fbed307ae5c248936d356553b61 /src/zenutil/process/asyncpipereader.h | |
| parent | v5.7.25 hotpatch (#874) (diff) | |
| download | zen-21c2abb1bde697c31bee562465cb986a0429a299.tar.xz zen-21c2abb1bde697c31bee562465cb986a0429a299.zip | |
Subprocess Manager (#889)
Adds a `SubprocessManager` for managing child processes with ASIO-integrated async exit detection, stdout/stderr pipe capture, and periodic metrics sampling. Also introduces `ProcessGroup` for OS-backed process grouping (Windows JobObjects / POSIX process groups).
### SubprocessManager
- Async process exit detection using platform-native mechanisms (Windows `object_handle`, Linux `pidfd_open`, macOS `kqueue EVFILT_PROC`) — no polling
- Stdout/stderr capture via async pipe readers with per-process or default callbacks
- Periodic round-robin metrics sampling (CPU, memory) across managed processes
- Spawn, adopt, remove, kill, and enumerate managed processes
### ProcessGroup
- OS-level process grouping: Windows JobObject (kill-on-close guarantee), POSIX `setpgid` (bulk signal delivery)
- Atomic group kill via `TerminateJobObject` (Windows) or `kill(-pgid, sig)` (POSIX)
- Per-group aggregate metrics and enumeration
### ProcessHandle improvements
- Added explicit constructors from `int` (pid) and `void*` (native handle)
- Added move constructor and move assignment operator
### ProcessMetricsTracker
- Cross-platform process metrics (CPU time, working set, page faults) via `QueryProcessMetrics()`
- ASIO timer-driven periodic sampling with configurable interval and batch size
- Aggregate metrics across tracked processes
### Other changes
- Fixed `zentest-appstub` writing a spurious `Versions` file to cwd on every invocation
Diffstat (limited to 'src/zenutil/process/asyncpipereader.h')
| -rw-r--r-- | src/zenutil/process/asyncpipereader.h | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/src/zenutil/process/asyncpipereader.h b/src/zenutil/process/asyncpipereader.h new file mode 100644 index 000000000..ad2ff8455 --- /dev/null +++ b/src/zenutil/process/asyncpipereader.h @@ -0,0 +1,62 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include <zencore/process.h> +#include <zencore/zencore.h> + +#include <functional> +#include <memory> +#include <string_view> + +namespace asio { +class io_context; +} + +namespace zen { + +/// Create an overlapped pipe pair suitable for async I/O on Windows. +/// +/// Unlike CreateStdoutPipe() (which creates anonymous non-overlapped pipes), +/// this creates a named pipe with FILE_FLAG_OVERLAPPED on the read end, so it +/// can be used with asio::windows::stream_handle for fully async reads. +/// The write end is inheritable and suitable for child process redirection. +/// +/// On non-Windows platforms this simply delegates to CreateStdoutPipe(). +bool CreateOverlappedStdoutPipe(StdoutPipeHandles& OutPipe); + +/// Async pipe reader for capturing child process stdout/stderr. +/// +/// Takes ownership of a pipe's read end and reads asynchronously: +/// Linux/macOS: non-blocking fd + asio::posix::stream_descriptor +/// Windows: overlapped named pipe + asio::windows::stream_handle +/// +/// On Windows the pipe must have been created with CreateOverlappedStdoutPipe() +/// for async I/O to work. Pipes from CreateStdoutPipe() will fail. +/// +/// DataCallback is invoked for each chunk read (on the io_context). +/// EofCallback is invoked when the pipe closes (child exited or pipe broken). +class AsyncPipeReader +{ +public: + explicit AsyncPipeReader(asio::io_context& IoContext); + ~AsyncPipeReader(); + + AsyncPipeReader(const AsyncPipeReader&) = delete; + AsyncPipeReader& operator=(const AsyncPipeReader&) = delete; + + /// Take ownership of the pipe read-end and start async reading. + /// The write end is closed immediately (caller should have already launched + /// the child process). DataCallback receives raw chunks. EofCallback fires + /// once when the pipe reaches EOF. + void Start(StdoutPipeHandles&& Pipe, std::function<void(std::string_view Data)> DataCallback, std::function<void()> EofCallback); + + /// Stop reading and close the pipe. Callbacks will not fire after this returns. + void Stop(); + +private: + struct Impl; + std::unique_ptr<Impl> m_Impl; +}; + +} // namespace zen |