use std::{
sync::{
atomic::{AtomicU32, Ordering},
Condvar, Mutex,
},
thread,
time::Duration,
};
use crate::{
timer::{Timed, Timer, TimerDuration},
Lock,
};
pub struct UntimedLock {
locked: Mutex<bool>,
double_unlock: Mutex<bool>,
waiting: AtomicU32,
condvar: Condvar,
}
impl UntimedLock {
pub const fn new(locked: bool) -> Self {
Self {
locked: Mutex::new(locked),
double_unlock: Mutex::new(false),
waiting: AtomicU32::new(0),
condvar: Condvar::new(),
}
}
pub const fn unlocked() -> Self {
Self::new(false)
}
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)));
}
}