// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include #include #include #include #include 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 DataCallback, std::function EofCallback); /// Stop reading and close the pipe. Callbacks will not fire after this returns. void Stop(); private: struct Impl; std::unique_ptr m_Impl; }; } // namespace zen