ipc-channel icon indicating copy to clipboard operation
ipc-channel copied to clipboard

Panic on large data transfer

Open Geens opened this issue 4 years ago • 1 comments

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();
}

Geens avatar Aug 12 '21 13:08 Geens

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();
}

SamRodri avatar Sep 04 '21 15:09 SamRodri