use std::sync::{Condvar, Mutex, MutexGuard, PoisonError};
use std::thread::{self, ThreadId};
#[derive(Default)]
pub(crate) struct ConstructionLock {
owner: Mutex<Owner>,
released: Condvar,
}
#[derive(Default)]
struct Owner {
thread: Option<ThreadId>,
depth: usize,
}
impl ConstructionLock {
pub(crate) fn lock(&self) -> ConstructionGuard<'_> {
let me = thread::current().id();
let mut owner = self.owner();
loop {
match owner.thread {
None => {
owner.thread = Some(me);
break;
}
Some(thread) if thread == me => break,
Some(_) => {
owner = self
.released
.wait(owner)
.unwrap_or_else(PoisonError::into_inner);
}
}
}
owner.depth += 1;
ConstructionGuard { lock: self }
}
fn owner(&self) -> MutexGuard<'_, Owner> {
self.owner.lock().unwrap_or_else(PoisonError::into_inner)
}
}
pub(crate) struct ConstructionGuard<'a> {
lock: &'a ConstructionLock,
}
impl Drop for ConstructionGuard<'_> {
fn drop(&mut self) {
let mut owner = self.lock.owner();
owner.depth -= 1;
if owner.depth == 0 {
owner.thread = None;
self.lock.released.notify_all();
}
}
}