Add try_lock

This commit is contained in:
Daniella / Tove 2024-07-14 03:17:14 +02:00
parent ea3b41eb1d
commit 131bc549d2
Signed by: TudbuT
GPG key ID: B3CF345217F202D3
5 changed files with 116 additions and 81 deletions

2
Cargo.lock generated
View file

@ -4,4 +4,4 @@ version = 3
[[package]] [[package]]
name = "microlock" name = "microlock"
version = "0.1.0" version = "0.2.0"

View file

@ -3,7 +3,7 @@ name = "microlock"
description = "A crate for waiting: Small locks and other timing things!" description = "A crate for waiting: Small locks and other timing things!"
license = "MIT" license = "MIT"
repository = "https://git.tudbut.de/tudbut/microlock" repository = "https://git.tudbut.de/tudbut/microlock"
version = "0.1.0" version = "0.2.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]

View file

@ -1,41 +1,9 @@
mod timed; mod timed;
pub mod timer; pub mod timer;
mod untimed;
pub use timed::*; pub use timed::*;
use timer::{Timed, Timer, TimerDuration}; use timer::TimerDuration;
pub use untimed::*;
use std::{
sync::{Condvar, Mutex},
time::Duration,
};
/// An untimed lock. This can be locked and unlocked, but it will never unlock
/// on its own (see [`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>,
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),
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)
}
}
pub trait Lock { pub trait Lock {
/// Returns if the lock is (still) locked. /// Returns if the lock is (still) locked.
@ -43,6 +11,11 @@ pub trait Lock {
/// Locks the lock indefinitely. In case of a timed one, the previous /// Locks the lock indefinitely. In case of a timed one, the previous
/// target will be replaced without releasing the waiting threads. /// target will be replaced without releasing the waiting threads.
fn lock(&self); fn lock(&self);
/// Locks the lock indefinitely like [`lock`], UNLESS unlock was called
/// more than once. If someone tried to unlock the lock while it was
/// already unlocked, this will NOT lock it. Returns false and resets
/// the double-unlock check if the locking failed.
fn try_lock(&self) -> bool;
/// Unlocks the lock and releases all waiting threads. /// Unlocks the lock and releases all waiting threads.
fn unlock(&self); fn unlock(&self);
/// Makes the current thread wait on this lock until it is /// Makes the current thread wait on this lock until it is
@ -53,41 +26,3 @@ pub trait Lock {
fn wait_here_for(&self, timeout: TimerDuration); fn wait_here_for(&self, timeout: TimerDuration);
fn wait_here_for_ms(&self, timeout_ms: u64); fn wait_here_for_ms(&self, timeout_ms: u64);
} }
impl Lock for UntimedLock {
fn is_locked(&self) -> bool {
*self.locked.lock().unwrap()
}
fn lock(&self) {
*self.locked.lock().unwrap() = true;
}
fn unlock(&self) {
*self.locked.lock().unwrap() = false;
self.condvar.notify_all();
}
fn wait_here(&self) {
let mut locked = self.locked.lock().unwrap();
while *locked {
locked = self.condvar.wait(locked).unwrap();
}
}
fn wait_here_for(&self, timeout: TimerDuration) {
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;
}
}
fn wait_here_for_ms(&self, timeout_ms: u64) {
self.wait_here_for(TimerDuration::Real(Duration::from_millis(timeout_ms)));
}
}

View file

@ -89,8 +89,7 @@ impl TimedLock {
let mut data = self.data.lock().unwrap(); let mut data = self.data.lock().unwrap();
data.time = Some(Timer::new(duration)); data.time = Some(Timer::new(duration));
data.remain_locked = true; data.remain_locked = true;
self.inner.lock(); self.inner.lock()
drop(data);
} }
pub fn lock_for_ms(&self, duration_ms: u64) { pub fn lock_for_ms(&self, duration_ms: u64) {
@ -121,16 +120,21 @@ impl Lock for TimedLock {
let mut data = self.data.lock().unwrap(); let mut data = self.data.lock().unwrap();
data.time = None; data.time = None;
data.remain_locked = true; data.remain_locked = true;
self.inner.lock(); self.inner.lock()
drop(data); }
fn try_lock(&self) -> bool {
let mut data = self.data.lock().unwrap();
data.time = None;
data.remain_locked = true;
self.inner.try_lock()
} }
fn unlock(&self) { fn unlock(&self) {
let mut data = self.data.lock().unwrap(); let mut data = self.data.lock().unwrap();
data.time = None; data.time = None;
data.remain_locked = false; data.remain_locked = false;
self.inner.unlock(); self.inner.unlock()
drop(data);
} }
fn wait_here(&self) { fn wait_here(&self) {

96
src/untimed.rs Normal file
View file

@ -0,0 +1,96 @@
use std::{
sync::{Condvar, Mutex},
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 [`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>,
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),
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();
}
fn wait_here(&self) {
let mut locked = self.locked.lock().unwrap();
while *locked {
locked = self.condvar.wait(locked).unwrap();
}
}
fn wait_here_for(&self, timeout: TimerDuration) {
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;
}
}
fn wait_here_for_ms(&self, timeout_ms: u64) {
self.wait_here_for(TimerDuration::Real(Duration::from_millis(timeout_ms)));
}
}