use std::thread::{self, Thread};
use std::sync::atomic::{AtomicBool, Ordering};
#[cfg(test)]
use super::super::test::{terminates, terminates_async};
pub struct ControlBlock {
thread: Thread,
blocked: AtomicBool,
}
impl ControlBlock {
#[cfg_attr(feature = "cargo-clippy", allow(new_without_default_derive))]
pub fn new() -> ControlBlock {
ControlBlock {
thread: thread::current(),
blocked: AtomicBool::new(true),
}
}
pub fn set_changed(&self) {
if self.blocked.swap(false, Ordering::SeqCst) {
self.thread.unpark();
}
}
pub fn wait(&self) {
while self.blocked.load(Ordering::SeqCst) {
thread::park();
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn blocked() {
let ctrl = ControlBlock::new();
assert!(!terminates(100, move || ctrl.wait()));
}
#[test]
fn wait_after_change() {
let ctrl = ControlBlock::new();
ctrl.set_changed();
assert!(terminates(50, move || ctrl.wait()));
}
#[test]
fn wait_after_multiple_changes() {
let ctrl = ControlBlock::new();
ctrl.set_changed();
ctrl.set_changed();
ctrl.set_changed();
ctrl.set_changed();
assert!(terminates(50, move || ctrl.wait()));
}
#[test]
fn wait_threaded_wakeup() {
use std::sync::Arc;
let ctrl = Arc::new(ControlBlock::new());
let ctrl2 = ctrl.clone();
let terminated = terminates_async(500,
move || ctrl.wait(),
move || ctrl2.set_changed());
assert!(terminated);
}
}