ctru/services/
reference.rs

1use crate::Error;
2use std::sync::{Mutex, MutexGuard, TryLockError};
3
4pub(crate) struct ServiceReference {
5    _guard: MutexGuard<'static, ()>,
6    close: Box<dyn Fn() + Send + Sync>,
7}
8
9impl ServiceReference {
10    pub fn new<S, E>(counter: &'static Mutex<()>, start: S, close: E) -> crate::Result<Self>
11    where
12        S: FnOnce() -> crate::Result<()>,
13        E: Fn() + Send + Sync + 'static,
14    {
15        let _guard = match counter.try_lock() {
16            Ok(lock) => lock,
17            Err(e) => match e {
18                TryLockError::Poisoned(guard) => {
19                    // If the MutexGuard is poisoned that means that the "other" service instance (of which the thread panicked)
20                    // was NOT properly closed. To avoid any weird behaviour, we try closing the service now, to then re-open a fresh instance.
21                    //
22                    // It's up to our `close()` implementations to avoid panicking/doing weird stuff again.
23                    close();
24
25                    guard.into_inner()
26                }
27                TryLockError::WouldBlock => return Err(Error::ServiceAlreadyActive),
28            },
29        };
30
31        start()?;
32
33        Ok(Self {
34            _guard,
35            close: Box::new(close),
36        })
37    }
38}
39
40impl Drop for ServiceReference {
41    fn drop(&mut self) {
42        (self.close)();
43    }
44}