You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Implement child process spawning: the browser process re-executes itself with special arguments to become a network or renderer child process. The child inherits an IPC channel fd/handle and enters its role-specific event loop.
Prerequisites
Step 8 (IPC channel — IpcChannel::pair() and send/recv working)
File Changes
crates/ie-sandbox/src/process.rs — full rewrite: ChildHandle, spawn_child() implementation
Extract raw fd from child_channel (the fd that will be inherited by the child)
Get current exe path: std::env::current_exe()?
Build tokio::process::Command:
letmut cmd = Command::new(exe_path);
cmd.arg("--subprocess-kind").arg(kind.as_str());
cmd.env("IE_IPC_FD", child_fd.to_string());// Ensure the child fd is NOT closed on exec:// Use pre_exec to clear FD_CLOEXEC on the child fdunsafe{
cmd.pre_exec(move || {let flags = libc::fcntl(child_fd, libc::F_GETFD);
libc::fcntl(child_fd, libc::F_SETFD, flags & !libc::FD_CLOEXEC);Ok(())});}
Graceful shutdown: spawn, send Shutdown, wait. Process exits with code 0.
Forced kill on timeout: spawn network process, don't send Shutdown, call shutdown() with a very short timeout (simulate unresponsive child by sending a long-running request). Verify process is killed.
Drop cleanup: spawn child, drop ChildHandle, verify process is eventually killed (no zombie)
Invalid subprocess-kind: run binary with --subprocess-kind=invalid, verify exits with error code
Network process tests
Fetch via IPC: spawn network child. Start local test server. Send FetchRequest { id: 1, url: "http://127.0.0.1:PORT/hello" }. Recv FetchResponse { id: 1, status: 200, ... }. Verify body matches server response.
Fetch error: send FetchRequest with URL pointing to nothing. Recv FetchError { id: 1, error: "..." }.
Invalid URL: send FetchRequest with empty URL. Recv FetchError.
Multiple sequential requests: send 3 FetchRequests with ids 1, 2, 3. Recv 3 FetchResponses, match by id.
Request correlation: send requests with specific ids, verify responses have matching ids (not reordered).
Shutdown with no pending requests: send Shutdown, child exits cleanly.
Renderer stub tests
Spawn and shutdown: spawn renderer, send Shutdown, exits cleanly
Ping/pong: spawn renderer, Ping → Pong
IPC fd inheritance test (Unix)
Spawn child, verify it successfully reconstructs IpcChannel from IE_IPC_FD env var (this is implicitly tested by the ping/pong tests above)
Acceptance Criteria
cargo test -p ie-sandbox — all tests pass
cargo test -p ie-shell — subprocess-related tests pass
cargo clippy --workspace -- -D warnings — no warnings
Network child process can fetch from a real HTTP server
Parent: #3
Goal
Implement child process spawning: the browser process re-executes itself with special arguments to become a network or renderer child process. The child inherits an IPC channel fd/handle and enters its role-specific event loop.
Prerequisites
IpcChannel::pair()and send/recv working)File Changes
crates/ie-sandbox/src/process.rs— full rewrite:ChildHandle,spawn_child()implementationcrates/ie-sandbox/src/lib.rs— update re-exportscrates/ie-shell/src/cli.rs— add hidden--subprocess-kindargcrates/ie-shell/src/main.rs— detect subprocess mode, branch to child entry pointcrates/ie-shell/src/child_network.rs— new file, network process event loopcrates/ie-shell/src/child_renderer.rs— new file, renderer process stubImplementation
ChildHandle (
process.rs)ChildHandlestruct:ChildHandle::channel(&mut self) -> &mut IpcChannel— access the parent's end of the IPC channelChildHandle::is_alive(&mut self) -> bool—self.process.try_wait()returnsOk(None)ChildHandle::shutdown(&mut self) -> Result<()>:IpcMessage::Shutdownvia channeltokio::time::timeout)self.process.kill().awaitChildHandle::kill(&mut self) -> Result<()>— immediate kill, no graceful shutdownimpl Drop for ChildHandle— best-effort kill if process still alive (don't leave zombies)spawn_child (
process.rs)pub async fn spawn_child(kind: ProcessKind) -> Result<ChildHandle>:Unix implementation (
#[cfg(unix)]):IpcChannel::pair()→(parent_channel, child_channel)child_channel(the fd that will be inherited by the child)std::env::current_exe()?tokio::process::Command:cmd.spawn()?ChildHandle { process, channel: parent_channel, kind }Windows implementation (
#[cfg(windows)]):\\.\pipe\ie-ipc-{uuid}Commandwith--subprocess-kindarg andIE_IPC_PIPE=<pipe_name>env varChildHandleProcessKind updates (
process.rs)CLI changes (
cli.rs)Cli::mode()update: ifsubprocess_kindis set, return a newMode::Subprocess { kind }variantMain entry point changes (
main.rs)Mode::Subprocess { kind }branch:fn run_subprocess(kind: ProcessKind) -> Result<()>:IpcChannel::from_raw_fd(env::var("IE_IPC_FD")?.parse()?)?IpcChannel::from_named_pipe(&env::var("IE_IPC_PIPE")?)?Network→run_network_process(channel).awaitRenderer→run_renderer_process(channel).awaitBrowser→ error (browser is never a subprocess)Network process event loop (
child_network.rs)pub async fn run_network_process(mut channel: IpcChannel) -> Result<()>:Renderer process stub (
child_renderer.rs)pub async fn run_renderer_process(mut channel: IpcChannel) -> Result<()>:Tests
spawn_child tests
spawn_child(Network), send Ping, recv Pong, shutdownis_alive()returns true. Shutdown.is_alive()returns false.shutdown()with a very short timeout (simulate unresponsive child by sending a long-running request). Verify process is killed.ChildHandle, verify process is eventually killed (no zombie)--subprocess-kind=invalid, verify exits with error codeNetwork process tests
FetchRequest { id: 1, url: "http://127.0.0.1:PORT/hello" }. RecvFetchResponse { id: 1, status: 200, ... }. Verify body matches server response.FetchError { id: 1, error: "..." }.FetchError.Renderer stub tests
IPC fd inheritance test (Unix)
IE_IPC_FDenv var (this is implicitly tested by the ping/pong tests above)Acceptance Criteria
cargo test -p ie-sandbox— all tests passcargo test -p ie-shell— subprocess-related tests passcargo clippy --workspace -- -D warnings— no warnings