mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-04 00:00:12 +02:00
Introduce tokio-sync crate containing synchronization primitives. (#839)
Introduce a tokio-sync crate containing useful synchronization primitives for programs written using Tokio. The initial release contains: * An mpsc channel * A oneshot channel * A semaphore implementation * An `AtomicTask` primitive. The `oneshot` and `mpsc` channels are new implementations providing improved performance characteristics. In some benchmarks, the new mpsc channel shows up to 7x improvement over the version provided by the `futures` crate. Unfortunately, the `oneshot` implementation only provides a slight performance improvement as it is mostly limited by the `futures` 0.1 task system. Once updated to the `std` version of `Future` (currently nightly only), much greater performance improvements should be achievable by `oneshot`. Additionally, he implementations provided here are checked using [Loom](http://github.com/carllerche/loom/), which provides greater confidence of correctness.
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
use super::chan;
|
||||
|
||||
use loom::sync::atomic::AtomicUsize;
|
||||
use futures::{Poll, Sink, StartSend, Stream};
|
||||
|
||||
use std::fmt;
|
||||
|
||||
/// Send values to the associated `UnboundedReceiver`.
|
||||
///
|
||||
/// Instances are created by the
|
||||
/// [`unbounded_channel`](fn.unbounded_channel.html) function.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UnboundedSender<T> {
|
||||
chan: chan::Tx<T, Semaphore>,
|
||||
}
|
||||
|
||||
/// Receive values from the associated `UnboundedSender`.
|
||||
///
|
||||
/// Instances are created by the
|
||||
/// [`unbounded_channel`](fn.unbounded_channel.html) function.
|
||||
#[derive(Debug)]
|
||||
pub struct UnboundedReceiver<T> {
|
||||
/// The channel receiver
|
||||
chan: chan::Rx<T, Semaphore>,
|
||||
}
|
||||
|
||||
/// Error returned by the `UnboundedSender`.
|
||||
#[derive(Debug)]
|
||||
pub struct UnboundedSendError(());
|
||||
|
||||
/// Error returned by `UnboundedSender::try_send`.
|
||||
#[derive(Debug)]
|
||||
pub struct UnboundedTrySendError<T>(T);
|
||||
|
||||
/// Error returned by `UnboundedReceiver`.
|
||||
#[derive(Debug)]
|
||||
pub struct UnboundedRecvError(());
|
||||
|
||||
/// Create an unbounded mpsc channel for communicating between asynchronous
|
||||
/// tasks.
|
||||
///
|
||||
/// A `send` on this channel will always succeed as long as the receive half has
|
||||
/// not been closed. If the receiver falls behind, messages will be arbitrarily
|
||||
/// buffered.
|
||||
///
|
||||
/// **Note** that the amount of available system memory is an implicit bound to
|
||||
/// the channel. Using an `unbounded` channel has the ability of causing the
|
||||
/// process to run out of memory. In this case, the process will be aborted.
|
||||
pub fn unbounded_channel<T>() -> (UnboundedSender<T>, UnboundedReceiver<T>) {
|
||||
let (tx, rx) = chan::channel(AtomicUsize::new(0));
|
||||
|
||||
let tx = UnboundedSender::new(tx);
|
||||
let rx = UnboundedReceiver::new(rx);
|
||||
|
||||
(tx, rx)
|
||||
}
|
||||
|
||||
/// No capacity
|
||||
type Semaphore = AtomicUsize;
|
||||
|
||||
impl<T> UnboundedReceiver<T> {
|
||||
pub(crate) fn new(chan: chan::Rx<T, Semaphore>) -> UnboundedReceiver<T> {
|
||||
UnboundedReceiver { chan }
|
||||
}
|
||||
|
||||
/// Closes the receiving half of a channel, without dropping it.
|
||||
///
|
||||
/// This prevents any further messages from being sent on the channel while
|
||||
/// still enabling the receiver to drain messages that are buffered.
|
||||
pub fn close(&mut self) {
|
||||
self.chan.close();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Stream for UnboundedReceiver<T> {
|
||||
type Item = T;
|
||||
type Error = UnboundedRecvError;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<T>, Self::Error> {
|
||||
self.chan.recv()
|
||||
.map_err(|_| UnboundedRecvError(()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T> UnboundedSender<T> {
|
||||
pub(crate) fn new(chan: chan::Tx<T, Semaphore>) -> UnboundedSender<T> {
|
||||
UnboundedSender { chan }
|
||||
}
|
||||
|
||||
/// Attempts to send a message on this `UnboundedSender` without blocking.
|
||||
pub fn try_send(&mut self, message: T)
|
||||
-> Result<(), UnboundedTrySendError<T>>
|
||||
{
|
||||
self.chan.try_send(message)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Sink for UnboundedSender<T> {
|
||||
type SinkItem = T;
|
||||
type SinkError = UnboundedSendError;
|
||||
|
||||
fn start_send(&mut self, msg: T) -> StartSend<T, Self::SinkError> {
|
||||
use futures::AsyncSink;
|
||||
|
||||
self.try_send(msg).map_err(|_| UnboundedSendError(()))?;
|
||||
Ok(AsyncSink::Ready)
|
||||
}
|
||||
|
||||
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
|
||||
use futures::Async::Ready;
|
||||
Ok(Ready(()))
|
||||
}
|
||||
|
||||
fn close(&mut self) -> Poll<(), Self::SinkError> {
|
||||
use futures::Async::Ready;
|
||||
Ok(Ready(()))
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl UnboundedSendError =====
|
||||
|
||||
impl fmt::Display for UnboundedSendError {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
use std::error::Error;
|
||||
write!(fmt, "{}", self.description())
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::error::Error for UnboundedSendError {
|
||||
fn description(&self) -> &str {
|
||||
"channel closed"
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl TrySendError =====
|
||||
|
||||
impl<T: fmt::Debug> fmt::Display for UnboundedTrySendError<T> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
use std::error::Error;
|
||||
write!(fmt, "{}", self.description())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> ::std::error::Error for UnboundedTrySendError<T> {
|
||||
fn description(&self) -> &str {
|
||||
"channel closed"
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<(T, chan::TrySendError)> for UnboundedTrySendError<T> {
|
||||
fn from((value, err): (T, chan::TrySendError)) -> UnboundedTrySendError<T> {
|
||||
assert_eq!(chan::TrySendError::Closed, err);
|
||||
UnboundedTrySendError(value)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user