#![forbid(unsafe_code)]
#![warn(
clippy::correctness,
clippy::suspicious,
clippy::complexity,
clippy::perf,
clippy::style
)]
#![allow(clippy::tabs_in_doc_comments)]
use parking_lot::{Condvar, Mutex};
use std::{ops::Deref, sync::Arc, time::Duration};
#[derive(Default, Clone)]
pub struct ThreadCounter {
inner: Arc<RawThreadCounter>,
}
impl ThreadCounter {
pub fn ticket(&self) -> Ticket {
self.increment();
Ticket {
counter: self.clone(),
}
}
}
impl Deref for ThreadCounter {
type Target = RawThreadCounter;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl AsRef<RawThreadCounter> for ThreadCounter {
fn as_ref(&self) -> &RawThreadCounter {
&self.inner
}
}
pub struct RawThreadCounter {
count: Mutex<usize>,
condvar: Condvar,
}
impl RawThreadCounter {
pub fn increment(&self) {
let mut count = self.count.lock();
*count += 1;
}
pub fn decrement(&self) {
let mut count = self.count.lock();
*count -= 1;
if *count == 0 {
self.condvar.notify_all();
}
}
pub fn wait(&self, timeout: impl Into<Option<Duration>>) -> bool {
let mut count = self.count.lock();
let condition = |count: &mut usize| *count > 0;
match timeout.into() {
Some(timeout) => !self
.condvar
.wait_while_for(&mut count, condition, timeout)
.timed_out(),
None => {
self.condvar.wait_while(&mut count, condition);
true
}
}
}
}
impl Default for RawThreadCounter {
fn default() -> Self {
Self {
count: Mutex::new(0),
condvar: Condvar::new(),
}
}
}
pub struct Ticket {
counter: ThreadCounter,
}
impl Drop for Ticket {
fn drop(&mut self) {
self.counter.decrement();
}
}