Add try_lock
This commit is contained in:
parent
ea3b41eb1d
commit
131bc549d2
5 changed files with 116 additions and 81 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -4,4 +4,4 @@ version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "microlock"
|
name = "microlock"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
|
|
|
@ -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]
|
||||||
|
|
81
src/lib.rs
81
src/lib.rs
|
@ -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)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
16
src/timed.rs
16
src/timed.rs
|
@ -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
96
src/untimed.rs
Normal 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)));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue