I'm trying to get a parent process and a child process to communicate with each other using a tokio::net::UnixStream
. For some reason the child is unable to read whatever the parent writes to the socket, and presumably the other way around.
The function I have is similar to the following:
pub async fn run() -> Result<(), Error> {
let mut socks = UnixStream::pair()?;
match fork() {
Ok(ForkResult::Parent { .. }) => {
socks.0.write_u32(31337).await?;
Ok(())
}
Ok(ForkResult::Child) => {
eprintln!("Reading from master");
let msg = socks.1.read_u32().await?;
eprintln!("Read from master {}", msg);
Ok(())
}
Err(_) => Err(Error),
}
}
The socket doesn't get closed, otherwise I'd get an immediate error trying to read from socks.1
. If I move the read into the parent process it works as expected. The first line "Reading from master" gets printed, but the second line never gets called.
I cannot change the communication paradigm, since I'll be using execve
to start another binary that expects to be talking to a socketpair
.
Any idea what I'm doing wrong here? Is it something to do with the async
/await
?
When you call the fork() system call:
The default executor in tokio is a thread pool executor. The child process will only get one of the threads in the pool, so it won't work properly.
I found I was able to make your program work by setting the thread pool to contain only a single thread, like this:
Another change I had to make was to force the parent to wait for the child to complete, by calling
wait()
- also something you probably do not want to be doing in a real async program.Most of the advice I have read that if you need to fork from a threaded program, either do it before creating any threads, or call
exec_ve()
in the child immediately after forking (which is what you plan to do anyway).