Skip to content

Commit 324218f

Browse files
committed
Merge tag 'tokio-1.47.4' (#8003)
2 parents 43134f1 + aa65d0d commit 324218f

3 files changed

Lines changed: 38 additions & 10 deletions

File tree

tokio/CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,15 @@ The MSRV is increased to 1.71.
273273
[#7672]: https://github.com/tokio-rs/tokio/pull/7672
274274
[#7675]: https://github.com/tokio-rs/tokio/pull/7675
275275

276-
# 1.47.3 (Januar 3rd, 2026)
276+
# 1.47.4 (April 2nd, 2026)
277+
278+
### Fixed
279+
280+
* sync: fix panic in `Chan::recv_many` when called with non-empty vector on closed channel ([#7991])
281+
282+
[#7991]: https://github.com/tokio-rs/tokio/pull/7991
283+
284+
# 1.47.3 (January 3rd, 2026)
277285

278286
### Fixed
279287

tokio/src/sync/mpsc/chan.rs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -306,13 +306,11 @@ impl<T, S: Semaphore> Rx<T, S> {
306306
return Ready(Some(value));
307307
}
308308
Some(Read::Closed) => {
309-
// TODO: This check may not be required as it most
310-
// likely can only return `true` at this point. A
311-
// channel is closed when all tx handles are
309+
// A channel is closed when all tx handles are
312310
// dropped. Dropping a tx handle releases memory,
313311
// which ensures that if dropping the tx handle is
314312
// visible, then all messages sent are also visible.
315-
assert!(self.inner.semaphore.is_idle());
313+
debug_assert!(self.inner.semaphore.is_idle());
316314
coop.made_progress();
317315
return Ready(None);
318316
}
@@ -380,13 +378,11 @@ impl<T, S: Semaphore> Rx<T, S> {
380378
if number_added > 0 {
381379
self.inner.semaphore.add_permits(number_added);
382380
}
383-
// TODO: This check may not be required as it most
384-
// likely can only return `true` at this point. A
385-
// channel is closed when all tx handles are
381+
// A channel is closed when all tx handles are
386382
// dropped. Dropping a tx handle releases memory,
387383
// which ensures that if dropping the tx handle is
388384
// visible, then all messages sent are also visible.
389-
assert!(self.inner.semaphore.is_idle());
385+
debug_assert!(self.inner.semaphore.is_idle());
390386
coop.made_progress();
391387
return Ready(number_added);
392388
}
@@ -415,7 +411,7 @@ impl<T, S: Semaphore> Rx<T, S> {
415411
try_recv!();
416412

417413
if rx_fields.rx_closed && self.inner.semaphore.is_idle() {
418-
assert!(buffer.is_empty());
414+
debug_assert_eq!(buffer.len(), initial_length);
419415
coop.made_progress();
420416
Ready(0usize)
421417
} else {

tokio/tests/sync_mpsc.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,30 @@ async fn send_recv_many_unbounded_capacity() {
358358
assert_eq!(expected, buffer);
359359
}
360360

361+
#[maybe_tokio_test]
362+
async fn recv_many_with_non_empty_buffer_bounded_rx_closed_and_idle() {
363+
let (_tx, mut rx) = mpsc::channel::<i32>(1);
364+
365+
let mut buffer: Vec<i32> = vec![1];
366+
367+
rx.close();
368+
369+
assert_eq!(0, rx.recv_many(&mut buffer, 1).await);
370+
assert_eq!(vec![1], buffer);
371+
}
372+
373+
#[maybe_tokio_test]
374+
async fn recv_many_with_non_empty_buffer_unbounded_rx_closed_and_idle() {
375+
let (_tx, mut rx) = mpsc::unbounded_channel::<i32>();
376+
377+
let mut buffer: Vec<i32> = vec![1];
378+
379+
rx.close();
380+
381+
assert_eq!(0, rx.recv_many(&mut buffer, 1).await);
382+
assert_eq!(vec![1], buffer);
383+
}
384+
361385
#[tokio::test]
362386
#[cfg(feature = "full")]
363387
async fn async_send_recv_unbounded() {

0 commit comments

Comments
 (0)