use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Mutex, Once, PoisonError, Weak};
use std::thread;
use pyo3::exceptions::PyRuntimeError;
use pyo3::prelude::*;
pub(crate) fn run_attached(thread: rayon::ThreadBuilder) {
let mut thread = Some(thread);
Python::try_attach(|py| py.detach(|| thread.take().map(rayon::ThreadBuilder::run)));
if let Some(thread) = thread {
thread.run();
}
}
pub(crate) fn attach<R, E>(f: impl for<'py> FnOnce(Python<'py>) -> Result<R, E>) -> Result<R, E>
where
E: From<PyErr>,
{
Python::try_attach(f).unwrap_or_else(|| {
Err(PyRuntimeError::new_err("the Python interpreter is shutting down").into())
})
}
static POOLS: Mutex<Vec<Weak<rayon::ThreadPool>>> = Mutex::new(Vec::new());
pub(crate) fn track(pool: Weak<rayon::ThreadPool>) {
let mut pools = POOLS.lock().unwrap_or_else(PoisonError::into_inner);
pools.retain(|p| p.strong_count() > 0);
if !pools.iter().any(|p| p.ptr_eq(&pool)) {
pools.push(pool);
}
}
static EXITING: AtomicBool = AtomicBool::new(false);
pub(crate) fn exiting() -> bool {
EXITING.load(Ordering::SeqCst)
}
#[pyfunction]
#[pyo3(name = "_keep_pools_alive")]
pub(crate) fn keep_pools_alive() {
EXITING.store(true, Ordering::SeqCst);
let pools = std::mem::take(&mut *POOLS.lock().unwrap_or_else(PoisonError::into_inner));
for pool in pools.iter().filter_map(Weak::upgrade) {
std::mem::forget(pool);
}
}
pub(crate) fn attach_global_pool(py: Python<'_>) {
static ONCE: Once = Once::new();
py.detach(|| {
ONCE.call_once(|| {
let _ = rayon::ThreadPoolBuilder::new()
.thread_name(|i| format!("dataloader-map-{i}"))
.spawn_handler(|thread| {
let mut builder = thread::Builder::new();
if let Some(name) = thread.name() {
builder = builder.name(name.to_owned());
}
builder.spawn(move || run_attached(thread))?;
Ok(())
})
.build_global();
})
});
}