🔒 Repository is read-only – file editing is disabled.

luna/de/selene/src/sync.rs main

42 lines Raw ← Back
123456789101112131415161718192021222324252627282930313233343536373839404142
//! Blokady odporne na „zatrucie” po panice.
//!
//! Po panice w obsłudze klienta (patrz `catch_unwind` w `main`) mutex, którego
//! `MutexGuard` był wtedy żywy, zostaje „zatruty”. Standardowe `.lock().unwrap()`
//! panikowałoby przy każdym kolejnym użyciu i — mimo izolacji panik — skutecznie
//! psuto by daną ścieżkę. [`LockExt::lock_or_recover`] odzyskuje wartość mimo
//! zatrucia, więc jeden błędny klient nie unieruchamia kompozytora.

use std::sync::{Mutex, MutexGuard};

/// Rozszerzenie [`Mutex`] o blokadę odzyskującą wartość z zatrutego mutexa.
pub trait LockExt<T> {
    /// Blokuje mutex; przy zatruciu (po panice) zwraca wewnętrzną wartość bez paniki.
    fn lock_or_recover(&self) -> MutexGuard<'_, T>;
}

impl<T> LockExt<T> for Mutex<T> {
    fn lock_or_recover(&self) -> MutexGuard<'_, T> {
        self.lock().unwrap_or_else(|poison| poison.into_inner())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::panic::{catch_unwind, AssertUnwindSafe};

    #[test]
    fn recovers_value_from_poisoned_mutex() {
        let m = Mutex::new(0i32);
        // Zatruwamy mutex: panika przy żywym guardzie.
        let _ = catch_unwind(AssertUnwindSafe(|| {
            let _guard = m.lock().unwrap();
            panic!("boom");
        }));
        assert!(m.lock().is_err(), "mutex powinien być zatruty po panice");

        // Mimo zatrucia odzyskujemy wartość i możemy ją zmienić.
        *m.lock_or_recover() = 42;
        assert_eq!(*m.lock_or_recover(), 42);
    }
}