blob: 8200e23b041f51d8c60418d8119f398ba4c13c03 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use std::cell::Cell;
use std::sync::Arc;
use std::sync::Barrier;
use std::task::Poll;
use std::task::Waker;
use std::thread;
use asyncband::blocking::FutureExt;
use asyncband::completion;
use tests_integration::PanicWake;
use tests_integration::WakeCounter;
use tests_integration::assert_completes_without_deadlock;
use tests_integration::poll_with;
use tests_integration::waker_on_drop;
use tests_integration::waker_on_wake;
struct NotClone(String);
#[test]
fn all_observers_borrow_the_same_non_clone_value() {
let (completer, completion) = completion::new();
let first = completion.clone();
let second = completion.clone();
drop(completion);
assert!(completer.complete(NotClone(String::from("ready"))).is_ok());
let first_value = FutureExt::block_on(first.wait()).unwrap();
let second_value = FutureExt::block_on(second.wait()).unwrap();
let repeated = FutureExt::block_on(first.wait()).unwrap();
assert_eq!(first_value.0.as_str(), "ready");
assert!(std::ptr::eq(first_value, second_value));
assert!(std::ptr::eq(first_value, repeated));
let late = first.clone();
drop(first);
let late_value = FutureExt::block_on(late.wait()).unwrap();
assert!(std::ptr::eq(second_value, late_value));
}
#[test]
fn completer_transfers_a_send_only_value_between_threads() {
let (completer, completion) = completion::new::<Cell<u8>>();
let worker = thread::spawn(move || completer.complete(Cell::new(7)));
assert_eq!(FutureExt::block_on(completion.wait()).unwrap().get(), 7);
worker.join().unwrap().unwrap();
}
#[test]
fn complete_returns_the_value_when_no_observers_remain() {
let (completer, completion) = completion::new();
drop(completion);
assert_eq!(
completer.complete(String::from("unobserved")).unwrap_err(),
"unobserved"
);
}
#[test]
fn completed_payload_is_released_with_the_last_observer() {
let payload = Arc::new(());
let (completer, completion) = completion::new();
completer.complete(payload.clone()).unwrap();
assert_eq!(Arc::strong_count(&payload), 2);
drop(completion);
assert_eq!(Arc::strong_count(&payload), 1);
}
#[test]
fn abandonment_wakes_registered_waits_and_is_visible_to_late_observers() {
let (completer, first) = completion::new::<usize>();
let second = first.clone();
let first_tracker = Arc::new(WakeCounter::default());
let second_tracker = Arc::new(WakeCounter::default());
let first_waker = Waker::from(first_tracker.clone());
let second_waker = Waker::from(second_tracker.clone());
let mut first_wait = Box::pin(first.wait());
let mut second_wait = Box::pin(second.wait());
assert!(poll_with(first_wait.as_mut(), &first_waker).is_pending());
assert!(poll_with(second_wait.as_mut(), &second_waker).is_pending());
drop(completer);
assert_eq!(first_tracker.count(), 1);
assert_eq!(second_tracker.count(), 1);
assert!(matches!(
poll_with(first_wait.as_mut(), &first_waker),
Poll::Ready(Err(_))
));
assert!(matches!(
poll_with(second_wait.as_mut(), &second_waker),
Poll::Ready(Err(_))
));
let late = first.clone();
assert!(FutureExt::block_on(late.wait()).is_err());
}
#[test]
fn payload_errors_remain_distinct_from_abandonment() {
let (completer, completion) = completion::new::<Result<u8, &'static str>>();
completer.complete(Err("domain error")).unwrap();
assert_eq!(
FutureExt::block_on(completion.wait()),
Ok(&Err("domain error"))
);
let (completer, completion) = completion::new::<Result<u8, &'static str>>();
drop(completer);
assert!(FutureExt::block_on(completion.wait()).is_err());
}
#[test]
fn cancelling_a_wait_releases_only_its_waker() {
let (completer, completion) = completion::new();
let cancelled_tracker = Arc::new(WakeCounter::default());
let waiting_tracker = Arc::new(WakeCounter::default());
let cancelled_waker = Waker::from(cancelled_tracker.clone());
let waiting_waker = Waker::from(waiting_tracker.clone());
let baseline = Arc::strong_count(&cancelled_tracker);
let mut cancelled = Box::pin(completion.wait());
let mut waiting = Box::pin(completion.wait());
assert!(poll_with(cancelled.as_mut(), &cancelled_waker).is_pending());
assert!(poll_with(waiting.as_mut(), &waiting_waker).is_pending());
assert_eq!(Arc::strong_count(&cancelled_tracker), baseline + 1);
drop(cancelled);
assert_eq!(Arc::strong_count(&cancelled_tracker), baseline);
completer.complete(7).unwrap();
assert_eq!(cancelled_tracker.count(), 0);
assert_eq!(waiting_tracker.count(), 1);
assert_eq!(
poll_with(waiting.as_mut(), &waiting_waker),
Poll::Ready(Ok(&7))
);
}
#[test]
fn cancelling_after_wake_does_not_consume_the_shared_result() {
let (completer, first) = completion::new();
let second = first.clone();
let tracker = Arc::new(WakeCounter::default());
let waker = Waker::from(tracker.clone());
let mut wait = Box::pin(first.wait());
assert!(poll_with(wait.as_mut(), &waker).is_pending());
completer.complete(9).unwrap();
assert_eq!(tracker.count(), 1);
drop(wait);
assert_eq!(FutureExt::block_on(second.wait()), Ok(&9));
}
#[test]
fn cancelling_after_abandonment_does_not_retain_the_waker() {
let (completer, completion) = completion::new::<usize>();
let tracker = Arc::new(WakeCounter::default());
let waker = Waker::from(tracker.clone());
let baseline = Arc::strong_count(&tracker);
let mut wait = Box::pin(completion.wait());
assert!(poll_with(wait.as_mut(), &waker).is_pending());
drop(completer);
assert_eq!(tracker.count(), 1);
assert_eq!(Arc::strong_count(&tracker), baseline);
drop(wait);
assert_eq!(Arc::strong_count(&tracker), baseline);
assert!(FutureExt::block_on(completion.wait()).is_err());
}
#[test]
fn completion_attempts_every_waker_after_one_panics() {
let (completer, first) = completion::new();
let second = first.clone();
let panicking = Waker::from(Arc::new(PanicWake));
let tracker = Arc::new(WakeCounter::default());
let tracked = Waker::from(tracker.clone());
let mut first_wait = Box::pin(first.wait());
let mut second_wait = Box::pin(second.wait());
assert!(poll_with(first_wait.as_mut(), &panicking).is_pending());
assert!(poll_with(second_wait.as_mut(), &tracked).is_pending());
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| completer.complete(11)));
assert!(result.is_err());
assert_eq!(tracker.count(), 1);
assert_eq!(
poll_with(second_wait.as_mut(), &tracked),
Poll::Ready(Ok(&11))
);
}
#[test]
fn wake_callbacks_run_outside_the_completion_lock() {
assert_completes_without_deadlock(|| {
let (completer, completion) = completion::new();
let callback_completion = completion.clone();
let waker = waker_on_wake(move || {
assert_eq!(FutureExt::block_on(callback_completion.wait()), Ok(&13));
});
let mut wait = Box::pin(completion.wait());
assert!(poll_with(wait.as_mut(), &waker).is_pending());
completer.complete(13).unwrap();
assert_eq!(poll_with(wait.as_mut(), &waker), Poll::Ready(Ok(&13)));
drop(wait);
let (completer, completion) = completion::new::<usize>();
let callback_completion = completion.clone();
let waker = waker_on_wake(move || {
assert!(FutureExt::block_on(callback_completion.wait()).is_err());
});
let mut wait = Box::pin(completion.wait());
assert!(poll_with(wait.as_mut(), &waker).is_pending());
drop(completer);
assert!(matches!(
poll_with(wait.as_mut(), &waker),
Poll::Ready(Err(_))
));
});
}
#[test]
fn replaced_wakers_are_dropped_outside_the_completion_lock() {
assert_completes_without_deadlock(|| {
let (completer, completion) = completion::new::<usize>();
let old_waker = waker_on_drop(move || drop(completer));
let mut wait = Box::pin(completion.wait());
assert!(poll_with(wait.as_mut(), &old_waker).is_pending());
drop(old_waker);
let tracker = Arc::new(WakeCounter::default());
let replacement = Waker::from(tracker.clone());
assert!(poll_with(wait.as_mut(), &replacement).is_pending());
assert_eq!(tracker.count(), 1);
assert!(matches!(
poll_with(wait.as_mut(), &replacement),
Poll::Ready(Err(_))
));
});
}
#[test]
fn cancelled_wakers_are_dropped_outside_the_completion_lock() {
assert_completes_without_deadlock(|| {
let (completer, completion) = completion::new::<usize>();
let waker = waker_on_drop(move || drop(completer));
let mut wait = Box::pin(completion.wait());
assert!(poll_with(wait.as_mut(), &waker).is_pending());
drop(waker);
drop(wait);
assert!(FutureExt::block_on(completion.wait()).is_err());
});
}
#[test]
fn complete_and_final_observer_drop_linearize_cleanly() {
for _ in 0..100 {
let (completer, completion) = completion::new();
let barrier = Arc::new(Barrier::new(2));
thread::scope(|scope| {
let worker_barrier = barrier.clone();
let worker = scope.spawn(move || {
worker_barrier.wait();
drop(completion);
});
barrier.wait();
let result = completer.complete(17);
worker.join().unwrap();
if let Err(error) = result {
assert_eq!(error, 17);
}
});
}
}