use std::sync::Weak;
use std::time::Duration;
use crate::graph::{GraphInner, NodeId};
mod delegating;
mod platform;
mod stats;
mod thread_pool;
pub use delegating::DelegatingExecutor;
pub(crate) use stats::ExecutorStatsSnapshot;
pub use thread_pool::ThreadPool;
pub const DEFAULT_EXECUTOR_NAME: &str = "default";
pub enum Executor {
Pool(ThreadPool),
Delegating(DelegatingExecutor),
}
impl Executor {
pub fn name(&self) -> &str {
match self {
Self::Pool(pool) => pool.name(),
Self::Delegating(delegating) => delegating.name(),
}
}
pub fn is_delegating(&self) -> bool {
matches!(self, Self::Delegating(_))
}
pub fn num_threads(&self) -> usize {
match self {
Self::Pool(pool) => pool.num_threads(),
Self::Delegating(_) => 0,
}
}
pub fn affinity(&self) -> &[usize] {
match self {
Self::Pool(pool) => pool.affinity(),
Self::Delegating(_) => &[],
}
}
pub fn priority(&self) -> i32 {
match self {
Self::Pool(pool) => pool.priority(),
Self::Delegating(_) => 0,
}
}
pub fn start(&self, graph: Weak<GraphInner>) {
if let Self::Pool(pool) = self {
pool.start(graph);
}
}
pub fn submit(&self, node: NodeId) -> bool {
match self {
Self::Pool(pool) => pool.submit(node),
Self::Delegating(delegating) => delegating.submit(node),
}
}
pub fn submit_source_wake(&self, node: NodeId, generation: u64, delay: Duration) -> bool {
match self {
Self::Pool(pool) => pool.submit_source_wake(node, generation, delay),
Self::Delegating(_) => false,
}
}
pub fn pending(&self) -> usize {
match self {
Self::Pool(pool) => pool.pending(),
Self::Delegating(delegating) => delegating.pending(),
}
}
pub fn shutdown(&self) {
if let Self::Pool(pool) = self {
pool.shutdown();
}
}
pub fn take_delegated(&self) -> Option<NodeId> {
match self {
Self::Pool(_) => None,
Self::Delegating(delegating) => delegating.take(),
}
}
pub fn complete_delegated(&self, execution: Option<Duration>) {
if let Self::Delegating(delegating) = self {
delegating.complete(execution);
}
}
pub(crate) fn stats(&self) -> ExecutorStatsSnapshot {
match self {
Self::Pool(pool) => pool.stats(),
Self::Delegating(delegating) => delegating.stats(),
}
}
pub(crate) fn has_pending_work(&self) -> bool {
match self {
Self::Pool(pool) => pool.has_pending_work(),
Self::Delegating(delegating) => delegating.has_pending_work(),
}
}
pub(crate) fn queued_nodes(&self) -> Vec<NodeId> {
match self {
Self::Pool(pool) => pool.queued_nodes(),
Self::Delegating(delegating) => delegating.queued_nodes(),
}
}
pub fn clear_delegated(&self) {
if let Self::Delegating(delegating) = self {
delegating.clear();
}
}
pub fn reset_run_state(&self) {
match self {
Self::Pool(pool) => {
pool.clear_delayed();
pool.reset_stats();
}
Self::Delegating(delegating) => {
delegating.clear();
delegating.reset_stats();
}
}
}
pub fn clear_delayed(&self) {
if let Self::Pool(pool) = self {
pool.clear_delayed();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn delegating_submit_survives_shutdown() {
let executor = Executor::Delegating(DelegatingExecutor::new("host"));
executor.shutdown();
assert!(
executor.submit(1),
"委托执行器没有『已关停』状态 —— 宿主线程不由引擎拉起,也无从 join"
);
}
#[test]
fn executor_kinds_report_their_shape() {
let pool = Executor::Pool(ThreadPool::new("cpu", 4, vec![2, 3], 10));
let host = Executor::Delegating(DelegatingExecutor::new(DEFAULT_EXECUTOR_NAME));
assert!(!pool.is_delegating());
assert_eq!(pool.name(), "cpu");
assert_eq!(pool.num_threads(), 4);
assert_eq!(pool.affinity(), &[2, 3]);
assert_eq!(pool.priority(), 10);
assert_eq!(pool.take_delegated(), None);
assert!(host.is_delegating());
assert_eq!(host.name(), DEFAULT_EXECUTOR_NAME);
assert_eq!(host.num_threads(), 0);
assert!(host.affinity().is_empty());
assert_eq!(host.priority(), 0);
host.submit(5);
assert_eq!(host.take_delegated(), Some(5));
}
}