ipc-channel
ipc-channel copied to clipboard
Panic on large data transfer
I get this strange panic when sending large messages to a child process. I was able to recreate the problem in this minimal example. The program runs correctly for smaller payload sizes, e.g. 60000.
Tested on:
- OS: Windows 10
- Rust: stable-x86_64-pc-windows-msvc unchanged - rustc 1.52.1 (9bc8c42bb 2021-05-09)
- ipc_channel: v0.15.0
thread 'main' panicked at 'Windows IPC channel received handles intended for pid 10012, but this is pid 872. This likely happened because a receiver was transferred while it had outstanding data that contained a channel or shared memory in its pipe. This isn't supported in the Windows implementation.', C:\Users\geens\.cargo\registry\src\github.com-1ecc6299db9ec823\ipc-channel-0.15.0\src\platform\windows\mod.rs:158:17
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `target\debug\ipc_channel_test.exe` (exit code: 101)
use ipc_channel::ipc;
fn main() {
let args: Vec<String> = std::env::args().collect();
if args.len() == 2 {
child(args[1].clone());
} else {
parent(args[0].clone());
}
}
fn parent(executable: String) {
let (server, server_name) = ipc::IpcOneShotServer::new().unwrap();
let mut child = std::process::Command::new(executable)
.arg(server_name)
.stdout(std::process::Stdio::piped())
.spawn()
.unwrap();
let (_, (tx, rx)): (_, (ipc::IpcSender<String>, ipc::IpcReceiver<String>)) = server.accept().unwrap();
let payload = "a".repeat(70000).to_owned();
tx.send(payload.clone()).unwrap();
let received = rx.recv().unwrap();
assert_eq!(payload, received);
child.wait().unwrap();
}
fn child(server_name: String) {
let one_shot_sender = ipc::IpcSender::connect(server_name).unwrap();
let (tx, rx_send) = ipc::channel().unwrap();
let (tx_send, rx) = ipc::channel().unwrap();
one_shot_sender.send((tx_send, rx_send)).unwrap();
let payload: String = rx.recv().unwrap();
tx.send(payload).unwrap();
}
I encountered the same panic, it only happens if the receiver was created in a different process, so a workaround is to create the channel in the process the receiver will be used:
use ipc_channel::ipc;
fn main() {
let args: Vec<String> = std::env::args().collect();
if args.len() == 2 {
child(args[1].clone());
} else {
parent(args[0].clone());
}
}
fn parent(executable: String) {
let (server, server_name) = ipc::IpcOneShotServer::new().unwrap();
let mut child = std::process::Command::new(executable)
.arg(server_name)
.stdout(std::process::Stdio::piped())
.spawn()
.unwrap();
let (_, (tx, workaround_tx)): (_, (ipc::IpcSender<String>, ipc::IpcSender<ipc::IpcSender<String>>)) = server.accept().unwrap();
let (tx_send, rx) = ipc::channel().unwrap();
workaround_tx.send(tx_send).unwrap();
let payload = "a".repeat(70000);
tx.send(payload.clone()).unwrap();
let received = rx.recv().unwrap();
assert_eq!(payload, received);
child.wait().unwrap();
}
fn child(server_name: String) {
let one_shot_sender = ipc::IpcSender::connect(server_name).unwrap();
let (workaround_tx_send, workaround_rx) = ipc::channel().unwrap();
let (tx_send, rx) = ipc::channel().unwrap();
one_shot_sender.send((tx_send, workaround_tx_send)).unwrap();
let tx: ipc::IpcSender<String> = workaround_rx.recv().unwrap();
let payload: String = rx.recv().unwrap();
tx.send(payload).unwrap();
}