Expand description

Viaduct is a library for establishing a duplex communication channel between a parent and child process, using unnamed pipes.

Example

Shared library

use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
pub enum ExampleRpc {
    Cow,
    Pig,
    Horse
}

#[derive(Serialize, Deserialize, PartialEq, Eq)]
pub struct FrontflipError;

#[derive(Serialize, Deserialize, PartialEq, Eq)]
pub struct BackflipError;

Parent process

let child = std::process::Command::new("child.exe");
let ((tx, rx), mut child) = ViaductParent::new(child).unwrap().build().unwrap();

std::thread::spawn(move || {
   rx.run(|event| match event {
       ViaductEvent::Rpc(rpc) => match rpc {
           ExampleRpc::Cow => println!("Moo"),
           ExampleRpc::Pig => println!("Oink"),
           ExampleRpc::Horse => println!("Neigh"),
       },

       ViaductEvent::Request { request, responder } => match request {
           ExampleRequest::DoAFrontflip => {
               println!("Doing a frontflip!");
               responder.respond(Ok::<_, FrontflipError>(())).unwrap();
           },

           ExampleRequest::DoABackflip => {
               println!("Doing a backflip!");
               responder.respond(Ok::<_, BackflipError>(())).unwrap();
           },
       }
   }).unwrap();
});

tx.rpc(ExampleRpc::Cow).unwrap();
tx.rpc(ExampleRpc::Pig).unwrap();
tx.rpc(ExampleRpc::Horse).unwrap();

let response: Result<(), FrontflipError> = tx.request(ExampleRequest::DoAFrontflip).unwrap().unwrap();
assert_eq!(response, Ok(()));

Child process

let (tx, rx) = unsafe { ViaductChild::new().build() }.unwrap();

std::thread::spawn(move || {
   rx.run(|event| match event {
       ViaductEvent::Rpc(rpc) => match rpc {
           ExampleRpc::Cow => println!("Moo"),
           ExampleRpc::Pig => println!("Oink"),
           ExampleRpc::Horse => println!("Neigh"),
       },

       ViaductEvent::Request { request, responder } => match request {
           ExampleRequest::DoAFrontflip => {
               println!("Doing a frontflip!");
               responder.respond(Ok::<_, FrontflipError>(())).unwrap();
           },

           ExampleRequest::DoABackflip => {
               println!("Doing a backflip!");
               responder.respond(Ok::<_, BackflipError>(())).unwrap();
           },
       }
   }).unwrap();
});

tx.rpc(ExampleRpc::Horse).unwrap();
tx.rpc(ExampleRpc::Pig).unwrap();
tx.rpc(ExampleRpc::Cow).unwrap();

let response: Result<(), BackflipError> = tx.request(ExampleRequest::DoABackflip).unwrap().unwrap();
assert_eq!(response, Ok(()));

Use Cases

Viaduct was designed for separating user interface from application logic in a cross-platform manner.

For example, an application may want to run a GUI in a separate process from the application logic, for modularity or performance reasons.

Viaduct allows for applications like this to communicate between these processes in a natural way, without having to manually implement IPC machinery & synchronization.

Usage

Serialization

Viaduct currently supports serialization and deserialization of data using bytemuck (default), bincode or speedy at your choice, using the respective Cargo feature flags.

You can also manually implement the ViaductSerialize and ViaductDeserialize traits.

Initializing a viaduct

A viaduct is started by calling ViaductParent::new as the parent process, which will spawn your child process.

Your child process should then call ViaductChild::new, [ViaductChild::new_with_args_os] or [ViaductChild::new_with_args] (see CAVEAT below) to bridge the connection between the parent and child.

Then, you are ready to start…

Passing data

Viaduct has two modes of operation: RPCs and Requests/Responses.

RPCs are one-way messages, and are useful for sending notifications to the other process.

Requests/Responses are two-way messages, and are useful for sending requests to the other process and receiving data as a response.

Requests will block any other thread trying to send requests and RPCs through the viaduct, until a response is received.

CAVEAT: Don’t use std::env::args_os or std::env::args in your child process!

The child process should not use args_os or args to get its arguments, as these will contain data Viaduct needs to pass to the child process.

Instead, use the argument iterator provided by [ViaductChild::new_with_args_os] or [ViaductChild::new_with_args] for args_os and args respectively.

Structs

Interface for creating a viaduct on the CHILD process.

Interface for creating a viaduct on the PARENT process.

Use ViaductRequestResponder::respond to send a response to the other side.

The receiving side of a viaduct.

The sending side of a viaduct.

Enums

You can use this type (which implements ViaductSerialize and ViaductDeserialize) to specify that this type of packet (RCP/request) will never happen.

An event that was received over the viaduct.

Traits

Types that can be serialized and deserialized for crossing the viaduct.

Types that can be serialized and deserialized for crossing the viaduct.

Type Definitions

A channel pair for sending and receiving data across the viaduct.