Skip to content

Commit 1e3d73c

Browse files
committed
docs: improve local socket overview
This should hopefully make the "new" enum dispatch design less confusing for those new to the crate.
1 parent 1f1b9bd commit 1e3d73c

File tree

3 files changed

+78
-21
lines changed

3 files changed

+78
-21
lines changed

src/local_socket.rs

Lines changed: 67 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,73 @@
1-
//! Local sockets, an IPC primitive featuring a server and multiple clients connecting to that
2-
//! server using a filesystem path or an identifier inside a special namespace, each having a
3-
//! private connection to that server.
1+
//! Local sockets, a socket-like IPC primitive in which clients access a server through a
2+
//! filesystem path or an identifier inside a special namespace, with each client having a
3+
//! private connection to the server.
44
//!
5-
//! ## Implementation types
6-
//! Local sockets are not a real IPC method implemented by the OS – they exist to paper over the
7-
//! differences between the two underlying implementations currently in use: Unix domain sockets and
8-
//! Windows named pipes.
5+
//! ## Implementations and dispatch
6+
//! Local sockets are not a real IPC primitive implemented by the OS, but rather a construct of
7+
//! Interprocess that is implemented in terms of an underlying IPC primitive. Different IPC
8+
//! primitives are available on different platforms and have different capabilities and
9+
//! limitations. As such, the types representing local sockets that you can find in this
10+
//! module – [`Listener`], [`Stream`], [`RecvHalf`], [`SendHalf`] – are really enums in the style
11+
//! of `enum_dispatch` that contain variants for all the different implementations of local
12+
//! sockets that are available, and the types that they dispatch between are talked to via the
13+
//! corresponding [`Listener`](traits::Listener), [`Stream`](traits::Stream)
14+
//! [`RecvHalf`](traits::RecvHalf), [`SendHalf`](traits::SendHalf) traits that you can find in the
15+
//! [`traits`] module. (Note that this dispatch is currently zero-cost on all platforms, as there
16+
//! is only one underlying local socket implementation per platform, with Windows only using named
17+
//! pipe based local sockets and Unix only using Unix-domain socket based local sockets, but this
18+
//! may change in the future with the introduction of support for
19+
//! [the Windows implementation of Unix-domain sockets][udswnd]. Even then, the overhead of this
20+
//! dispatch is insignificant compared to the overhead of making the system calls that perform
21+
//! the actual communication.)
922
//!
10-
//! Interprocess defines [traits] that implementations of local sockets implement, and enums that
11-
//! constitute devirtualized trait objects (not unlike those provided by the `enum_dispatch` crate)
12-
//! for those traits. The implementation used, in cases where multiple options apply, is chosen at
13-
//! construction via the [name](Name) and [name type](NameType) infrastructure.
23+
//! [udswnd]: https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/
1424
//!
15-
//! ## Differences from regular sockets
16-
//! A few missing features, primarily on Windows, require local sockets to omit some important
17-
//! functionality, because code relying on it wouldn't be portable. Some notable differences are:
18-
//! - No `.shutdown()` – your communication protocol must manually negotiate end of transmission.
19-
//! Notably, `.read_to_string()` and `.read_all()` will always block indefinitely at some point.
20-
//! - No datagram sockets – the difference in semantics between connectionless datagram Unix-domain
21-
//! sockets and connection-based named message pipes on Windows does not allow bridging those two
22-
//! into a common API. You can emulate datagrams on top of streams anyway, so no big deal, right?
25+
//! The [`prelude`] module is there to make it easier to handle all of this complexity without
26+
//! suffering from naming collisions. **`use interprocess::local_socket::prelude::*;` is the
27+
//! recommended way of bringing local sockets into scope.**
28+
//!
29+
//! ## Stability
30+
//! Since interprocess communication cannot happen without agreement on a protocol between two or
31+
//! more processes, the mapping of local sockets to underlying primitives is stable and
32+
//! predictable. **The IPC primitive selected depends only on the current platform and the
33+
//! [name type](NameType) used.** The mapping is trivial unless noted otherwise (in particular,
34+
//! Interprocess never inserts its own message framing or any other type of metadata into the
35+
//! stream – the bytes you write are the exact bytes that come out the other end), which means
36+
//! that the portable API of local sockets is suitable for communicating with programs that do
37+
//! not use Interprocess themselves, including programs not written in Rust. All you need to do
38+
//! is use the correct name type for every platform.
39+
//!
40+
//! ## Raw handle and file descriptor access
41+
//! The enum dispatchers purposely omit implementations of `{As,Into,From}Raw{Handle,Fd}`,
42+
//! `As{Handle,Fd}`, `From<Owned{HandleFd}>` and `Into<Owned{Handle,Fd}>`. To access those trait
43+
//! implementations on the underlying implementation types, you need to match on the enum. For
44+
//! instance:
45+
//! ```no_run
46+
//! # #[cfg(unix)]
47+
//! use {interprocess::local_socket::prelude::*, std::os::unix::prelude::*};
48+
//! # #[cfg(unix)] fn hi(fd: OwnedFd, fd2: OwnedFd) {
49+
//!
50+
//! // Creating a stream from a file descriptor
51+
//! let stream = LocalSocketStream::UdSocket(fd.into());
52+
//! # let _ = stream;
53+
//!
54+
//! // Consuming a stream to get its file descriptor
55+
//! let fd = match stream {
56+
//! LocalSocketStream::UdSocket(s) => OwnedFd::from(s),
57+
//! };
58+
//! # let _ = fd;
59+
//!
60+
//! # let stream = LocalSocketStream::UdSocket(fd2.into());
61+
//! // Accessing a stream's file descriptor without taking ownership
62+
//! let fd = match stream {
63+
//! LocalSocketStream::UdSocket(s) => s.as_fd(),
64+
//! };
65+
//! # let _ = fd;
66+
//!
67+
//! // Listener, RecvHalf, and SendHalf work analogously.
68+
//! // Works just the same on Windows under the replacement of Fd with Handle.
69+
//! # }
70+
//! ```
2371
2472
#[macro_use]
2573
mod enumdef;

src/local_socket/listener/enum.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,18 @@ mkenum!(
1515
///
1616
/// This struct is created by [`ListenerOptions`](super::options::ListenerOptions).
1717
///
18+
/// See the [module-level documentation](crate::local_socket) for more details.
19+
///
1820
/// # Name reclamation
1921
/// *This section only applies to Unix domain sockets.*
2022
///
2123
/// When a Unix domain socket listener is closed, its associated socket file is not automatically
2224
/// deleted. Instead, it remains on the filesystem in a zombie state, neither accepting connections
2325
/// nor allowing a new listener to reuse it – [`create_sync()`] will return
24-
/// [`AddrInUse`](io::ErrorKind::AddrInUse) unless it is deleted manually.
26+
/// [`AddrInUse`](io::ErrorKind::AddrInUse) on some platforms unless the detached socket file is
27+
/// deleted manually.
2528
///
26-
/// Interprocess implements *automatic name reclamation* via: when the local socket listener is
29+
/// Interprocess implements *automatic name reclamation*: when the local socket listener is
2730
/// dropped, it performs [`std::fs::remove_file()`] (i.e. `unlink()`) with the path that was
2831
/// originally passed to [`create_sync()`], allowing for subsequent reuse of the local socket name.
2932
///

src/local_socket/stream/enum.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ mkenum!(
6161
/// Local socket byte stream, obtained either from [`Listener`](super::super::Listener) or by
6262
/// connecting to an existing local socket.
6363
///
64+
/// See the [module-level documentation](crate::local_socket) for more details.
65+
///
6466
/// # Examples
6567
///
6668
/// ## Basic client
@@ -120,6 +122,8 @@ multimacro! {
120122

121123
mkenum!(
122124
/// Receive half of a local socket stream, obtained by splitting a [`Stream`].
125+
///
126+
/// See the [module-level documentation](crate::local_socket) for more details.
123127
"local_socket::" RecvHalf);
124128
impl r#trait::RecvHalf for RecvHalf {
125129
type Stream = Stream;
@@ -128,6 +132,8 @@ dispatch_read!(RecvHalf);
128132

129133
mkenum!(
130134
/// Send half of a local socket stream, obtained by splitting a [`Stream`].
135+
///
136+
/// See the [module-level documentation](crate::local_socket) for more details.
131137
"local_socket::" SendHalf);
132138
impl r#trait::SendHalf for SendHalf {
133139
type Stream = Stream;

0 commit comments

Comments
 (0)