use std::{sync::Mutex, time::Duration};
use crate::{
timer::{Timed, Timer, TimerDuration},
Lock, UntimedLock,
};
pub struct TimedLock {
inner: UntimedLock,
timer: Mutex<Timer>,
}
impl TimedLock {
pub const fn new(locked: bool) -> Self {
let duration = if locked {
TimerDuration::Infinite
} else {
TimerDuration::Elapsed
};
Self {
inner: UntimedLock::new(locked),
timer: Mutex::new(Timer::new_const(duration)),
}
}
pub fn new_for(duration: TimerDuration) -> Self {
Self {
inner: UntimedLock::new(!duration.is_elapsed()),
timer: Mutex::new(Timer::new(duration)),
}
}
pub const fn unlocked() -> Self {
Self::new(false)
}
pub const fn locked() -> Self {
Self::new(true)
}
pub fn get_timer(&self) -> Timer {
*self.timer.lock().unwrap()
}
}
impl Timed for TimedLock {
fn has_elapsed(&self) -> bool {
!self.is_locked()
}
fn time_left(&self) -> TimerDuration {
if !self.is_locked() {
return TimerDuration::Elapsed;
}
self.timer.lock().unwrap().time_left()
}
}
impl TimedLock {
pub fn lock_for(&self, duration: TimerDuration) {
if duration == TimerDuration::Elapsed {
self.unlock();
return;
}
*self.timer.lock().unwrap() = Timer::new(duration);
self.relock();
}
pub fn change_lock_time(
&self,
f: impl FnOnce(TimerDuration) -> TimerDuration,
if_unlocked: TimerDuration,
) {
if !self.is_locked() {
self.lock_for(f(if_unlocked));
return;
}
let timer = self.timer.lock().unwrap();
let old_time = timer.time_left();
let new_time = f(old_time);
drop(timer);
self.lock_for(new_time);
}
pub fn reduce_lock_time(&self, duration: TimerDuration, lock_if_unlocked: bool) {
self.change_lock_time(
|old_time| old_time.min(duration),
if lock_if_unlocked {
TimerDuration::Infinite
} else {
TimerDuration::Elapsed
},
);
}
pub fn increase_lock_time(&self, duration: TimerDuration) {
self.change_lock_time(|old_time| old_time.max(duration), TimerDuration::Elapsed);
}
pub fn lock_for_ms(&self, duration_ms: u64) {
self.lock_for(TimerDuration::from(duration_ms))
}
fn relock(&self) {
if self.inner.is_locked() {
self.inner.unlock();
}
self.inner.lock();
}
}
impl Lock for TimedLock {
fn is_locked(&self) -> bool {
let timer = self.timer.lock().unwrap();
if timer.has_elapsed() {
drop(timer);
self.unlock();
return false;
}
true
}
fn lock(&self) {
self.lock_for(TimerDuration::Infinite);
}
fn try_lock(&self) -> bool {
*self.timer.lock().unwrap() = Timer::new(TimerDuration::Infinite);
if self.inner.is_locked() {
self.inner.unlock();
}
self.inner.try_lock()
}
fn unlock(&self) {
*self.timer.lock().unwrap() = Timer::new(TimerDuration::Elapsed);
self.inner.unlock()
}
fn wait_here(&self) {
while self.is_locked() {
let time = self.timer.lock().unwrap().time_left();
self.inner.wait_here_for(time);
}
}
fn wait_here_for(&self, timeout: TimerDuration) {
let timer = Timer::new(timeout);
while !timer.has_elapsed() && self.is_locked() {
let time = self.timer.lock().unwrap().time_left();
self.inner.wait_here_for(time.min(timer.time_left()));
}
}
fn wait_here_for_ms(&self, timeout_ms: u64) {
self.wait_here_for(Duration::from_millis(timeout_ms).into());
}
}