use std::time::Duration;
#[cfg(not(target_arch = "wasm32"))]
use std::sync::Arc;
#[cfg(not(target_arch = "wasm32"))]
use std::time::Instant;
#[cfg(not(target_arch = "wasm32"))]
use kovan_channel::signal::Signal;
#[cfg(not(target_arch = "wasm32"))]
use kovan_queue::array_queue::ArrayQueue;
#[cfg(not(target_arch = "wasm32"))]
const STALL_WAITER_SLOTS: usize = 256;
#[cfg(not(target_arch = "wasm32"))]
pub(crate) struct StallSignal {
waiters: ArrayQueue<Arc<Signal>>,
}
#[cfg(target_arch = "wasm32")]
pub(crate) struct StallSignal;
#[cfg(target_arch = "wasm32")]
impl StallSignal {
pub(crate) fn new() -> Self {
Self
}
pub(crate) fn wait(&self, _timeout: Duration) {}
pub(crate) fn notify_all(&self) {}
}
#[cfg(not(target_arch = "wasm32"))]
impl StallSignal {
pub(crate) fn new() -> Self {
Self {
waiters: ArrayQueue::new(STALL_WAITER_SLOTS),
}
}
pub(crate) fn wait(&self, timeout: Duration) {
let signal = Arc::new(Signal::new());
let _ = self.waiters.push(Arc::clone(&signal));
signal.wait_deadline(Instant::now() + timeout);
signal.notify();
}
pub(crate) fn notify_all(&self) {
while let Some(waiter) = self.waiters.pop() {
if !waiter.is_notified() {
waiter.notify();
}
}
}
}
impl Default for StallSignal {
fn default() -> Self {
Self::new()
}
}
#[cfg(all(test, not(target_arch = "wasm32")))]
mod tests {
use super::*;
use std::thread;
#[test]
fn stall_signal_wakes_a_registered_waiter() {
let signal = Arc::new(StallSignal::new());
let waiter = Arc::clone(&signal);
let handle = thread::spawn(move || {
waiter.wait(Duration::from_secs(5));
});
let deadline = Instant::now() + Duration::from_secs(10);
while !handle.is_finished() && Instant::now() < deadline {
signal.notify_all();
thread::yield_now();
}
handle.join().expect("waiter thread panicked");
}
#[test]
fn stall_signal_wait_returns_on_its_deadline_without_a_notify() {
let signal = StallSignal::new();
let start = Instant::now();
signal.wait(Duration::from_millis(20));
assert!(start.elapsed() >= Duration::from_millis(15));
}
#[test]
fn stall_signal_survives_more_waiters_than_slots() {
let signal = StallSignal::new();
for _ in 0..(STALL_WAITER_SLOTS * 2) {
let _ = signal.waiters.push(Arc::new(Signal::new()));
}
signal.notify_all();
assert!(signal.waiters.pop().is_none());
}
}