@@ -78,6 +78,9 @@ struct Channel<T> {
78
78
/// Stream operations while the channel is empty and not closed.
79
79
stream_ops : Event ,
80
80
81
+ /// Closed operations while the channel is not closed.
82
+ closed_ops : Event ,
83
+
81
84
/// The number of currently active `Sender`s.
82
85
sender_count : AtomicUsize ,
83
86
@@ -97,6 +100,7 @@ impl<T> Channel<T> {
97
100
// Notify all receive and stream operations.
98
101
self . recv_ops . notify ( usize:: MAX ) ;
99
102
self . stream_ops . notify ( usize:: MAX ) ;
103
+ self . closed_ops . notify ( usize:: MAX ) ;
100
104
101
105
true
102
106
} else {
@@ -136,6 +140,7 @@ pub fn bounded<T>(cap: usize) -> (Sender<T>, Receiver<T>) {
136
140
send_ops : Event :: new ( ) ,
137
141
recv_ops : Event :: new ( ) ,
138
142
stream_ops : Event :: new ( ) ,
143
+ closed_ops : Event :: new ( ) ,
139
144
sender_count : AtomicUsize :: new ( 1 ) ,
140
145
receiver_count : AtomicUsize :: new ( 1 ) ,
141
146
} ) ;
@@ -177,6 +182,7 @@ pub fn unbounded<T>() -> (Sender<T>, Receiver<T>) {
177
182
send_ops : Event :: new ( ) ,
178
183
recv_ops : Event :: new ( ) ,
179
184
stream_ops : Event :: new ( ) ,
185
+ closed_ops : Event :: new ( ) ,
180
186
sender_count : AtomicUsize :: new ( 1 ) ,
181
187
receiver_count : AtomicUsize :: new ( 1 ) ,
182
188
} ) ;
@@ -266,6 +272,29 @@ impl<T> Sender<T> {
266
272
} )
267
273
}
268
274
275
+ /// Completes when all receivers have dropped.
276
+ ///
277
+ /// This allows the producers to get notified when interest in the produced values is canceled and immediately stop doing work.
278
+ ///
279
+ /// # Examples
280
+ ///
281
+ /// ```
282
+ /// # futures_lite::future::block_on(async {
283
+ /// use async_channel::{unbounded, SendError};
284
+ ///
285
+ /// let (s, r) = unbounded::<i32>();
286
+ /// drop(r);
287
+ /// s.closed().await;
288
+ /// # });
289
+ /// ```
290
+ pub fn closed ( & self ) -> Closed < ' _ , T > {
291
+ Closed :: _new ( ClosedInner {
292
+ sender : self ,
293
+ listener : None ,
294
+ _pin : PhantomPinned ,
295
+ } )
296
+ }
297
+
269
298
/// Sends a message into this channel using the blocking strategy.
270
299
///
271
300
/// If the channel is full, this method will block until there is room.
@@ -1288,6 +1317,59 @@ impl<T> EventListenerFuture for RecvInner<'_, T> {
1288
1317
}
1289
1318
}
1290
1319
1320
+ easy_wrapper ! {
1321
+ /// A future returned by [`Sender::closed()`].
1322
+ #[ derive( Debug ) ]
1323
+ #[ must_use = "futures do nothing unless you `.await` or poll them" ]
1324
+ pub struct Closed <' a, T >( ClosedInner <' a, T > => ( ) ) ;
1325
+ #[ cfg( all( feature = "std" , not( target_family = "wasm" ) ) ) ]
1326
+ pub ( crate ) wait( ) ;
1327
+ }
1328
+
1329
+ pin_project ! {
1330
+ #[ derive( Debug ) ]
1331
+ #[ project( !Unpin ) ]
1332
+ struct ClosedInner <' a, T > {
1333
+ // Reference to the sender.
1334
+ sender: & ' a Sender <T >,
1335
+
1336
+ // Listener waiting on the channel.
1337
+ listener: Option <EventListener >,
1338
+
1339
+ // Keeping this type `!Unpin` enables future optimizations.
1340
+ #[ pin]
1341
+ _pin: PhantomPinned
1342
+ }
1343
+ }
1344
+
1345
+ impl < ' a , T > EventListenerFuture for ClosedInner < ' a , T > {
1346
+ type Output = ( ) ;
1347
+
1348
+ /// Run this future with the given `Strategy`.
1349
+ fn poll_with_strategy < ' x , S : Strategy < ' x > > (
1350
+ self : Pin < & mut Self > ,
1351
+ strategy : & mut S ,
1352
+ cx : & mut S :: Context ,
1353
+ ) -> Poll < ( ) > {
1354
+ let this = self . project ( ) ;
1355
+
1356
+ loop {
1357
+ // Check if the channel is closed.
1358
+ if this. sender . is_closed ( ) {
1359
+ return Poll :: Ready ( ( ) ) ;
1360
+ }
1361
+
1362
+ // Not closed - now start listening for notifications or wait for one.
1363
+ if this. listener . is_some ( ) {
1364
+ // Poll using the given strategy
1365
+ ready ! ( S :: poll( strategy, & mut * this. listener, cx) ) ;
1366
+ } else {
1367
+ * this. listener = Some ( this. sender . channel . closed_ops . listen ( ) ) ;
1368
+ }
1369
+ }
1370
+ }
1371
+ }
1372
+
1291
1373
#[ cfg( feature = "std" ) ]
1292
1374
use std:: process:: abort;
1293
1375
0 commit comments