Skip to content

Commit f2729e1

Browse files
authored
Improve documentation (#384)
* Improve documentation * Improve README and share with rustdocs * Improve docs of one-shot servers * Tidy up lib.rs * Fix doc warning in router.rs Delete documentation link from README as it doesn't look good in the docs. The documentation link (https://docs.rs/ipc-channel) should be moved to About->Website on the repository page at github.com. * Address review comment
1 parent e1ad621 commit f2729e1

File tree

4 files changed

+47
-22
lines changed

4 files changed

+47
-22
lines changed

README.md

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
# ipc-channel
1+
`ipc-channel` is an inter-process implementation of Rust channels (which were inspired by CSP[^CSP]).
22

3-
📚 [Documentation](https://docs.rs/ipc-channel) 📚
3+
A Rust channel is a unidirectional, FIFO queue of messages which can be used to send messages between threads in a single operating system process.
4+
For an excellent introduction to Rust channels, see [Using Message Passing to Transfer Data Between Threads](https://doc.rust-lang.org/stable/book/ch16-02-message-passing.html) in the Rust reference.
45

5-
## Overview
6-
7-
`ipc-channel` is an implementation of the Rust channel API (a form of communicating sequential processes, CSP) over the native OS abstractions. Under the hood, this API uses Mach ports on the Mac and file descriptor passing over Unix sockets on Linux. The `serde` library is used to serialize values for transport over the wire.
6+
`ipc-channel` extends Rust channels to support inter-process communication (IPC) in a single operating system instance. The `serde` library is used to serialize and deserialize messages sent over `ipc-channel`.
87

98
As much as possible, `ipc-channel` has been designed to be a drop-in replacement for Rust channels. The mapping from the Rust channel APIs to `ipc-channel` APIs is as follows:
109

@@ -14,10 +13,46 @@ As much as possible, `ipc-channel` has been designed to be a drop-in replacement
1413

1514
Note that both `IpcSender<T>` and `IpcReceiver<T>` implement `Serialize` and `Deserialize`, so you can send IPC channels over IPC channels freely, just as you can with Rust channels.
1615

17-
The easiest way to make your types implement `Serialize` and `Deserialize` is to use the `serde_macros` crate from crates.io as a plugin and then annotate the types you want to send with `#[derive(Deserialize, Serialize])`. In many cases, that's all you need to do—the compiler generates all the tedious boilerplate code needed to save and restore instances of your types.
16+
The easiest way to make your types implement `Serialize` and `Deserialize` is to use the `serde_macros` crate from crates.io as a plugin and then annotate the types you want to send with `#[derive(Deserialize, Serialize])`. In many cases, that's all you need to do — the compiler generates all the tedious boilerplate code needed to serialize and deserialize instances of your types.
17+
18+
## Semantic differences from Rust channels
19+
20+
* Rust channels can be either unbounded or bounded whereas ipc-channels are always unbounded and `send()` never blocks.
21+
* Rust channels do not consume OS IPC resources whereas ipc-channels consume IPC resources such as sockets, file descriptors, shared memory segments, named pipes, and such like, depending on the OS.
22+
* Rust channels transfer ownership of messages whereas ipc-channels serialize and deserialize messages.
23+
* Rust channels are type safe whereas ipc-channels depend on client and server programs using identical message types (or at least message types with compatible serial forms).
24+
25+
## Bootstrapping channels between processes
26+
27+
`ipc-channel` provides a one-shot server to help establish a channel between two processes. When a one-shot server is created, a server name is generated and returned along with the server.
28+
29+
The client process calls `connect()` passing the server name and this returns the sender end of an ipc-channel from
30+
the client to the server. Note that there is a restriction in `ipc-channel`: `connect()` may be called at most once per one-shot server.
31+
32+
The server process calls `accept()` on the server to accept connect requests from clients. `accept()` blocks until a client has connected to the server and sent a message. It then returns a pair consisting of the receiver end of the ipc-channel from client to server and the first message received from the client.
1833

19-
In order to bootstrap an IPC connection across processes, you create an instance of the `IpcOneShotServer` type, register a global name, pass that name into the client process (perhaps with an environment variable or command line flag), and connect to the server in the client. See `cross_process_embedded_senders()` in `test.rs` for an example of how to do this using Unix `fork()` to spawn the process.
34+
So, in order to bootstrap an IPC channel between processes, you create an instance of the `IpcOneShotServer` type, pass the resultant server name into the client process (perhaps via an environment variable or command line flag), and connect to the server in the client. See `spawn_one_shot_server_client()` in `integration_test.rs` for an example of how to do this using a command to spawn the client process and `cross_process_embedded_senders_fork()` in `test.rs` for an example of how to do this using Unix `fork()`[^fork] to create the client process.
35+
36+
## Implementation overview
37+
38+
`ipc-channel` is implemented in terms of native IPC primitives: file descriptor passing over Unix sockets on Unix variants, Mach ports on macOS, and named pipes on Windows.
39+
40+
One-shot server names are implemented as a file system path (for Unix variants, with the file system path bound to the socket) or other kinds of generated names on macOS and Windows.
2041

2142
## Major missing features
2243

23-
* Servers only accept one client at a time. This is fine if you simply want to use this API to split your application up into a fixed number of mutually untrusting processes, but it's not suitable for implementing a system service. An API for multiple clients may be added later if demand exists for it.
44+
* Each one-shot server accepts only one client connect request. This is fine if you simply want to use this API to split your application up into a fixed number of mutually untrusting processes, but it's not suitable for implementing a system service. An API for multiple clients may be added later if demand exists for it.
45+
46+
## Related
47+
48+
* [Rust channel](https://doc.rust-lang.org/std/sync/mpsc/index.html): MPSC (multi-producer, single-consumer) channels in the Rust standard library. The implementation
49+
consists of a single consumer wrapper of a port of Crossbeam channel.
50+
* [Crossbeam channel](https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-channel): extends Rust channels to be more like their Go counterparts. Crossbeam channels are MPMC (multi-producer, multi-consumer)
51+
* [Channels](https://docs.rs/channels/latest/channels/): provides Sender and Receiver types for communicating with a channel-like API across generic IO streams.
52+
53+
[^CSP]: Tony Hoare conceived Communicating Sequential Processes (CSP) as a concurrent programming language.
54+
Stephen Brookes and A.W. Roscoe developed a sound mathematical basis for CSP as a process algebra.
55+
CSP can now be used to reason about concurrency and to verify concurrency properties using model checkers such as FDR4.
56+
Go channels were also inspired by CSP.
57+
58+
[^fork]: `fork()` has a number of semantic rough edges and is not recommended for general use. See "A fork() in the road" by Andrew Baumann _et al._, Proceedings of the Workshop on Hot Topics in Operating Systems, ACM, 2019. ([PDF](https://www.microsoft.com/en-us/research/uploads/prod/2019/04/fork-hotos19.pdf))

src/ipc.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -833,7 +833,8 @@ impl Serialize for OpaqueIpcReceiver {
833833
}
834834
}
835835

836-
/// A server associated with a given name.
836+
/// A server associated with a given name. The server is "one-shot" because
837+
/// it accepts only one connect request from a client.
837838
///
838839
/// # Examples
839840
///

src/lib.rs

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,14 @@
77
// option. This file may not be copied, modified, or distributed
88
// except according to those terms.
99

10-
//! An implementation of the Rust channel API over process boundaries. Under the
11-
//! hood, this API uses Mach ports on Mac and file descriptor passing over Unix
12-
//! sockets on Linux. The serde library is used to serialize values for transport
13-
//! over the wire.
10+
#![doc = include_str!("../README.md")]
1411
//!
1512
//! # Features
1613
//! ## `force-inprocess`
1714
//!
1815
//! Force the `inprocess` backend to be used instead of the OS specific backend.
1916
//! The `inprocess` backend is a dummy back-end, that behaves like the real ones,
2017
//! but doesn't actually work between processes.
21-
//!
22-
//! ## `unstable`
23-
//!
24-
//! [IpcReceiver]: ipc/struct.IpcReceiver.html
25-
//! [IpcSender]: ipc/struct.IpcSender.html
26-
//! [IpcReceiverSet]: ipc/struct.IpcReceiverSet.html
27-
//! [IpcSharedMemory]: ipc/struct.IpcSharedMemory.html
28-
//! [OsIpcSharedMemory]: platform/struct.OsIpcSharedMemory.html
2918
3019
#[cfg(any(
3120
feature = "force-inprocess",

src/router.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// except according to those terms.
99

1010
//! Routers allow converting IPC channels to crossbeam channels.
11-
//! The [RouterProxy](crate::router::RouterProxy) provides various methods to register
11+
//! The [RouterProxy] provides various methods to register
1212
//! `IpcReceiver<T>`s. The router will then either call the appropriate callback or route the
1313
//! message to a crossbeam `Sender<T>` or `Receiver<T>`. You should use the global `ROUTER` to
1414
//! access the `RouterProxy` methods (via `ROUTER`'s `Deref` for `RouterProxy`.

0 commit comments

Comments
 (0)