zenith-runtime 0.1.0

Zenith 全链路数据面运行时:WorkerRuntime(eBPF + XSK + Worker 集成)、三级 Supervisor、ChangeSet 热切换、RuntimeGraph 拓扑规划
//! CPU 亲和绑定模块(非 Linux 可移植桩)
//!
//! 非 Linux 目标下提供与 `cpu_affinity` 相同的公开 API 表面,
//! 所有真实绑定操作 fail-closed 返回 `Unsupported`,
//! `get_online_cpus` 退化为 `std::thread::available_parallelism`。

use thiserror::Error;

/// 亲和性操作错误(非 Linux 桩)
#[derive(Debug, Error)]
pub enum CpuAffinityError {
    /// 当前平台不支持 CPU 亲和绑定
    #[error("cpu affinity is not supported on this platform (Linux only)")]
    Unsupported,

    /// CPU 编号超出范围
    #[error("CPU {cpu} out of range (max: {max})")]
    CpuOutOfRange {
        /// 非法的 CPU 编号
        cpu: usize,
        /// 当前在线 CPU 总数
        max: usize,
    },

    /// NUMA 节点不存在
    #[error("NUMA node {node} not found")]
    NumaNodeNotFound {
        /// 不存在的 NUMA 节点编号
        node: u32,
    },

    /// Worker 配置未找到(worker_id 不在 AffinityManager 配置列表中)
    #[error("worker {worker_id} not found in affinity configs (count: {config_count})")]
    WorkerNotFound {
        /// 未找到的 worker ID
        worker_id: u32,
        /// 当前已注册的配置总数
        config_count: usize,
    },
}

/// CPU 标识(包含核号与 NUMA 节点号)
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CpuId {
    /// CPU 编号(0 起)
    pub id: usize,
    /// 所属 NUMA 节点
    pub node: u32,
}

impl CpuId {
    /// 创建新的 CpuId
    pub fn new(id: usize, node: u32) -> Self {
        Self { id, node }
    }
}

/// 将当前线程绑定到指定 CPU 核心(非 Linux:fail-closed Unsupported)
pub fn set_thread_affinity(_cpu: usize) -> Result<(), CpuAffinityError> {
    Err(CpuAffinityError::Unsupported)
}

/// 将当前线程绑定到一组 CPU 核心(非 Linux:fail-closed Unsupported)
pub fn set_thread_affinity_range(_cpus: &[usize]) -> Result<(), CpuAffinityError> {
    Err(CpuAffinityError::Unsupported)
}

/// 获取在线 CPU 数量(非 Linux:available_parallelism 退化)
pub fn get_online_cpus() -> usize {
    std::thread::available_parallelism()
        .map(|n| n.get())
        .unwrap_or(1)
}

/// CPU 亲和配置:将一个 Worker 绑定到一组 CPU
#[derive(Debug, Clone)]
pub struct CpuAffinityConfig {
    /// Worker 标识
    pub worker_id: u32,
    /// 目标 CPU 编号列表
    pub cpu_ids: Vec<usize>,
    /// 所属 NUMA 节点
    pub numa_node: u32,
}

impl CpuAffinityConfig {
    /// 创建新的亲和配置
    pub fn new(worker_id: u32, cpu_ids: Vec<usize>, numa_node: u32) -> Self {
        Self { worker_id, cpu_ids, numa_node }
    }

    /// 应用配置到当前线程(非 Linux:fail-closed Unsupported)
    ///
    /// SYS-016:应用前校验 CPU 编号在在线范围内(越界返回 `CpuOutOfRange`,
    /// 与 Linux 实现语义一致)。
    pub fn apply(&self) -> Result<(), CpuAffinityError> {
        let online = get_online_cpus();
        for &cpu in &self.cpu_ids {
            if cpu >= online {
                return Err(CpuAffinityError::CpuOutOfRange { cpu, max: online });
            }
        }
        Err(CpuAffinityError::Unsupported)
    }
}

/// 亲和性管理器:管理多个 Worker 的亲和配置
#[derive(Debug)]
pub struct AffinityManager {
    configs: Vec<CpuAffinityConfig>,
}

impl AffinityManager {
    /// 创建空的管理器
    pub fn new() -> Self {
        Self { configs: Vec::new() }
    }

    /// 添加一个 Worker 的亲和配置
    pub fn add_config(&mut self, config: CpuAffinityConfig) {
        self.configs.push(config);
    }

    /// 应用所有配置并返回每个 Worker 的结果
    pub fn apply_all(&self) -> Vec<(u32, Result<(), CpuAffinityError>)> {
        self.configs
            .iter()
            .map(|c| {
                let result = c.apply();
                (c.worker_id, result)
            })
            .collect()
    }

    /// 应用指定 Worker 的亲和配置
    pub fn apply_for_worker(&self, worker_id: u32) -> Result<(), CpuAffinityError> {
        let config = self
            .configs
            .iter()
            .find(|c| c.worker_id == worker_id)
            .ok_or(CpuAffinityError::WorkerNotFound {
                worker_id,
                config_count: self.configs.len(),
            })?;
        config.apply()
    }

    /// 获取所有配置
    pub fn configs(&self) -> &[CpuAffinityConfig] {
        &self.configs
    }
}

impl Default for AffinityManager {
    fn default() -> Self {
        Self::new()
    }
}