use foundation_nostd::comp::condvar_comp::{CondVar, Mutex};
use std::panic::{RefUnwindSafe, UnwindSafe};
use super::Waker;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum LockState {
Free,
Locked,
Released,
}
pub struct LockSignal {
event: CondVar,
lock: Mutex<LockState>,
}
impl UnwindSafe for LockSignal {}
impl RefUnwindSafe for LockSignal {}
pub enum NotifyDirective {
One,
All,
}
impl Waker for LockSignal {
fn wake(&self) {
self.signal_all();
}
}
impl Default for LockSignal {
fn default() -> Self {
Self::new()
}
}
impl LockSignal {
#[must_use]
pub fn new() -> Self {
Self {
event: CondVar::new(),
lock: Mutex::new(LockState::Free),
}
}
pub fn signal(&self, directive: NotifyDirective) {
let mut state = self.lock.lock().unwrap();
*state = LockState::Released;
drop(state);
match directive {
NotifyDirective::One => self.event.notify_one(),
NotifyDirective::All => self.event.notify_all(),
}
}
pub fn probe(&self) -> LockState {
let current_state_guard = self.lock.lock().unwrap();
let current_state = current_state_guard.clone();
drop(current_state_guard);
current_state
}
pub fn probe_locked(&self) -> bool {
let current_state = self.lock.lock().unwrap();
let is_locked = *current_state == LockState::Locked;
drop(current_state);
is_locked
}
pub fn try_lock(&self) -> bool {
let mut current_state = self.lock.lock().unwrap();
if *current_state == LockState::Locked {
return false;
}
*current_state = LockState::Locked;
drop(current_state);
true
}
pub fn lock(&self) {
let mut current_state = self.lock.lock().unwrap();
if *current_state != LockState::Locked {
*current_state = LockState::Locked;
}
drop(current_state);
}
pub fn signal_one(&self) {
self.signal(NotifyDirective::One);
}
pub fn signal_all(&self) {
self.signal(NotifyDirective::All);
}
pub fn lock_and_wait(&self) {
if self.try_lock() {
tracing::debug!("LockSignal was locked");
}
self.wait();
}
pub fn wait(&self) {
let mut current_state = self.lock.lock().unwrap();
if *current_state == LockState::Free {
tracing::debug!("Lock is free");
return;
}
loop {
if *current_state == LockState::Released {
*current_state = LockState::Free;
tracing::debug!("Lock is now free");
return;
}
if *current_state == LockState::Free {
tracing::debug!("Lock already freed by another thread");
return;
}
tracing::debug!("Waiting for event signal to be received");
current_state = self.event.wait(current_state).unwrap();
}
}
}
#[cfg(test)]
mod test_lock_signals {
use std::{
sync::{mpsc, Arc},
thread,
time::Duration,
};
use crate::synca::{LockState, Waker};
use super::LockSignal;
#[test]
fn can_lock_signal() {
let latch = Arc::new(LockSignal::new());
let (sender, receiver) = mpsc::channel::<()>();
let latch_clone = latch.clone();
let handler = thread::spawn(move || {
latch_clone.try_lock();
sender.send(()).expect("should send");
latch_clone.wait();
});
thread::sleep(Duration::from_millis(100));
let _ = receiver.recv();
assert_eq!(LockState::Locked, latch.probe());
latch.signal_all();
handler.join().expect("should safely join");
assert_eq!(LockState::Free, latch.probe());
}
#[test]
fn test_lock_signal_new() {
let signal = LockSignal::new();
assert_eq!(LockState::Free, signal.probe());
}
#[test]
fn test_lock_signal_default() {
let signal = LockSignal::default();
assert_eq!(LockState::Free, signal.probe());
}
#[test]
fn test_try_lock_success() {
let signal = LockSignal::new();
assert!(signal.try_lock());
assert_eq!(LockState::Locked, signal.probe());
}
#[test]
fn test_try_lock_already_locked() {
let signal = LockSignal::new();
assert!(signal.try_lock());
assert!(!signal.try_lock());
assert_eq!(LockState::Locked, signal.probe());
}
#[test]
fn test_lock_operation() {
let signal = LockSignal::new();
signal.lock();
assert_eq!(LockState::Locked, signal.probe());
signal.lock();
assert_eq!(LockState::Locked, signal.probe());
}
#[test]
fn test_probe_locked_true() {
let signal = LockSignal::new();
signal.lock();
assert!(signal.probe_locked());
}
#[test]
fn test_probe_locked_false() {
let signal = LockSignal::new();
assert!(!signal.probe_locked());
}
#[test]
fn test_signal_one_releases_lock() {
let signal = Arc::new(LockSignal::new());
signal.lock();
let (tx, rx) = mpsc::channel();
let signal_clone = signal.clone();
let handle = thread::spawn(move || {
tx.send(()).unwrap();
signal_clone.wait();
});
rx.recv().unwrap();
thread::sleep(Duration::from_millis(50));
signal.signal_one();
handle.join().expect("Thread should complete");
assert_eq!(LockState::Free, signal.probe());
}
#[test]
fn test_signal_all_releases_lock() {
let signal = Arc::new(LockSignal::new());
signal.lock();
let (tx, rx) = mpsc::channel();
let signal_clone = signal.clone();
let handle = thread::spawn(move || {
tx.send(()).unwrap();
signal_clone.wait();
});
rx.recv().unwrap();
thread::sleep(Duration::from_millis(50));
signal.signal_all();
handle.join().expect("Thread should complete");
assert_eq!(LockState::Free, signal.probe());
}
#[test]
fn test_wait_when_free_returns_immediately() {
let signal = LockSignal::new();
signal.wait();
assert_eq!(LockState::Free, signal.probe());
}
#[test]
fn test_lock_and_wait() {
let signal = Arc::new(LockSignal::new());
let (tx, rx) = mpsc::channel();
let signal_clone = signal.clone();
let handle = thread::spawn(move || {
tx.send(()).unwrap();
signal_clone.lock_and_wait();
});
rx.recv().unwrap();
thread::sleep(Duration::from_millis(100));
assert_eq!(LockState::Locked, signal.probe());
signal.signal_all();
handle.join().expect("Thread should complete");
assert_eq!(LockState::Free, signal.probe());
}
#[test]
fn test_multiple_waiters_signal_all() {
let signal = Arc::new(LockSignal::new());
signal.lock();
let mut handles = vec![];
for _i in 0..3 {
let signal_clone = signal.clone();
handles.push(thread::spawn(move || {
signal_clone.wait();
}));
}
thread::sleep(Duration::from_millis(200));
signal.signal_all();
for handle in handles {
handle.join().expect("Thread should complete");
}
assert_eq!(LockState::Free, signal.probe());
}
#[test]
fn test_waker_trait_implementation() {
let signal = Arc::new(LockSignal::new());
signal.lock();
let (tx, rx) = mpsc::channel();
let signal_clone = signal.clone();
let handle = thread::spawn(move || {
tx.send(()).unwrap();
signal_clone.wait();
});
rx.recv().unwrap();
thread::sleep(Duration::from_millis(50));
signal.wake();
handle.join().expect("Thread should complete");
assert_eq!(LockState::Free, signal.probe());
}
#[test]
fn test_concurrent_probe_operations() {
let signal = Arc::new(LockSignal::new());
signal.lock();
let mut handles = vec![];
for _ in 0..5 {
let signal_clone = signal.clone();
handles.push(thread::spawn(move || {
let state = signal_clone.probe();
assert!(
state == LockState::Locked
|| state == LockState::Released
|| state == LockState::Free
);
}));
}
thread::sleep(Duration::from_millis(50));
signal.signal_all();
for handle in handles {
handle.join().expect("Thread should complete");
}
}
#[test]
fn test_state_transitions() {
let signal = LockSignal::new();
assert_eq!(LockState::Free, signal.probe());
signal.lock();
assert_eq!(LockState::Locked, signal.probe());
let signal = Arc::new(signal);
let (tx, rx) = mpsc::channel();
let signal_clone = signal.clone();
let handle = thread::spawn(move || {
tx.send(()).unwrap();
signal_clone.wait();
});
rx.recv().unwrap();
thread::sleep(Duration::from_millis(50));
signal.signal_one();
handle.join().expect("Thread should complete");
assert_eq!(LockState::Free, signal.probe());
}
}