diff --git a/tests-integration/tests/oneshot_test/main.rs b/tests-integration/tests/oneshot_test/main.rs index 69db4ddb..7dadc8e1 100644 --- a/tests-integration/tests/oneshot_test/main.rs +++ b/tests-integration/tests/oneshot_test/main.rs @@ -412,3 +412,42 @@ fn concurrent_drop_sender_and_poll_to_completion() { receiver_thread.join().unwrap(); sender_thread.join().unwrap(); } + +#[test] +fn poll_then_complete_receiver_during_sender_wake() { + for round in 0..if cfg!(miri) { 128 } else { 1024 } { + let send = round % 2 == 0; + let (sender, receiver) = oneshot::channel::(); + let mut receiver = receiver.into_future(); + let (waker, _waker_probe) = WakerProbe::new(); + let mut context = Context::from_waker(&waker); + + // Register the waker first so sending or dropping the sender takes the waking path. + assert!(matches!( + Pin::new(&mut receiver).poll(&mut context), + Poll::Pending + )); + + let sender_thread = spawn_named("sender", move || { + if send { + sender.send(999).unwrap(); + } else { + drop(sender); + } + }); + + spin_until("poll ready", || { + match Pin::new(&mut receiver).poll(&mut context) { + Poll::Ready(Ok(999)) if send => true, + Poll::Ready(Err(oneshot::RecvError::Disconnected)) if !send => true, + Poll::Ready(result) => panic!("unexpected result: {result:?}"), + Poll::Pending => false, + } + }); + + // The completed receiver frees the channel, possibly before the sender returns from waking + // it. + drop(receiver); + sender_thread.join().unwrap(); + } +}