minor doc fixes and edge case handling

This commit is contained in:
Daniella / Tove 2024-07-14 02:03:03 +00:00
parent 131bc549d2
commit a7a57b23f7
5 changed files with 33 additions and 7 deletions

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.2.0" version = "0.2.1"
edition = "2021" edition = "2021"
[dependencies] [dependencies]

View file

@ -11,8 +11,8 @@ 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 /// Locks the lock indefinitely like [`Lock::lock`], UNLESS unlock was
/// more than once. If someone tried to unlock the lock while it 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 /// already unlocked, this will NOT lock it. Returns false and resets
/// the double-unlock check if the locking failed. /// the double-unlock check if the locking failed.
fn try_lock(&self) -> bool; fn try_lock(&self) -> bool;

View file

@ -15,7 +15,7 @@ struct TimedLockData {
remain_locked: bool, remain_locked: bool,
} }
/// An untimed lock. This can be locked and unlocked, and it will unlock on /// A timed lock. This can be locked and unlocked, and it will unlock on
/// its own after a timeout, if specified (see [`UntimedLock`] for a /// its own after a timeout, if specified (see [`UntimedLock`] for a
/// non-expiring lock). If a thread calls wait_here on a locked lock, it will /// non-expiring lock). If a thread calls wait_here on a locked lock, it will
/// wait until the lock is unlocked by another thread, or the lock expires. /// wait until the lock is unlocked by another thread, or the lock expires.

View file

@ -3,16 +3,31 @@ use std::{
time::{Duration, Instant}, time::{Duration, Instant},
}; };
/// Our real representation of an Infinite duration.
pub const INFINITE_DURATION: Duration = Duration::new(u64::MAX, 1_000_000_000 - 1); pub const INFINITE_DURATION: Duration = Duration::new(u64::MAX, 1_000_000_000 - 1);
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
/// A duration wrapper supporting elapsed/negative, real, and infinite
/// durations. Due to the solar system's impending collapse before the 64 bit
/// integer limit of seconds is reached, an Infinite duration is translated
/// to that when turning this back info a normal duration. If you run your
/// program for longer than the sun's entire lifespan including the past, this
/// may cause an issue. But I think you'll have much bigger issues long before
/// that, including the fact that you even came up with that idea.
pub enum TimerDuration { pub enum TimerDuration {
Elapsed, Elapsed,
Real(Duration), Real(Duration),
Infinite, Infinite,
} }
impl TimerDuration { impl TimerDuration {
/// Takes the difference between a TimerDuration a and a Duration b,
/// resulting in either a negative (Elapsed) duration or a Real one.
/// The Infinite duration is passed through as-is, with no subtraction
/// done to it.
pub fn from_difference(a: TimerDuration, b: Duration) -> TimerDuration { pub fn from_difference(a: TimerDuration, b: Duration) -> TimerDuration {
if a == TimerDuration::Infinite {
return a;
}
let a = a.to_real(); let a = a.to_real();
if a < b { if a < b {
TimerDuration::Elapsed TimerDuration::Elapsed
@ -21,6 +36,8 @@ impl TimerDuration {
} }
} }
/// Converts this into a Duration that is waitable by rust's standard
/// functions, for use in e.g. specifying timeouts to std operations.
pub fn to_real(&self) -> Duration { pub fn to_real(&self) -> Duration {
match *self { match *self {
Self::Real(d) if d > Duration::ZERO => d, Self::Real(d) if d > Duration::ZERO => d,
@ -34,6 +51,9 @@ impl From<Duration> for TimerDuration {
if value == Duration::ZERO { if value == Duration::ZERO {
return Self::Elapsed; return Self::Elapsed;
} }
if value == INFINITE_DURATION {
return Self::Infinite;
}
Self::Real(value) Self::Real(value)
} }
} }
@ -57,6 +77,8 @@ impl Ord for TimerDuration {
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
/// Holds a start time and a TimerDuration and allows for the time elapsed
/// and the time left to be queried. Timers are immutable.
pub struct Timer(Instant, TimerDuration); pub struct Timer(Instant, TimerDuration);
impl Timer { impl Timer {
pub fn new(d: TimerDuration) -> Self { pub fn new(d: TimerDuration) -> Self {
@ -68,8 +90,12 @@ impl Timer {
} }
} }
/// Something with a timeout.
pub trait Timed { pub trait Timed {
/// Returns if the time has elapsed or not.
fn has_elapsed(&self) -> bool; fn has_elapsed(&self) -> bool;
/// Returns how much time is left. Unbounded and elapsed durations use
/// Infinite and Elapsed respectively. Others always return Real.
fn time_left(&self) -> TimerDuration; fn time_left(&self) -> TimerDuration;
} }
impl Timed for Timer { impl Timed for Timer {

View file

@ -9,9 +9,9 @@ use crate::{
}; };
/// An untimed lock. This can be locked and unlocked, but it will never unlock /// 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 /// on its own (see [`crate::TimedLock`] for an expiring lock). If a thread
/// wait_here on a locked lock, it will wait until the lock is unlocked by /// calls wait_here on a locked lock, it will wait until the lock is unlocked
/// another thread. /// by another thread.
pub struct UntimedLock { pub struct UntimedLock {
locked: Mutex<bool>, locked: Mutex<bool>,
double_unlock: Mutex<bool>, double_unlock: Mutex<bool>,