#[cfg(loom)]
use loom::sync::atomic::{AtomicBool, Ordering};
#[cfg(not(loom))]
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
pub struct ReadyGate {
ready: AtomicBool,
}
impl Default for ReadyGate {
fn default() -> Self {
Self::new()
}
}
impl ReadyGate {
pub fn new() -> Self {
Self {
ready: AtomicBool::new(false),
}
}
pub fn set_ready(&self) {
self.ready.store(true, Ordering::Release);
}
pub fn set_not_ready(&self) {
self.ready.store(false, Ordering::Release);
}
pub fn is_ready(&self) -> bool {
self.ready.load(Ordering::Acquire)
}
pub async fn wait_until_ready(&self) {
loop {
if self.is_ready() {
return;
}
tokio::time::sleep(Duration::from_millis(100)).await;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn starts_not_ready() {
let gate = ReadyGate::new();
assert!(!gate.is_ready());
}
#[tokio::test]
async fn becomes_ready() {
let gate = ReadyGate::new();
gate.set_ready();
assert!(gate.is_ready());
}
#[tokio::test]
async fn can_become_not_ready() {
let gate = ReadyGate::new();
gate.set_ready();
gate.set_not_ready();
assert!(!gate.is_ready());
}
#[tokio::test]
async fn wait_until_ready() {
let gate = ReadyGate::new();
let gate_clone = std::sync::Arc::new(gate);
let g = gate_clone.clone();
tokio::spawn(async move {
tokio::time::sleep(Duration::from_millis(50)).await;
g.set_ready();
});
gate_clone.wait_until_ready().await;
assert!(gate_clone.is_ready());
}
}