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]]
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!"
license = "MIT"
repository = "https://git.tudbut.de/tudbut/microlock"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
[dependencies]

View file

@ -1,41 +1,9 @@
mod timed;
pub mod timer;
mod untimed;
pub use timed::*;
use timer::{Timed, Timer, TimerDuration};
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)
}
}
use timer::TimerDuration;
pub use untimed::*;
pub trait Lock {
/// 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
/// target will be replaced without releasing the waiting threads.
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.
fn unlock(&self);
/// 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_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();
data.time = Some(Timer::new(duration));
data.remain_locked = true;
self.inner.lock();
drop(data);
self.inner.lock()
}
pub fn lock_for_ms(&self, duration_ms: u64) {
@ -121,16 +120,21 @@ impl Lock for TimedLock {
let mut data = self.data.lock().unwrap();
data.time = None;
data.remain_locked = true;
self.inner.lock();
drop(data);
self.inner.lock()
}
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) {
let mut data = self.data.lock().unwrap();
data.time = None;
data.remain_locked = false;
self.inner.unlock();
drop(data);
self.inner.unlock()
}
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)));
}
}