blob: fb74f25cdec2af8abe02468b376f44cfbd5ad79a (
plain)
use std::sync::atomic::{AtomicBool, Ordering};
/// A dumb lock that's just a wrapper for an [`AtomicBool`].
#[derive(Debug, Default)]
pub struct Lock {
is_locked: AtomicBool,
}
#[derive(Debug)]
pub struct Key<'a> {
lock: &'a Lock,
}
impl<'a> Key<'a> {
fn new(lock: &'a Lock) -> Self {
Self { lock }
}
}
impl<'a> Drop for Key<'a> {
fn drop(&mut self) {
// safety: this key will soon be destroyed
unsafe { self.lock.force_unlock() }
}
}
impl Lock {
/// Create a new unlocked `Lock`.
pub const fn new() -> Self {
Self {
is_locked: AtomicBool::new(false),
}
}
/// Attempt to lock the `Lock`.
///
/// If the lock is already locked, then this'll return false. If it is
/// unlocked, it'll return true. If the lock is currently unlocked, then it
/// will be locked after this function is called.
///
/// This is not a fair lock. It is not recommended to call this function
/// repeatedly in a loop.
pub fn try_lock(&self) -> Option<Key> {
(!self.is_locked.fetch_or(true, Ordering::Acquire)).then_some(Key::new(self))
}
/// Unlock the lock, without a key.
///
/// # Safety
///
/// This should only be called if the key to the lock has been "lost". That
/// means the program no longer has a reference to the key, but it has not
/// been dropped.
unsafe fn force_unlock(&self) {
self.is_locked.store(false, Ordering::Release)
}
pub fn unlock(key: Key) {
drop(key)
}
}
|