- Rename corex -> mytheclipse (corex name taken on crates.io by an unrelated crate published by David-Kunz in 2023) - Flatten corex + corex-core workspace into a single crate; all modules (context/error/io/compute/bg) now live directly under src/ - Update Cargo.toml metadata: real repo URL, docs.rs, author - Update README, example, doc comments, tracing span names - Fix CI: single package dry-run (corex-core no longer separate) - Fix publish workflow: single cargo publish step
99 lines
3.4 KiB
Rust
99 lines
3.4 KiB
Rust
//! Global engine context providing resource-aware execution primitives.
|
|
//!
|
|
//! The context is initialized lazily on first access via [`context`], or
|
|
//! explicitly via [`init`]. Resource sizing is derived from the number of
|
|
//! logical CPU cores reported by [`num_cpus::get`].
|
|
|
|
use std::sync::OnceLock;
|
|
|
|
static CONTEXT: OnceLock<EngineContext> = OnceLock::new();
|
|
|
|
/// The global, lazily-initialized engine context.
|
|
///
|
|
/// Holds the computed thread and concurrency counts for each mytheclipse
|
|
/// subsystem, along with the resource pools those counts were used to
|
|
/// build. The context lives for the lifetime of the process once
|
|
/// initialized: it is stored in a `'static` [`OnceLock`] and is never
|
|
/// dropped.
|
|
pub struct EngineContext {
|
|
/// Logical CPU core count used for sizing async I/O scheduling.
|
|
pub io_threads: usize,
|
|
/// Number of worker threads allocated to the compute [`rayon::ThreadPool`].
|
|
pub compute_threads: usize,
|
|
/// Maximum number of background tasks permitted to run concurrently.
|
|
pub bg_concurrency: usize,
|
|
#[cfg(feature = "compute")]
|
|
pub(crate) compute_pool: rayon::ThreadPool,
|
|
#[cfg(feature = "bg")]
|
|
pub(crate) bg_semaphore: tokio::sync::Semaphore,
|
|
}
|
|
|
|
impl EngineContext {
|
|
/// Builds a new [`EngineContext`] sized from the current machine's
|
|
/// logical core count.
|
|
///
|
|
/// # Panics
|
|
///
|
|
/// Panics if the compute thread pool cannot be constructed. This only
|
|
/// happens under an unrecoverable environment failure, such as the
|
|
/// operating system refusing to spawn any new thread.
|
|
fn build() -> Self {
|
|
let logical_cores = num_cpus::get();
|
|
let io_threads = logical_cores;
|
|
let compute_threads = logical_cores.saturating_sub(1).max(1);
|
|
let bg_concurrency = (logical_cores / 2).max(2);
|
|
|
|
#[cfg(feature = "compute")]
|
|
let compute_pool = rayon::ThreadPoolBuilder::new()
|
|
.num_threads(compute_threads)
|
|
.thread_name(|index| format!("mytheclipse-compute-{index}"))
|
|
.build()
|
|
.expect("mytheclipse: failed to build rayon compute thread pool");
|
|
|
|
#[cfg(feature = "bg")]
|
|
let bg_semaphore = tokio::sync::Semaphore::new(bg_concurrency);
|
|
|
|
Self {
|
|
io_threads,
|
|
compute_threads,
|
|
bg_concurrency,
|
|
#[cfg(feature = "compute")]
|
|
compute_pool,
|
|
#[cfg(feature = "bg")]
|
|
bg_semaphore,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Returns the global [`EngineContext`], initializing it on first access.
|
|
///
|
|
/// Safe to call from any thread at any time; initialization happens
|
|
/// exactly once regardless of how many callers race to trigger it.
|
|
pub fn context() -> &'static EngineContext {
|
|
CONTEXT.get_or_init(EngineContext::build)
|
|
}
|
|
|
|
/// Bootstraps the global [`EngineContext`].
|
|
///
|
|
/// Behaviorally identical to [`context`]; provided as the explicit,
|
|
/// discoverable entry point applications call at startup to force
|
|
/// initialization eagerly, for example so resource sizing can be logged
|
|
/// before any workload runs. Calling it more than once, or never calling
|
|
/// it at all before using [`context`], is equally correct.
|
|
pub fn init() -> &'static EngineContext {
|
|
CONTEXT.get_or_init(EngineContext::build)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn context_numbers_are_sane() {
|
|
let ctx = context();
|
|
assert!(ctx.io_threads >= 1);
|
|
assert!(ctx.compute_threads >= 1);
|
|
assert!(ctx.bg_concurrency >= 2);
|
|
}
|
|
}
|