Module glommio::channels::shared_channel [−][src]
Allow data to be transmitted across two tasks in the different executors.
Most useful thread-per-core applications will heavily process data locally to their executor, but at times inter-executor communication is unavoidable (if you never communicate inter-executors, you may consider just using independent processes!)
Whenever you need to send data across two executors you can use the
shared_channel
. This channel is a fully lockless
single-producer-single-consumer channel, unlike the standard
library channel
, which is multi-producer and need locks for
synchronization. That means that to wire N executors to each other you will
have to create O(N^2) channels.
The channels are also not bidirectional, so for full bidirectional communication you will need pairs of channels.
True to the spirit of our library the shared_channel
is not created or
wired automatically among executors. You can have executors that work as
sinks and never respond back, other executors that are not really connected
to anybody, cliques of executors, you name it.
However, note that connecting a channel will involve a blocking operation as we need to synchronize and exchange information between peers. That can block the executor, although in practice that will only happen if it races with the creation of a new executor. Because of that, The best performance pattern when using shared channels is to create both the executors and channels early, connect the channels as soon as they start, and keep them alive.
Data that goes into the channels need to be Send
, as well as the
channels themselves (otherwise you would not be able to really pass them
along to the actual executors). But to prevent accidental use from multiple
producers, the channel endpoints have to be connected
before
using: Connecting consumes the endpoint, transforming a SharedSender
into a ConnectedSender
and a SharedReceiver
into a
ConnectedReceiver
so although you can pass one of the ends of the
channel to multiple executors, you can only connect in one of them. The
connected channel itself is not Send
nor Sync
so once connected they
are stuck in an executor.
The communication between sender and receiver is broken when one of them goes out of scope. They however behave differently:
- The
ConnectedReceiver
never sees an error, as it is implemented as a stream interface compatible withStreamExt
. When the sender is no longer available the receiver’s call tonext
will returnNone
. - The
ConnectedSender
will return aGlommioError::Closed(..)
if it tries tosend
into a channel that no longer has a receiver.
Examples
use glommio::{channels::shared_channel, Local, LocalExecutorBuilder}; // creates both ends of the channel. This is done outside the executors // and we will not pass the sender to ex1 and the receiver to ex2 let (sender, receiver) = shared_channel::new_bounded(1); let ex1 = LocalExecutorBuilder::new() .spawn(move || async move { // Before using we have to connect. Connecting this endpoint // binds it this executor as the connected endpoint is not Send. let sender = sender.connect().await; // Channel has room for 1 element so this will always succeed sender.try_send(100).unwrap(); }) .unwrap(); let ex2 = LocalExecutorBuilder::new() .spawn(move || async move { // much like the sender, the receiver also needs to be connected let receiver = receiver.connect().await; let x = receiver.recv().await.unwrap(); assert_eq!(x, 100); }) .unwrap(); ex1.join().unwrap(); ex2.join().unwrap();
Structs
ConnectedReceiver | The |
ConnectedSender | The |
SharedReceiver | The |
SharedSender | The |
Functions
new_bounded | Creates a a new |