#![cfg_attr(nightly_tls_active, feature(thread_local))]
#![deny(missing_docs)]
pub mod hybrid;
pub mod metrics;
pub mod registry;
pub mod schedule;
pub mod task;
#[cfg(test)]
mod counting_wake;
pub use hybrid::HybridExecutor;
pub use metrics::ExecutorMetrics;
pub use registry::{RetentionPolicy, TaskRegistry};
pub use schedule::{
AcceleratorCounts, AcceleratorId, AcceleratorKind, AcceleratorRoute, AcceleratorRoutePolicy,
AsyncLaneId, AsyncLanesPerProcess, AsyncTask, BlockingTask, HybridRoutePolicy, HybridRouter,
IdleHook, IdleHookRegistrationError, MAX_IDLE_HOOKS, ProcessCount, ProcessId, ProcessRoute,
RoutePolicy, RouteSummary, RouteTopology, ScheduleMetrics, SchedulerRoute, SchedulerScope,
ServerCount, ServerId, ServerRoute, ServerRoutePolicy, SyncTask, ThreadId, ThreadRoute,
ThreadRoutePolicy, ThreadScheduler, WorkClass, WorkerCount, register_idle_hook, run_idle_hooks,
};
#[cfg(feature = "scheduler-diagnostics")]
pub use schedule::{
ContendedWakeDecision, DiagnosticWakeDecision, EmptyWakeDecision, SaturatedWakeDecision,
};
pub use task::TaskMetadata;
pub fn block_on<F>(future: F) -> F::Output
where
F: core::future::Future,
{
schedule::wake::block_on_current_thread(future)
}
pub struct ExecutorBuilder {
worker_threads: usize,
async_threads: usize,
worker_placement: moirai_core::executor::WorkerPlacement,
}
impl ExecutorBuilder {
pub fn new() -> Self {
Self {
worker_threads: themis::CpuTopology::detect()
.map(|topology| topology.logical_processors())
.or_else(|| std::thread::available_parallelism().ok().map(|n| n.get()))
.unwrap_or(4)
.max(1),
async_threads: 4,
worker_placement: moirai_core::executor::WorkerPlacement::default(),
}
}
pub fn worker_threads(mut self, count: usize) -> Self {
self.worker_threads = count;
self
}
pub fn worker_placement(mut self, placement: moirai_core::executor::WorkerPlacement) -> Self {
self.worker_placement = placement;
self
}
pub fn async_threads(mut self, count: usize) -> Self {
self.async_threads = count;
self
}
pub fn build(self) -> Result<HybridExecutor, Box<dyn std::error::Error>> {
let config = moirai_core::executor::ExecutorConfig {
worker_threads: self.worker_threads,
async_threads: self.async_threads,
worker_placement: self.worker_placement,
..moirai_core::executor::ExecutorConfig::default()
};
HybridExecutor::new(config).map_err(|e| Box::new(e) as Box<dyn std::error::Error>)
}
}
impl Default for ExecutorBuilder {
fn default() -> Self {
Self::new()
}
}
#[derive(Copy, Clone)]
struct SendContext(*mut ());
unsafe impl Send for SendContext {}
unsafe impl Sync for SendContext {}
impl SendContext {
#[inline]
fn get(self) -> *mut () {
self.0
}
}
struct MoiraiExecutor;
unsafe impl melinoe::ParallelExecutor for MoiraiExecutor {
unsafe fn run_indexed(num_tasks: usize, task: unsafe fn(usize, *mut ()), context: *mut ()) {
let context = SendContext(context);
let res = global().for_each_indexed::<SyncTask, _>(num_tasks, move |index| {
unsafe {
task(index, context.get());
}
});
if let Err(e) = res {
panic!(
"Moirai executor failure in Melinoe parallel driver: {:?}",
e
);
}
}
}
fn global_arc() -> &'static std::sync::Arc<HybridExecutor> {
static GLOBAL_EXECUTOR: std::sync::OnceLock<std::sync::Arc<HybridExecutor>> =
std::sync::OnceLock::new();
GLOBAL_EXECUTOR.get_or_init(|| {
let executor = std::sync::Arc::new(
ExecutorBuilder::new()
.build()
.expect("initialize global Moirai executor"),
);
melinoe::register_parallel_executor::<MoiraiExecutor>();
executor
})
}
pub fn initialize() {
let _ = global_arc();
melinoe::register_parallel_executor::<MoiraiExecutor>();
}
pub fn global() -> &'static HybridExecutor {
global_arc()
}
pub fn shared() -> std::sync::Arc<HybridExecutor> {
std::sync::Arc::clone(global_arc())
}