Skip to content

Commit aa25b95

Browse files
authored
Add blocking functionality to channels (#47)
1 parent 94cdc43 commit aa25b95

File tree

3 files changed

+197
-42
lines changed

3 files changed

+197
-42
lines changed

src/lib.rs

Lines changed: 165 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,35 @@ impl<T> Sender<T> {
242242
}
243243
}
244244

245+
/// Sends a message into this channel using the blocking strategy.
246+
///
247+
/// If the channel is full, this method will block until there is room.
248+
/// If the channel is closed, this method returns an error.
249+
///
250+
/// # Blocking
251+
///
252+
/// Rather than using asynchronous waiting, like the [`send`] method,
253+
/// this method will block the current thread until the message is sent.
254+
///
255+
/// This method should not be used in an asynchronous context. It is intended
256+
/// to be used such that a channel can be used in both asynchronous and synchronous contexts.
257+
/// Calling this method in an asynchronous context may result in deadlocks.
258+
///
259+
/// # Examples
260+
///
261+
/// ```
262+
/// use async_channel::{unbounded, SendError};
263+
///
264+
/// let (s, r) = unbounded();
265+
///
266+
/// assert_eq!(s.send_blocking(1), Ok(()));
267+
/// drop(r);
268+
/// assert_eq!(s.send_blocking(2), Err(SendError(2)));
269+
/// ```
270+
pub fn send_blocking(&self, msg: T) -> Result<(), SendError<T>> {
271+
self.send(msg).wait()
272+
}
273+
245274
/// Closes the channel.
246275
///
247276
/// Returns `true` if this call has closed the channel and it was not closed already.
@@ -511,6 +540,38 @@ impl<T> Receiver<T> {
511540
}
512541
}
513542

543+
/// Receives a message from the channel using the blocking strategy.
544+
///
545+
/// If the channel is empty, this method waits until there is a message.
546+
/// If the channel is closed, this method receives a message or returns an error if there are
547+
/// no more messages.
548+
///
549+
/// # Blocking
550+
///
551+
/// Rather than using asynchronous waiting, like the [`recv`] method,
552+
/// this method will block the current thread until the message is sent.
553+
///
554+
/// This method should not be used in an asynchronous context. It is intended
555+
/// to be used such that a channel can be used in both asynchronous and synchronous contexts.
556+
/// Calling this method in an `async` block may result in deadlocks.
557+
///
558+
/// # Examples
559+
///
560+
/// ```
561+
/// use async_channel::{unbounded, RecvError};
562+
///
563+
/// let (s, r) = unbounded();
564+
///
565+
/// assert_eq!(s.send_blocking(1), Ok(()));
566+
/// drop(s);
567+
///
568+
/// assert_eq!(r.recv_blocking(), Ok(1));
569+
/// assert_eq!(r.recv_blocking(), Err(RecvError));
570+
/// ```
571+
pub fn recv_blocking(&self) -> Result<T, RecvError> {
572+
self.recv().wait()
573+
}
574+
514575
/// Closes the channel.
515576
///
516577
/// Returns `true` if this call has closed the channel and it was not closed already.
@@ -895,50 +956,62 @@ pub struct Send<'a, T> {
895956
msg: Option<T>,
896957
}
897958

898-
impl<'a, T> Unpin for Send<'a, T> {}
899-
900-
impl<'a, T> Future for Send<'a, T> {
901-
type Output = Result<(), SendError<T>>;
902-
903-
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
904-
let mut this = Pin::new(self);
905-
959+
impl<'a, T> Send<'a, T> {
960+
/// Run this future with the given `Strategy`.
961+
fn run_with_strategy<S: Strategy>(
962+
&mut self,
963+
cx: &mut S::Context,
964+
) -> Poll<Result<(), SendError<T>>> {
906965
loop {
907-
let msg = this.msg.take().unwrap();
966+
let msg = self.msg.take().unwrap();
908967
// Attempt to send a message.
909-
match this.sender.try_send(msg) {
968+
match self.sender.try_send(msg) {
910969
Ok(()) => {
911970
// If the capacity is larger than 1, notify another blocked send operation.
912-
match this.sender.channel.queue.capacity() {
971+
match self.sender.channel.queue.capacity() {
913972
Some(1) => {}
914-
Some(_) | None => this.sender.channel.send_ops.notify(1),
973+
Some(_) | None => self.sender.channel.send_ops.notify(1),
915974
}
916975
return Poll::Ready(Ok(()));
917976
}
918977
Err(TrySendError::Closed(msg)) => return Poll::Ready(Err(SendError(msg))),
919-
Err(TrySendError::Full(m)) => this.msg = Some(m),
978+
Err(TrySendError::Full(m)) => self.msg = Some(m),
920979
}
921980

922981
// Sending failed - now start listening for notifications or wait for one.
923-
match &mut this.listener {
982+
match self.listener.take() {
924983
None => {
925984
// Start listening and then try sending again.
926-
this.listener = Some(this.sender.channel.send_ops.listen());
985+
self.listener = Some(self.sender.channel.send_ops.listen());
927986
}
928987
Some(l) => {
929-
// Wait for a notification.
930-
match Pin::new(l).poll(cx) {
931-
Poll::Ready(_) => {
932-
this.listener = None;
933-
continue;
934-
}
935-
936-
Poll::Pending => return Poll::Pending,
988+
// Poll using the given strategy
989+
if let Err(l) = S::poll(l, cx) {
990+
self.listener = Some(l);
991+
return Poll::Pending;
937992
}
938993
}
939994
}
940995
}
941996
}
997+
998+
/// Run using the blocking strategy.
999+
fn wait(mut self) -> Result<(), SendError<T>> {
1000+
match self.run_with_strategy::<Blocking>(&mut ()) {
1001+
Poll::Ready(res) => res,
1002+
Poll::Pending => unreachable!(),
1003+
}
1004+
}
1005+
}
1006+
1007+
impl<'a, T> Unpin for Send<'a, T> {}
1008+
1009+
impl<'a, T> Future for Send<'a, T> {
1010+
type Output = Result<(), SendError<T>>;
1011+
1012+
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
1013+
self.run_with_strategy::<NonBlocking<'_>>(cx)
1014+
}
9421015
}
9431016

9441017
/// A future returned by [`Receiver::recv()`].
@@ -951,22 +1024,22 @@ pub struct Recv<'a, T> {
9511024

9521025
impl<'a, T> Unpin for Recv<'a, T> {}
9531026

954-
impl<'a, T> Future for Recv<'a, T> {
955-
type Output = Result<T, RecvError>;
956-
957-
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
958-
let mut this = Pin::new(self);
959-
1027+
impl<'a, T> Recv<'a, T> {
1028+
/// Run this future with the given `Strategy`.
1029+
fn run_with_strategy<S: Strategy>(
1030+
&mut self,
1031+
cx: &mut S::Context,
1032+
) -> Poll<Result<T, RecvError>> {
9601033
loop {
9611034
// Attempt to receive a message.
962-
match this.receiver.try_recv() {
1035+
match self.receiver.try_recv() {
9631036
Ok(msg) => {
9641037
// If the capacity is larger than 1, notify another blocked receive operation.
9651038
// There is no need to notify stream operations because all of them get
9661039
// notified every time a message is sent into the channel.
967-
match this.receiver.channel.queue.capacity() {
1040+
match self.receiver.channel.queue.capacity() {
9681041
Some(1) => {}
969-
Some(_) | None => this.receiver.channel.recv_ops.notify(1),
1042+
Some(_) | None => self.receiver.channel.recv_ops.notify(1),
9701043
}
9711044
return Poll::Ready(Ok(msg));
9721045
}
@@ -975,23 +1048,73 @@ impl<'a, T> Future for Recv<'a, T> {
9751048
}
9761049

9771050
// Receiving failed - now start listening for notifications or wait for one.
978-
match &mut this.listener {
1051+
match self.listener.take() {
9791052
None => {
9801053
// Start listening and then try receiving again.
981-
this.listener = Some(this.receiver.channel.recv_ops.listen());
1054+
self.listener = Some(self.receiver.channel.recv_ops.listen());
9821055
}
9831056
Some(l) => {
984-
// Wait for a notification.
985-
match Pin::new(l).poll(cx) {
986-
Poll::Ready(_) => {
987-
this.listener = None;
988-
continue;
989-
}
990-
991-
Poll::Pending => return Poll::Pending,
1057+
// Poll using the given strategy.
1058+
if let Err(l) = S::poll(l, cx) {
1059+
self.listener = Some(l);
1060+
return Poll::Pending;
9921061
}
9931062
}
9941063
}
9951064
}
9961065
}
1066+
1067+
/// Run with the blocking strategy.
1068+
fn wait(mut self) -> Result<T, RecvError> {
1069+
match self.run_with_strategy::<Blocking>(&mut ()) {
1070+
Poll::Ready(res) => res,
1071+
Poll::Pending => unreachable!(),
1072+
}
1073+
}
1074+
}
1075+
1076+
impl<'a, T> Future for Recv<'a, T> {
1077+
type Output = Result<T, RecvError>;
1078+
1079+
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
1080+
self.run_with_strategy::<NonBlocking<'_>>(cx)
1081+
}
1082+
}
1083+
1084+
/// A strategy used to poll an `EventListener`.
1085+
trait Strategy {
1086+
/// Context needed to be provided to the `poll` method.
1087+
type Context;
1088+
1089+
/// Polls the given `EventListener`.
1090+
///
1091+
/// Returns the `EventListener` back if it was not completed; otherwise,
1092+
/// returns `Ok(())`.
1093+
fn poll(evl: EventListener, cx: &mut Self::Context) -> Result<(), EventListener>;
1094+
}
1095+
1096+
/// Non-blocking strategy for use in asynchronous code.
1097+
struct NonBlocking<'a>(&'a mut ());
1098+
1099+
impl<'a> Strategy for NonBlocking<'a> {
1100+
type Context = Context<'a>;
1101+
1102+
fn poll(mut evl: EventListener, cx: &mut Context<'a>) -> Result<(), EventListener> {
1103+
match Pin::new(&mut evl).poll(cx) {
1104+
Poll::Ready(()) => Ok(()),
1105+
Poll::Pending => Err(evl),
1106+
}
1107+
}
1108+
}
1109+
1110+
/// Blocking strategy for use in synchronous code.
1111+
struct Blocking;
1112+
1113+
impl Strategy for Blocking {
1114+
type Context = ();
1115+
1116+
fn poll(evl: EventListener, _cx: &mut ()) -> Result<(), EventListener> {
1117+
evl.wait();
1118+
Ok(())
1119+
}
9971120
}

tests/bounded.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,22 @@ fn smoke() {
2525
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
2626
}
2727

28+
#[test]
29+
fn smoke_blocking() {
30+
let (s, r) = bounded(1);
31+
32+
s.send_blocking(7).unwrap();
33+
assert_eq!(r.try_recv(), Ok(7));
34+
35+
s.send_blocking(8).unwrap();
36+
assert_eq!(future::block_on(r.recv()), Ok(8));
37+
38+
future::block_on(s.send(9)).unwrap();
39+
assert_eq!(r.recv_blocking(), Ok(9));
40+
41+
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
42+
}
43+
2844
#[test]
2945
fn capacity() {
3046
for i in 1..10 {

tests/unbounded.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,22 @@ fn smoke() {
2424
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
2525
}
2626

27+
#[test]
28+
fn smoke_blocking() {
29+
let (s, r) = unbounded();
30+
31+
s.send_blocking(7).unwrap();
32+
assert_eq!(r.try_recv(), Ok(7));
33+
34+
s.send_blocking(8).unwrap();
35+
assert_eq!(future::block_on(r.recv()), Ok(8));
36+
37+
future::block_on(s.send(9)).unwrap();
38+
assert_eq!(r.recv_blocking(), Ok(9));
39+
40+
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
41+
}
42+
2743
#[test]
2844
fn capacity() {
2945
let (s, r) = unbounded::<()>();

0 commit comments

Comments
 (0)