microlock 0.3.1

A crate for waiting: Small locks and other timing things!
Documentation
use std::{
    sync::{
        atomic::{AtomicU32, Ordering},
        Condvar, Mutex,
    },
    thread,
    time::Duration,
};

use crate::{
    timer::{Timed, Timer, TimerDuration},
    Lock,
};

/// An untimed lock. This can be locked and unlocked, but it will never unlock
/// on its own (see [`crate::TimedLock`] for an expiring lock). If a thread
/// calls wait_here on a locked lock, it will wait until the lock is unlocked
/// by another thread.
pub struct UntimedLock {
    locked: Mutex<bool>,
    double_unlock: Mutex<bool>,
    waiting: AtomicU32,
    condvar: Condvar,
}

impl UntimedLock {
    /// Creates a new untimed lock that is either unlocked or locked.
    pub const fn new(locked: bool) -> Self {
        Self {
            locked: Mutex::new(locked),
            double_unlock: Mutex::new(false),
            waiting: AtomicU32::new(0),
            condvar: Condvar::new(),
        }
    }

    /// Creates a new untimed lock that is unlocked.
    pub const fn unlocked() -> Self {
        Self::new(false)
    }

    /// Creates a new untimed lock that is locked.
    pub const fn locked() -> Self {
        Self::new(true)
    }
}

impl Lock for UntimedLock {
    fn is_locked(&self) -> bool {
        *self.locked.lock().unwrap()
    }

    fn lock(&self) {
        *self.locked.lock().unwrap() = true;
        *self.double_unlock.lock().unwrap() = false;
    }

    fn try_lock(&self) -> bool {
        let mut double_unlock = self.double_unlock.lock().unwrap();
        if !*double_unlock {
            *self.locked.lock().unwrap() = true;
            return true;
        }
        *double_unlock = false;
        drop(double_unlock);
        false
    }

    fn unlock(&self) {
        {
            let mut locked = self.locked.lock().unwrap();
            let mut double_unlock = self.double_unlock.lock().unwrap();
            if !*locked {
                *double_unlock = true;
                return;
            }
            *locked = false;
            self.condvar.notify_all();
        }
        while self.waiting.load(Ordering::Relaxed) > 0 {
            thread::yield_now();
        }
    }

    fn wait_here(&self) {
        self.waiting.fetch_add(1, Ordering::Relaxed);
        {
            let mut locked = self.locked.lock().unwrap();
            while *locked {
                locked = self.condvar.wait(locked).unwrap();
            }
        }
        self.waiting.fetch_sub(1, Ordering::Relaxed);
        while self.waiting.load(Ordering::Relaxed) > 0 {
            thread::yield_now();
        }
    }

    fn wait_here_for(&self, timeout: TimerDuration) {
        self.waiting.fetch_add(1, Ordering::Relaxed);
        {
            let mut locked = self.locked.lock().unwrap();
            let timer = Timer::new(timeout);
            while *locked && !timer.has_elapsed() {
                locked = self
                    .condvar
                    .wait_timeout(locked, timer.time_left().to_real())
                    .unwrap()
                    .0;
            }
        }
        self.waiting.fetch_sub(1, Ordering::Relaxed);
        while self.waiting.load(Ordering::Relaxed) > 0 {
            thread::yield_now();
        }
    }

    fn wait_here_for_ms(&self, timeout_ms: u64) {
        self.wait_here_for(TimerDuration::Real(Duration::from_millis(timeout_ms)));
    }
}