use std::io;
use std::time::{Duration, Instant};
use win_sync_sys::{Event, WaitOutcome, wait_for_single_object};
use super::pumped::{next, readiness_set};
use super::pushes::{BOUND, GIVEN, Harness, Ring, given, temp};
use super::setup;
use crate::dioring::{DeliveryMode, Setup};
use crate::held_pool::{Held, one_thread_pool};
use durable_ioring::DurableRing;
use durable_ioring::Entry;
const QUIET: Duration = Duration::from_millis(100);
fn signalled(files: Vec<crate::dioring::TargetSetup>) -> Harness {
let ring = Ring::new(Setup {
delivery: DeliveryMode::Signalled(None),
..setup(files, Vec::new(), None)
})
.expect("build a signalled instance")
.0;
assert!(
ring.delivery.as_ref().expect("live").is_signalled(),
"built as the setup said"
);
Harness::new(ring)
}
fn set_within(signal: &Event, timeout: Duration) -> bool {
match wait_for_single_object(signal, Some(timeout)).expect("a wait") {
WaitOutcome::Signalled => true,
WaitOutcome::TimedOut => false,
other => panic!("an event ended a wait as {other:?}"),
}
}
fn settle(signal: &Event) {
for _ in 0..50 {
if !set_within(signal, QUIET) {
return;
}
}
panic!("the signal never went quiet");
}
#[test]
fn a_signalled_instance_delivers_nothing_until_the_consumer_drains() {
let temp = temp(&[0; 64]);
let mut harness = signalled(vec![given(GIVEN, &temp)]);
let signal = harness.ring.completion_signal().expect("the signal");
settle(&signal);
let id = harness.write(GIVEN, 0, b"signal", 1, 7);
assert!(
set_within(&signal, BOUND),
"a completion arrived and the pool said so"
);
let started = Instant::now();
while started.elapsed() < QUIET {
assert!(harness.ring.pop().expect("pop").is_none());
std::thread::sleep(Duration::from_millis(5));
}
assert!(
!readiness_set(&mut harness),
"nothing has drained, so nothing is poppable"
);
assert!(
harness.ring.pump(Some(Duration::ZERO)).expect("pump"),
"one pump drains it"
);
let entry = harness.ring.pop().expect("pop").expect("an entry");
harness
.oracle
.observe(&entry)
.expect("the oracle accepts the entry");
match entry {
Entry::Op(done) => assert_eq!(done.id, id),
other => panic!("expected the write's completion, got {other:?}"),
}
assert!(
readiness_set(&mut harness),
"the pump that drained it set the readiness signal"
);
harness.finish();
}
#[test]
fn the_signal_is_set_when_a_completion_arrives_and_not_before() {
let temp = temp(&[0; 64]);
let mut harness = signalled(vec![given(GIVEN, &temp)]);
let signal = harness.ring.completion_signal().expect("the signal");
settle(&signal);
assert!(!set_within(&signal, QUIET), "nothing in flight, so quiet");
harness.write(GIVEN, 0, b"arrives", 1, 1);
assert!(set_within(&signal, BOUND), "the completion set it");
}
#[test]
fn a_consumers_wait_then_a_zero_pump_delivers() {
let temp = temp(&[0; 64]);
let mut harness = signalled(vec![given(GIVEN, &temp)]);
let signal = harness.ring.completion_signal().expect("the signal");
settle(&signal);
harness.write(GIVEN, 0, b"consumer", 1, 1);
assert!(set_within(&signal, BOUND));
let started = Instant::now();
let mut ready = false;
while !ready {
assert!(started.elapsed() < BOUND, "no pump took the completion");
ready = harness.ring.pump(Some(Duration::ZERO)).expect("pump");
std::thread::sleep(Duration::from_millis(1));
}
assert!(matches!(
harness.ring.pop().expect("pop"),
Some(Entry::Op(_))
));
}
#[test]
fn a_pump_after_the_consumer_took_the_signal_still_delivers_at_once() {
let temp = temp(&[0; 64]);
let mut harness = signalled(vec![given(GIVEN, &temp)]);
let signal = harness.ring.completion_signal().expect("the signal");
settle(&signal);
harness.write(GIVEN, 0, b"taken", 1, 1);
assert!(set_within(&signal, BOUND));
let started = Instant::now();
let ready = harness.ring.pump(Some(BOUND)).expect("pump");
assert!(ready, "what the signal announced is delivered");
assert!(started.elapsed() < BOUND, "took {:?}", started.elapsed());
}
#[test]
fn a_consumer_that_resets_the_signal_costs_nothing_but_its_own_wakeup() {
let temp = temp(&[0; 64]);
let mut harness = signalled(vec![given(GIVEN, &temp)]);
let signal = harness.ring.completion_signal().expect("the signal");
let id = harness.write(GIVEN, 0, b"reset", 1, 1);
for _ in 0..10 {
signal.reset().expect("the consumer's handle may reset it");
std::thread::sleep(Duration::from_millis(2));
}
match next(&mut harness) {
Entry::Op(done) => assert_eq!(done.id, id),
other => panic!("expected the write's completion, got {other:?}"),
}
harness.finish();
}
#[test]
fn only_a_signalled_instance_has_a_signal_and_each_call_is_the_same_event() {
let temp = temp(&[0; 16]);
let pooled = Harness::new(super::pushes::instance(
vec![given(GIVEN, &temp)],
Vec::new(),
));
let error = pooled
.ring
.completion_signal()
.expect_err("a pool has none");
assert_eq!(error.kind(), io::ErrorKind::Unsupported);
let temp = super::pushes::temp(&[0; 16]);
let harness = signalled(vec![given(GIVEN, &temp)]);
let first = harness.ring.completion_signal().expect("a signal");
let second = harness.ring.completion_signal().expect("a signal");
settle(&first);
settle(&second);
first.set().expect("set it through one duplicate");
assert!(
set_within(&second, BOUND),
"and the other sees it: one event"
);
}
#[test]
fn a_signalled_instance_delivers_on_the_pool_its_environment_names() {
use windows_threadpool_sys::callback_env::CallbackEnviron;
use windows_threadpool_sys::pool::ThreadpoolPool;
let threads = ThreadpoolPool::new().expect("create a pool");
let mut env = CallbackEnviron::new();
env.set_pool(&threads);
let file = temp(&[0; 16]);
let ring = Ring::new(Setup {
delivery: DeliveryMode::Signalled(Some(&mut env)),
..setup(vec![given(GIVEN, &file)], Vec::new(), None)
})
.expect("build")
.0;
let mut harness = Harness::new(ring);
let signal = harness.ring.completion_signal().expect("the signal");
settle(&signal);
harness.write(GIVEN, 0, b"env", 1, 1);
assert!(
set_within(&signal, BOUND),
"the named pool's callback set the signal"
);
assert!(matches!(next(&mut harness), Entry::Op(_)));
harness.finish();
}
#[test]
fn a_signalled_instance_does_not_offer_the_rings_own_event() {
let temp = temp(&[0; 64]);
let harness = signalled(vec![given(GIVEN, &temp)]);
let error = harness
.ring
.completion_event()
.expect_err("the wake owns the ring's event");
assert_eq!(error.kind(), io::ErrorKind::Unsupported);
harness.finish();
}
#[test]
fn a_signalled_instance_waits_for_the_thread_of_the_pool_its_environment_names() {
use windows_threadpool_sys::callback_env::CallbackEnviron;
let threads = one_thread_pool();
let mut env = CallbackEnviron::new();
env.set_pool(&threads);
let file = temp(&[0; 16]);
let ring = Ring::new(Setup {
delivery: DeliveryMode::Signalled(Some(&mut env)),
..setup(vec![given(GIVEN, &file)], Vec::new(), None)
})
.expect("build")
.0;
let mut harness = Harness::new(ring);
let signal = harness.ring.completion_signal().expect("the signal");
settle(&signal);
let held = Held::start(&mut env);
harness.write(GIVEN, 0, b"held", 1, 1);
assert!(
!set_within(&signal, QUIET),
"the named pool's only thread is held, so the signal was not set"
);
held.release();
assert!(
set_within(&signal, BOUND),
"released, the named pool's callback set the signal"
);
assert!(matches!(next(&mut harness), Entry::Op(_)));
harness.finish();
}
#[test]
fn a_signalled_instance_ends_with_work_outstanding_and_the_signal_outlives_it() {
let temp = temp(&[0; 64]);
let mut harness = signalled(vec![given(GIVEN, &temp)]);
let signal = harness.ring.completion_signal().expect("the signal");
harness.write(GIVEN, 0, b"closing", 1, 1);
let leftovers = harness.ring.close();
assert!(leftovers.held.is_empty() && leftovers.failures.is_empty());
signal.set().expect("the consumer's handle is still good");
let temp = super::pushes::temp(&[0; 64]);
let mut harness = signalled(vec![given(GIVEN, &temp)]);
harness.write(GIVEN, 0, b"dropping", 1, 1);
drop(harness);
assert!(!temp.is_held(), "the ring let go of the file");
}