use crate::runtime::Handle; use std::sync::atomic::Ordering::Relaxed; use std::time::Duration; /// Handle to the runtime's metrics. /// /// This handle is internally reference-counted and can be freely cloned. A /// `RuntimeMetrics` handle is obtained using the [`Runtime::metrics`] method. /// /// [`Runtime::metrics`]: crate::runtime::Runtime::metrics() #[derive(Clone, Debug)] pub struct RuntimeMetrics { handle: Handle, } impl RuntimeMetrics { pub(crate) fn new(handle: Handle) -> RuntimeMetrics { RuntimeMetrics { handle } } /// Returns the number of worker threads used by the runtime. /// /// The number of workers is set by configuring `worker_threads` on /// `runtime::Builder`. When using the `current_thread` runtime, the return /// value is always `1`. /// /// # Examples /// /// ``` /// use tokio::runtime::Handle; /// /// #[tokio::main] /// async fn main() { /// let metrics = Handle::current().metrics(); /// /// let n = metrics.num_workers(); /// println!("Runtime is using {} workers", n); /// } /// ``` pub fn num_workers(&self) -> usize { self.handle.spawner.num_workers() } /// Returns the number of tasks scheduled from **outside** of the runtime. /// /// The remote schedule count starts at zero when the runtime is created and /// increases by one each time a task is woken from **outside** of the /// runtime. This usually means that a task is spawned or notified from a /// non-runtime thread and must be queued using the Runtime's injection /// queue, which tends to be slower. /// /// The counter is monotonically increasing. It is never decremented or /// reset to zero. /// /// # Examples /// /// ``` /// use tokio::runtime::Handle; /// /// #[tokio::main] /// async fn main() { /// let metrics = Handle::current().metrics(); /// /// let n = metrics.remote_schedule_count(); /// println!("{} tasks were scheduled from outside the runtime", n); /// } /// ``` pub fn remote_schedule_count(&self) -> u64 { self.handle .spawner .scheduler_metrics() .remote_schedule_count .load(Relaxed) } /// Returns the total number of times the given worker thread has parked. /// /// The worker park count starts at zero when the runtime is created and /// increases by one each time the worker parks the thread waiting for new /// inbound events to process. This usually means the worker has processed /// all pending work and is currently idle. /// /// The counter is monotonically increasing. It is never decremented or /// reset to zero. /// /// # Arguments /// /// `worker` is the index of the worker being queried. The given value must /// be between 0 and `num_workers()`. The index uniquely identifies a single /// worker and will continue to indentify the worker throughout the lifetime /// of the runtime instance. /// /// # Panics /// /// The method panics when `worker` represents an invalid worker, i.e. is /// greater than or equal to `num_workers()`. /// /// # Examples /// /// ``` /// use tokio::runtime::Handle; /// /// #[tokio::main] /// async fn main() { /// let metrics = Handle::current().metrics(); /// /// let n = metrics.worker_park_count(0); /// println!("worker 0 parked {} times", n); /// } /// ``` pub fn worker_park_count(&self, worker: usize) -> u64 { self.handle .spawner .worker_metrics(worker) .park_count .load(Relaxed) } /// Returns the number of times the given worker thread unparked but /// performed no work before parking again. /// /// The worker no-op count starts at zero when the runtime is created and /// increases by one each time the worker unparks the thread but finds no /// new work and goes back to sleep. This indicates a false-positive wake up. /// /// The counter is monotonically increasing. It is never decremented or /// reset to zero. /// /// # Arguments /// /// `worker` is the index of the worker being queried. The given value must /// be between 0 and `num_workers()`. The index uniquely identifies a single /// worker and will continue to indentify the worker throughout the lifetime /// of the runtime instance. /// /// # Panics /// /// The method panics when `worker` represents an invalid worker, i.e. is /// greater than or equal to `num_workers()`. /// /// # Examples /// /// ``` /// use tokio::runtime::Handle; /// /// #[tokio::main] /// async fn main() { /// let metrics = Handle::current().metrics(); /// /// let n = metrics.worker_noop_count(0); /// println!("worker 0 had {} no-op unparks", n); /// } /// ``` pub fn worker_noop_count(&self, worker: usize) -> u64 { self.handle .spawner .worker_metrics(worker) .noop_count .load(Relaxed) } /// Returns the number of times the given worker thread stole tasks from /// another worker thread. /// /// This metric only applies to the **multi-threaded** runtime and will always return `0` when using the current thread runtime. /// /// The worker steal count starts at zero when the runtime is created and /// increases by one each time the worker has processed its scheduled queue /// and successfully steals more pending tasks from another worker. /// /// The counter is monotonically increasing. It is never decremented or /// reset to zero. /// /// # Arguments /// /// `worker` is the index of the worker being queried. The given value must /// be between 0 and `num_workers()`. The index uniquely identifies a single /// worker and will continue to indentify the worker throughout the lifetime /// of the runtime instance. /// /// # Panics /// /// The method panics when `worker` represents an invalid worker, i.e. is /// greater than or equal to `num_workers()`. /// /// # Examples /// /// ``` /// use tokio::runtime::Handle; /// /// #[tokio::main] /// async fn main() { /// let metrics = Handle::current().metrics(); /// /// let n = metrics.worker_noop_count(0); /// println!("worker 0 has stolen tasks {} times", n); /// } /// ``` pub fn worker_steal_count(&self, worker: usize) -> u64 { self.handle .spawner .worker_metrics(worker) .steal_count .load(Relaxed) } /// Returns the number of tasks the given worker thread has polled. /// /// The worker poll count starts at zero when the runtime is created and /// increases by one each time the worker polls a scheduled task. /// /// The counter is monotonically increasing. It is never decremented or /// reset to zero. /// /// # Arguments /// /// `worker` is the index of the worker being queried. The given value must /// be between 0 and `num_workers()`. The index uniquely identifies a single /// worker and will continue to indentify the worker throughout the lifetime /// of the runtime instance. /// /// # Panics /// /// The method panics when `worker` represents an invalid worker, i.e. is /// greater than or equal to `num_workers()`. /// /// # Examples /// /// ``` /// use tokio::runtime::Handle; /// /// #[tokio::main] /// async fn main() { /// let metrics = Handle::current().metrics(); /// /// let n = metrics.worker_poll_count(0); /// println!("worker 0 has polled {} tasks", n); /// } /// ``` pub fn worker_poll_count(&self, worker: usize) -> u64 { self.handle .spawner .worker_metrics(worker) .poll_count .load(Relaxed) } /// Returns the amount of time the given worker thread has been busy. /// /// The worker busy duration starts at zero when the runtime is created and /// increases whenever the worker is spending time processing work. Using /// this value can indicate the load of the given worker. If a lot of time /// is spent busy, then the worker is under load and will check for inbound /// events less often. /// /// The timer is monotonically increasing. It is never decremented or reset /// to zero. /// /// # Arguments /// /// `worker` is the index of the worker being queried. The given value must /// be between 0 and `num_workers()`. The index uniquely identifies a single /// worker and will continue to indentify the worker throughout the lifetime /// of the runtime instance. /// /// # Panics /// /// The method panics when `worker` represents an invalid worker, i.e. is /// greater than or equal to `num_workers()`. /// /// # Examples /// /// ``` /// use tokio::runtime::Handle; /// /// #[tokio::main] /// async fn main() { /// let metrics = Handle::current().metrics(); /// /// let n = metrics.worker_poll_count(0); /// println!("worker 0 has polled {} tasks", n); /// } /// ``` pub fn worker_total_busy_duration(&self, worker: usize) -> Duration { let nanos = self .handle .spawner .worker_metrics(worker) .busy_duration_total .load(Relaxed); Duration::from_nanos(nanos) } /// Returns the number of tasks scheduled from **within** the runtime on the /// given worker's local queue. /// /// The local schedule count starts at zero when the runtime is created and /// increases by one each time a task is woken from **inside** of the /// runtime on the given worker. This usually means that a task is spawned /// or notified from within a runtime thread and will be queued on the /// worker-local queue. /// /// The counter is monotonically increasing. It is never decremented or /// reset to zero. /// /// # Arguments /// /// `worker` is the index of the worker being queried. The given value must /// be between 0 and `num_workers()`. The index uniquely identifies a single /// worker and will continue to indentify the worker throughout the lifetime /// of the runtime instance. /// /// # Panics /// /// The method panics when `worker` represents an invalid worker, i.e. is /// greater than or equal to `num_workers()`. /// /// # Examples /// /// ``` /// use tokio::runtime::Handle; /// /// #[tokio::main] /// async fn main() { /// let metrics = Handle::current().metrics(); /// /// let n = metrics.worker_local_schedule_count(0); /// println!("{} tasks were scheduled on the worker's local queue", n); /// } /// ``` pub fn worker_local_schedule_count(&self, worker: usize) -> u64 { self.handle .spawner .worker_metrics(worker) .local_schedule_count .load(Relaxed) } /// Returns the number of times the given worker thread saturated its local /// queue. /// /// This metric only applies to the **multi-threaded** scheduler. /// /// The worker steal count starts at zero when the runtime is created and /// increases by one each time the worker attempts to schedule a task /// locally, but its local queue is full. When this happens, half of the /// local queue is moved to the injection queue. /// /// The counter is monotonically increasing. It is never decremented or /// reset to zero. /// /// # Arguments /// /// `worker` is the index of the worker being queried. The given value must /// be between 0 and `num_workers()`. The index uniquely identifies a single /// worker and will continue to indentify the worker throughout the lifetime /// of the runtime instance. /// /// # Panics /// /// The method panics when `worker` represents an invalid worker, i.e. is /// greater than or equal to `num_workers()`. /// /// # Examples /// /// ``` /// use tokio::runtime::Handle; /// /// #[tokio::main] /// async fn main() { /// let metrics = Handle::current().metrics(); /// /// let n = metrics.worker_overflow_count(0); /// println!("worker 0 has overflowed its queue {} times", n); /// } /// ``` pub fn worker_overflow_count(&self, worker: usize) -> u64 { self.handle .spawner .worker_metrics(worker) .overflow_count .load(Relaxed) } /// Returns the number of tasks currently scheduled in the runtime's /// injection queue. /// /// Tasks that are spanwed or notified from a non-runtime thread are /// scheduled using the runtime's injection queue. This metric returns the /// **current** number of tasks pending in the injection queue. As such, the /// returned value may increase or decrease as new tasks are scheduled and /// processed. /// /// # Examples /// /// ``` /// use tokio::runtime::Handle; /// /// #[tokio::main] /// async fn main() { /// let metrics = Handle::current().metrics(); /// /// let n = metrics.injection_queue_depth(); /// println!("{} tasks currently pending in the runtime's injection queue", n); /// } /// ``` pub fn injection_queue_depth(&self) -> usize { self.handle.spawner.injection_queue_depth() } /// Returns the number of tasks currently scheduled in the given worker's /// local queue. /// /// Tasks that are spawned or notified from within a runtime thread are /// scheduled using that worker's local queue. This metric returns the /// **current** number of tasks pending in the worker's local queue. As /// such, the returned value may increase or decrease as new tasks are /// scheduled and processed. /// /// # Arguments /// /// `worker` is the index of the worker being queried. The given value must /// be between 0 and `num_workers()`. The index uniquely identifies a single /// worker and will continue to indentify the worker throughout the lifetime /// of the runtime instance. /// /// # Panics /// /// The method panics when `worker` represents an invalid worker, i.e. is /// greater than or equal to `num_workers()`. /// /// # Examples /// /// ``` /// use tokio::runtime::Handle; /// /// #[tokio::main] /// async fn main() { /// let metrics = Handle::current().metrics(); /// /// let n = metrics.worker_local_queue_depth(0); /// println!("{} tasks currently pending in worker 0's local queue", n); /// } /// ``` pub fn worker_local_queue_depth(&self, worker: usize) -> usize { self.handle.spawner.worker_local_queue_depth(worker) } }