timerwheel 0.1.0

Hierarchical timer wheel for delayed task scheduling with pluggable executors.
Documentation
// Copyright © 2026-present The Timerwheel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fmt;
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};

use crate::executor::metric_sink::MetricSink;

/// Immutable snapshot of worker executor counters.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct ExecutorMetrics {
    /// Number of tasks accepted into the executor queue.
    pub submitted_tasks: u64,
    /// Number of tasks completed by workers, including panicked tasks.
    pub completed_tasks: u64,
    /// Number of tasks rejected by the executor.
    pub rejected_tasks: u64,
    /// Number of tasks that panicked while running.
    pub panicked_tasks: u64,
    /// Current number of accepted tasks not yet completed.
    pub queue_depth: usize,
    /// Number of configured worker threads.
    pub workers: usize,
}

pub(crate) struct ExecutorMetricsInner {
    submitted_tasks: AtomicU64,
    completed_tasks: AtomicU64,
    rejected_tasks: AtomicU64,
    panicked_tasks: AtomicU64,
    queue_depth: AtomicUsize,
    workers: usize,
    sink: Arc<dyn MetricSink>,
}

impl fmt::Debug for ExecutorMetricsInner {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ExecutorMetricsInner")
            .field("snapshot", &self.snapshot())
            .finish_non_exhaustive()
    }
}

impl ExecutorMetricsInner {
    pub(crate) fn new(workers: usize, sink: Arc<dyn MetricSink>) -> Self {
        Self {
            submitted_tasks: AtomicU64::new(0),
            completed_tasks: AtomicU64::new(0),
            rejected_tasks: AtomicU64::new(0),
            panicked_tasks: AtomicU64::new(0),
            queue_depth: AtomicUsize::new(0),
            workers,
            sink,
        }
    }

    pub(crate) fn submitted(&self) {
        self.submitted_tasks.fetch_add(1, Ordering::Relaxed);
        self.queue_depth.fetch_add(1, Ordering::Relaxed);
        self.observe();
    }

    pub(crate) fn completed(&self) {
        self.completed_tasks.fetch_add(1, Ordering::Relaxed);
        self.queue_depth.fetch_sub(1, Ordering::Relaxed);
        self.observe();
    }

    pub(crate) fn rejected(&self) {
        self.rejected_tasks.fetch_add(1, Ordering::Relaxed);
        self.observe();
    }

    pub(crate) fn panicked(&self) {
        self.panicked_tasks.fetch_add(1, Ordering::Relaxed);
        self.observe();
    }

    fn observe(&self) {
        self.sink.observe_metrics(self.snapshot());
    }

    pub(crate) fn snapshot(&self) -> ExecutorMetrics {
        ExecutorMetrics {
            submitted_tasks: self.submitted_tasks.load(Ordering::Relaxed),
            completed_tasks: self.completed_tasks.load(Ordering::Relaxed),
            rejected_tasks: self.rejected_tasks.load(Ordering::Relaxed),
            panicked_tasks: self.panicked_tasks.load(Ordering::Relaxed),
            queue_depth: self.queue_depth.load(Ordering::Relaxed),
            workers: self.workers,
        }
    }
}