use core::sync::atomic::{AtomicBool, Ordering};
static EXITING: AtomicBool = AtomicBool::new(false);
pub(crate) fn is_exiting() -> bool {
EXITING.load(Ordering::Acquire)
}
#[cfg(multi_threading)]
pub(crate) use handler::close_at_exit;
#[cfg(multi_threading)]
mod handler {
use super::EXITING;
use crate::device::{DeviceId, handle::ServiceCloseError};
use alloc::vec::Vec;
use core::any::TypeId;
use core::sync::atomic::Ordering;
use std::sync::Once;
struct ServiceAtExit {
device_id: DeviceId,
type_id: TypeId,
close: fn(DeviceId) -> Result<(), ServiceCloseError>,
}
static SERVICES: spin::Mutex<Vec<ServiceAtExit>> = spin::Mutex::new(Vec::new());
static HANDLER: Once = Once::new();
unsafe extern "C" {
fn atexit(handler: extern "C" fn()) -> core::ffi::c_int;
}
pub(crate) fn close_at_exit(
device_id: DeviceId,
type_id: TypeId,
close: fn(DeviceId) -> Result<(), ServiceCloseError>,
) {
HANDLER.call_once(|| {
if unsafe { atexit(close_on_exit) } != 0 {
log::warn!(
"no exit handler for the device services: an exit from a thread other than \
the main one may crash the process"
);
}
});
let mut services = SERVICES.lock();
let known = services
.iter()
.any(|service| service.device_id == device_id && service.type_id == type_id);
if !known {
services.push(ServiceAtExit {
device_id,
type_id,
close,
});
}
}
extern "C" fn close_on_exit() {
close_services();
}
fn close_services() {
EXITING.store(true, Ordering::Release);
let services = core::mem::take(&mut *SERVICES.lock());
for service in services {
if let Err(error) = (service.close)(service.device_id) {
log::warn!(
"a service on device {:?} was not closed before exit: {error:?}",
service.device_id
);
}
}
}
#[cfg(all(target_os = "linux", target_env = "gnu"))]
mod main_thread {
struct ExitGuard;
impl Drop for ExitGuard {
fn drop(&mut self) {
super::close_services();
}
}
std::thread_local! {
static GUARD: ExitGuard = const { ExitGuard };
}
extern "C" fn arm() {
let main_thread = unsafe { libc::getpid() == libc::gettid() };
if main_thread {
GUARD.with(|_| ());
}
}
#[used]
#[unsafe(link_section = ".init_array")]
static ARM: extern "C" fn() = arm;
}
}