Skip to main content

tokio/runtime/metrics/
runtime.rs

1use crate::runtime::Handle;
2
3cfg_unstable_metrics! {
4    use std::ops::Range;
5    cfg_64bit_metrics! {
6        use std::sync::atomic::Ordering::Relaxed;
7    }
8    use std::time::Duration;
9}
10
11/// Handle to the runtime's metrics.
12///
13/// This handle is internally reference-counted and can be freely cloned. A
14/// `RuntimeMetrics` handle is obtained using the [`Runtime::metrics`] method.
15///
16/// [`Runtime::metrics`]: crate::runtime::Runtime::metrics()
17#[derive(Clone, Debug)]
18pub struct RuntimeMetrics {
19    handle: Handle,
20}
21
22impl RuntimeMetrics {
23    pub(crate) fn new(handle: Handle) -> RuntimeMetrics {
24        RuntimeMetrics { handle }
25    }
26
27    /// Returns the number of worker threads used by the runtime.
28    ///
29    /// The number of workers is set by configuring `worker_threads` on
30    /// `runtime::Builder`. When using the `current_thread` runtime, the return
31    /// value is always `1`.
32    ///
33    /// # Examples
34    ///
35    /// ```
36    /// use tokio::runtime::Handle;
37    ///
38    /// #[tokio::main]
39    /// async fn main() {
40    ///     let metrics = Handle::current().metrics();
41    ///
42    ///     let n = metrics.num_workers();
43    ///     println!("Runtime is using {} workers", n);
44    /// }
45    /// ```
46    pub fn num_workers(&self) -> usize {
47        self.handle.inner.num_workers()
48    }
49
50    cfg_unstable_metrics! {
51
52        /// Returns the number of additional threads spawned by the runtime.
53        ///
54        /// The number of workers is set by configuring `max_blocking_threads` on
55        /// `runtime::Builder`.
56        ///
57        /// # Examples
58        ///
59        /// ```
60        /// use tokio::runtime::Handle;
61        ///
62        /// #[tokio::main]
63        /// async fn main() {
64        ///     let _ = tokio::task::spawn_blocking(move || {
65        ///         // Stand-in for compute-heavy work or using synchronous APIs
66        ///         1 + 1
67        ///     }).await;
68        ///     let metrics = Handle::current().metrics();
69        ///
70        ///     let n = metrics.num_blocking_threads();
71        ///     println!("Runtime has created {} threads", n);
72        /// }
73        /// ```
74        pub fn num_blocking_threads(&self) -> usize {
75            self.handle.inner.num_blocking_threads()
76        }
77
78        /// Returns the number of active tasks in the runtime.
79        ///
80        /// # Examples
81        ///
82        /// ```
83        /// use tokio::runtime::Handle;
84        ///
85        /// #[tokio::main]
86        /// async fn main() {
87        ///    let metrics = Handle::current().metrics();
88        ///
89        ///     let n = metrics.active_tasks_count();
90        ///     println!("Runtime has {} active tasks", n);
91        /// }
92        /// ```
93        pub fn active_tasks_count(&self) -> usize {
94            self.handle.inner.active_tasks_count()
95        }
96
97        /// Returns the number of idle threads, which have spawned by the runtime
98        /// for `spawn_blocking` calls.
99        ///
100        /// # Examples
101        ///
102        /// ```
103        /// use tokio::runtime::Handle;
104        ///
105        /// #[tokio::main]
106        /// async fn main() {
107        ///     let _ = tokio::task::spawn_blocking(move || {
108        ///         // Stand-in for compute-heavy work or using synchronous APIs
109        ///         1 + 1
110        ///     }).await;
111        ///     let metrics = Handle::current().metrics();
112        ///
113        ///     let n = metrics.num_idle_blocking_threads();
114        ///     println!("Runtime has {} idle blocking thread pool threads", n);
115        /// }
116        /// ```
117        pub fn num_idle_blocking_threads(&self) -> usize {
118            self.handle.inner.num_idle_blocking_threads()
119        }
120
121        cfg_64bit_metrics! {
122                /// Returns the number of tasks scheduled from **outside** of the runtime.
123                ///
124                /// The remote schedule count starts at zero when the runtime is created and
125                /// increases by one each time a task is woken from **outside** of the
126                /// runtime. This usually means that a task is spawned or notified from a
127                /// non-runtime thread and must be queued using the Runtime's injection
128                /// queue, which tends to be slower.
129                ///
130                /// The counter is monotonically increasing. It is never decremented or
131                /// reset to zero.
132                ///
133                /// # Examples
134                ///
135                /// ```
136                /// use tokio::runtime::Handle;
137                ///
138                /// #[tokio::main]
139                /// async fn main() {
140                ///     let metrics = Handle::current().metrics();
141                ///
142                ///     let n = metrics.remote_schedule_count();
143                ///     println!("{} tasks were scheduled from outside the runtime", n);
144                /// }
145                /// ```
146                pub fn remote_schedule_count(&self) -> u64 {
147                    self.handle
148                        .inner
149                        .scheduler_metrics()
150                        .remote_schedule_count
151                        .load(Relaxed)
152                }
153
154                /// Returns the number of times that tasks have been forced to yield back to the scheduler
155                /// after exhausting their task budgets.
156                ///
157                /// This count starts at zero when the runtime is created and increases by one each time a task yields due to exhausting its budget.
158                ///
159                /// The counter is monotonically increasing. It is never decremented or
160                /// reset to zero.
161                pub fn budget_forced_yield_count(&self) -> u64 {
162                    self.handle
163                        .inner
164                        .scheduler_metrics()
165                        .budget_forced_yield_count
166                        .load(Relaxed)
167                }
168
169                /// Returns the total number of times the given worker thread has parked.
170                ///
171                /// The worker park count starts at zero when the runtime is created and
172                /// increases by one each time the worker parks the thread waiting for new
173                /// inbound events to process. This usually means the worker has processed
174                /// all pending work and is currently idle.
175                ///
176                /// The counter is monotonically increasing. It is never decremented or
177                /// reset to zero.
178                ///
179                /// # Arguments
180                ///
181                /// `worker` is the index of the worker being queried. The given value must
182                /// be between 0 and `num_workers()`. The index uniquely identifies a single
183                /// worker and will continue to identify the worker throughout the lifetime
184                /// of the runtime instance.
185                ///
186                /// # Panics
187                ///
188                /// The method panics when `worker` represents an invalid worker, i.e. is
189                /// greater than or equal to `num_workers()`.
190                ///
191                /// # Examples
192                ///
193                /// ```
194                /// use tokio::runtime::Handle;
195                ///
196                /// #[tokio::main]
197                /// async fn main() {
198                ///     let metrics = Handle::current().metrics();
199                ///
200                ///     let n = metrics.worker_park_count(0);
201                ///     println!("worker 0 parked {} times", n);
202                /// }
203                /// ```
204                pub fn worker_park_count(&self, worker: usize) -> u64 {
205                    self.handle
206                        .inner
207                        .worker_metrics(worker)
208                        .park_count
209                        .load(Relaxed)
210                }
211
212                /// Returns the number of times the given worker thread unparked but
213                /// performed no work before parking again.
214                ///
215                /// The worker no-op count starts at zero when the runtime is created and
216                /// increases by one each time the worker unparks the thread but finds no
217                /// new work and goes back to sleep. This indicates a false-positive wake up.
218                ///
219                /// The counter is monotonically increasing. It is never decremented or
220                /// reset to zero.
221                ///
222                /// # Arguments
223                ///
224                /// `worker` is the index of the worker being queried. The given value must
225                /// be between 0 and `num_workers()`. The index uniquely identifies a single
226                /// worker and will continue to identify the worker throughout the lifetime
227                /// of the runtime instance.
228                ///
229                /// # Panics
230                ///
231                /// The method panics when `worker` represents an invalid worker, i.e. is
232                /// greater than or equal to `num_workers()`.
233                ///
234                /// # Examples
235                ///
236                /// ```
237                /// use tokio::runtime::Handle;
238                ///
239                /// #[tokio::main]
240                /// async fn main() {
241                ///     let metrics = Handle::current().metrics();
242                ///
243                ///     let n = metrics.worker_noop_count(0);
244                ///     println!("worker 0 had {} no-op unparks", n);
245                /// }
246                /// ```
247                pub fn worker_noop_count(&self, worker: usize) -> u64 {
248                    self.handle
249                        .inner
250                        .worker_metrics(worker)
251                        .noop_count
252                        .load(Relaxed)
253                }
254
255                /// Returns the number of tasks the given worker thread stole from
256                /// another worker thread.
257                ///
258                /// This metric only applies to the **multi-threaded** runtime and will
259                /// always return `0` when using the current thread runtime.
260                ///
261                /// The worker steal count starts at zero when the runtime is created and
262                /// increases by `N` each time the worker has processed its scheduled queue
263                /// and successfully steals `N` more pending tasks from another worker.
264                ///
265                /// The counter is monotonically increasing. It is never decremented or
266                /// reset to zero.
267                ///
268                /// # Arguments
269                ///
270                /// `worker` is the index of the worker being queried. The given value must
271                /// be between 0 and `num_workers()`. The index uniquely identifies a single
272                /// worker and will continue to identify the worker throughout the lifetime
273                /// of the runtime instance.
274                ///
275                /// # Panics
276                ///
277                /// The method panics when `worker` represents an invalid worker, i.e. is
278                /// greater than or equal to `num_workers()`.
279                ///
280                /// # Examples
281                ///
282                /// ```
283                /// use tokio::runtime::Handle;
284                ///
285                /// #[tokio::main]
286                /// async fn main() {
287                ///     let metrics = Handle::current().metrics();
288                ///
289                ///     let n = metrics.worker_steal_count(0);
290                ///     println!("worker 0 has stolen {} tasks", n);
291                /// }
292                /// ```
293                pub fn worker_steal_count(&self, worker: usize) -> u64 {
294                    self.handle
295                        .inner
296                        .worker_metrics(worker)
297                        .steal_count
298                        .load(Relaxed)
299                }
300
301                /// Returns the number of times the given worker thread stole tasks from
302                /// another worker thread.
303                ///
304                /// This metric only applies to the **multi-threaded** runtime and will
305                /// always return `0` when using the current thread runtime.
306                ///
307                /// The worker steal count starts at zero when the runtime is created and
308                /// increases by one each time the worker has processed its scheduled queue
309                /// and successfully steals more pending tasks from another worker.
310                ///
311                /// The counter is monotonically increasing. It is never decremented or
312                /// reset to zero.
313                ///
314                /// # Arguments
315                ///
316                /// `worker` is the index of the worker being queried. The given value must
317                /// be between 0 and `num_workers()`. The index uniquely identifies a single
318                /// worker and will continue to identify the worker throughout the lifetime
319                /// of the runtime instance.
320                ///
321                /// # Panics
322                ///
323                /// The method panics when `worker` represents an invalid worker, i.e. is
324                /// greater than or equal to `num_workers()`.
325                ///
326                /// # Examples
327                ///
328                /// ```
329                /// use tokio::runtime::Handle;
330                ///
331                /// #[tokio::main]
332                /// async fn main() {
333                ///     let metrics = Handle::current().metrics();
334                ///
335                ///     let n = metrics.worker_steal_operations(0);
336                ///     println!("worker 0 has stolen tasks {} times", n);
337                /// }
338                /// ```
339                pub fn worker_steal_operations(&self, worker: usize) -> u64 {
340                    self.handle
341                        .inner
342                        .worker_metrics(worker)
343                        .steal_operations
344                        .load(Relaxed)
345                }
346
347                /// Returns the number of tasks the given worker thread has polled.
348                ///
349                /// The worker poll count starts at zero when the runtime is created and
350                /// increases by one each time the worker polls a scheduled task.
351                ///
352                /// The counter is monotonically increasing. It is never decremented or
353                /// reset to zero.
354                ///
355                /// # Arguments
356                ///
357                /// `worker` is the index of the worker being queried. The given value must
358                /// be between 0 and `num_workers()`. The index uniquely identifies a single
359                /// worker and will continue to identify the worker throughout the lifetime
360                /// of the runtime instance.
361                ///
362                /// # Panics
363                ///
364                /// The method panics when `worker` represents an invalid worker, i.e. is
365                /// greater than or equal to `num_workers()`.
366                ///
367                /// # Examples
368                ///
369                /// ```
370                /// use tokio::runtime::Handle;
371                ///
372                /// #[tokio::main]
373                /// async fn main() {
374                ///     let metrics = Handle::current().metrics();
375                ///
376                ///     let n = metrics.worker_poll_count(0);
377                ///     println!("worker 0 has polled {} tasks", n);
378                /// }
379                /// ```
380                pub fn worker_poll_count(&self, worker: usize) -> u64 {
381                    self.handle
382                        .inner
383                        .worker_metrics(worker)
384                        .poll_count
385                        .load(Relaxed)
386                }
387
388            /// Returns the amount of time the given worker thread has been busy.
389            ///
390            /// The worker busy duration starts at zero when the runtime is created and
391            /// increases whenever the worker is spending time processing work. Using
392            /// this value can indicate the load of the given worker. If a lot of time
393            /// is spent busy, then the worker is under load and will check for inbound
394            /// events less often.
395            ///
396            /// The timer is monotonically increasing. It is never decremented or reset
397            /// to zero.
398            ///
399            /// # Arguments
400            ///
401            /// `worker` is the index of the worker being queried. The given value must
402            /// be between 0 and `num_workers()`. The index uniquely identifies a single
403            /// worker and will continue to identify the worker throughout the lifetime
404            /// of the runtime instance.
405            ///
406            /// # Panics
407            ///
408            /// The method panics when `worker` represents an invalid worker, i.e. is
409            /// greater than or equal to `num_workers()`.
410            ///
411            /// # Examples
412            ///
413            /// ```
414            /// use tokio::runtime::Handle;
415            ///
416            /// #[tokio::main]
417            /// async fn main() {
418            ///     let metrics = Handle::current().metrics();
419            ///
420            ///     let n = metrics.worker_total_busy_duration(0);
421            ///     println!("worker 0 was busy for a total of {:?}", n);
422            /// }
423            /// ```
424            pub fn worker_total_busy_duration(&self, worker: usize) -> Duration {
425                let nanos = self
426                    .handle
427                    .inner
428                    .worker_metrics(worker)
429                    .busy_duration_total
430                    .load(Relaxed);
431                Duration::from_nanos(nanos)
432            }
433
434            /// Returns the number of tasks scheduled from **within** the runtime on the
435            /// given worker's local queue.
436            ///
437            /// The local schedule count starts at zero when the runtime is created and
438            /// increases by one each time a task is woken from **inside** of the
439            /// runtime on the given worker. This usually means that a task is spawned
440            /// or notified from within a runtime thread and will be queued on the
441            /// worker-local queue.
442            ///
443            /// The counter is monotonically increasing. It is never decremented or
444            /// reset to zero.
445            ///
446            /// # Arguments
447            ///
448            /// `worker` is the index of the worker being queried. The given value must
449            /// be between 0 and `num_workers()`. The index uniquely identifies a single
450            /// worker and will continue to identify the worker throughout the lifetime
451            /// of the runtime instance.
452            ///
453            /// # Panics
454            ///
455            /// The method panics when `worker` represents an invalid worker, i.e. is
456            /// greater than or equal to `num_workers()`.
457            ///
458            /// # Examples
459            ///
460            /// ```
461            /// use tokio::runtime::Handle;
462            ///
463            /// #[tokio::main]
464            /// async fn main() {
465            ///     let metrics = Handle::current().metrics();
466            ///
467            ///     let n = metrics.worker_local_schedule_count(0);
468            ///     println!("{} tasks were scheduled on the worker's local queue", n);
469            /// }
470            /// ```
471            pub fn worker_local_schedule_count(&self, worker: usize) -> u64 {
472                self.handle
473                    .inner
474                    .worker_metrics(worker)
475                    .local_schedule_count
476                    .load(Relaxed)
477            }
478
479            /// Returns the number of times the given worker thread saturated its local
480            /// queue.
481            ///
482            /// This metric only applies to the **multi-threaded** scheduler.
483            ///
484            /// The worker overflow count starts at zero when the runtime is created and
485            /// increases by one each time the worker attempts to schedule a task
486            /// locally, but its local queue is full. When this happens, half of the
487            /// local queue is moved to the injection queue.
488            ///
489            /// The counter is monotonically increasing. It is never decremented or
490            /// reset to zero.
491            ///
492            /// # Arguments
493            ///
494            /// `worker` is the index of the worker being queried. The given value must
495            /// be between 0 and `num_workers()`. The index uniquely identifies a single
496            /// worker and will continue to identify the worker throughout the lifetime
497            /// of the runtime instance.
498            ///
499            /// # Panics
500            ///
501            /// The method panics when `worker` represents an invalid worker, i.e. is
502            /// greater than or equal to `num_workers()`.
503            ///
504            /// # Examples
505            ///
506            /// ```
507            /// use tokio::runtime::Handle;
508            ///
509            /// #[tokio::main]
510            /// async fn main() {
511            ///     let metrics = Handle::current().metrics();
512            ///
513            ///     let n = metrics.worker_overflow_count(0);
514            ///     println!("worker 0 has overflowed its queue {} times", n);
515            /// }
516            /// ```
517            pub fn worker_overflow_count(&self, worker: usize) -> u64 {
518                self.handle
519                    .inner
520                    .worker_metrics(worker)
521                    .overflow_count
522                    .load(Relaxed)
523            }
524        }
525
526        /// Returns the number of tasks currently scheduled in the runtime's
527        /// injection queue.
528        ///
529        /// Tasks that are spawned or notified from a non-runtime thread are
530        /// scheduled using the runtime's injection queue. This metric returns the
531        /// **current** number of tasks pending in the injection queue. As such, the
532        /// returned value may increase or decrease as new tasks are scheduled and
533        /// processed.
534        ///
535        /// # Examples
536        ///
537        /// ```
538        /// use tokio::runtime::Handle;
539        ///
540        /// #[tokio::main]
541        /// async fn main() {
542        ///     let metrics = Handle::current().metrics();
543        ///
544        ///     let n = metrics.injection_queue_depth();
545        ///     println!("{} tasks currently pending in the runtime's injection queue", n);
546        /// }
547        /// ```
548        pub fn injection_queue_depth(&self) -> usize {
549            self.handle.inner.injection_queue_depth()
550        }
551
552        /// Returns the number of tasks currently scheduled in the given worker's
553        /// local queue.
554        ///
555        /// Tasks that are spawned or notified from within a runtime thread are
556        /// scheduled using that worker's local queue. This metric returns the
557        /// **current** number of tasks pending in the worker's local queue. As
558        /// such, the returned value may increase or decrease as new tasks are
559        /// scheduled and processed.
560        ///
561        /// # Arguments
562        ///
563        /// `worker` is the index of the worker being queried. The given value must
564        /// be between 0 and `num_workers()`. The index uniquely identifies a single
565        /// worker and will continue to identify the worker throughout the lifetime
566        /// of the runtime instance.
567        ///
568        /// # Panics
569        ///
570        /// The method panics when `worker` represents an invalid worker, i.e. is
571        /// greater than or equal to `num_workers()`.
572        ///
573        /// # Examples
574        ///
575        /// ```
576        /// use tokio::runtime::Handle;
577        ///
578        /// #[tokio::main]
579        /// async fn main() {
580        ///     let metrics = Handle::current().metrics();
581        ///
582        ///     let n = metrics.worker_local_queue_depth(0);
583        ///     println!("{} tasks currently pending in worker 0's local queue", n);
584        /// }
585        /// ```
586        pub fn worker_local_queue_depth(&self, worker: usize) -> usize {
587            self.handle.inner.worker_local_queue_depth(worker)
588        }
589
590        /// Returns `true` if the runtime is tracking the distribution of task poll
591        /// times.
592        ///
593        /// Task poll times are not instrumented by default as doing so requires
594        /// calling [`Instant::now()`] twice per task poll. The feature is enabled
595        /// by calling [`enable_metrics_poll_count_histogram()`] when building the
596        /// runtime.
597        ///
598        /// # Examples
599        ///
600        /// ```
601        /// use tokio::runtime::{self, Handle};
602        ///
603        /// fn main() {
604        ///     runtime::Builder::new_current_thread()
605        ///         .enable_metrics_poll_count_histogram()
606        ///         .build()
607        ///         .unwrap()
608        ///         .block_on(async {
609        ///             let metrics = Handle::current().metrics();
610        ///             let enabled = metrics.poll_count_histogram_enabled();
611        ///
612        ///             println!("Tracking task poll time distribution: {:?}", enabled);
613        ///         });
614        /// }
615        /// ```
616        ///
617        /// [`enable_metrics_poll_count_histogram()`]: crate::runtime::Builder::enable_metrics_poll_count_histogram
618        /// [`Instant::now()`]: std::time::Instant::now
619        pub fn poll_count_histogram_enabled(&self) -> bool {
620            self.handle
621                .inner
622                .worker_metrics(0)
623                .poll_count_histogram
624                .is_some()
625        }
626
627        /// Returns the number of histogram buckets tracking the distribution of
628        /// task poll times.
629        ///
630        /// This value is configured by calling
631        /// [`metrics_poll_count_histogram_buckets()`] when building the runtime.
632        ///
633        /// # Examples
634        ///
635        /// ```
636        /// use tokio::runtime::{self, Handle};
637        ///
638        /// fn main() {
639        ///     runtime::Builder::new_current_thread()
640        ///         .enable_metrics_poll_count_histogram()
641        ///         .build()
642        ///         .unwrap()
643        ///         .block_on(async {
644        ///             let metrics = Handle::current().metrics();
645        ///             let buckets = metrics.poll_count_histogram_num_buckets();
646        ///
647        ///             println!("Histogram buckets: {:?}", buckets);
648        ///         });
649        /// }
650        /// ```
651        ///
652        /// [`metrics_poll_count_histogram_buckets()`]:
653        ///     crate::runtime::Builder::metrics_poll_count_histogram_buckets
654        pub fn poll_count_histogram_num_buckets(&self) -> usize {
655            self.handle
656                .inner
657                .worker_metrics(0)
658                .poll_count_histogram
659                .as_ref()
660                .map(|histogram| histogram.num_buckets())
661                .unwrap_or_default()
662        }
663
664        /// Returns the range of task poll times tracked by the given bucket.
665        ///
666        /// This value is configured by calling
667        /// [`metrics_poll_count_histogram_resolution()`] when building the runtime.
668        ///
669        /// # Panics
670        ///
671        /// The method panics if `bucket` represents an invalid bucket index, i.e.
672        /// is greater than or equal to `poll_count_histogram_num_buckets()`.
673        ///
674        /// # Examples
675        ///
676        /// ```
677        /// use tokio::runtime::{self, Handle};
678        ///
679        /// fn main() {
680        ///     runtime::Builder::new_current_thread()
681        ///         .enable_metrics_poll_count_histogram()
682        ///         .build()
683        ///         .unwrap()
684        ///         .block_on(async {
685        ///             let metrics = Handle::current().metrics();
686        ///             let buckets = metrics.poll_count_histogram_num_buckets();
687        ///
688        ///             for i in 0..buckets {
689        ///                 let range = metrics.poll_count_histogram_bucket_range(i);
690        ///                 println!("Histogram bucket {} range: {:?}", i, range);
691        ///             }
692        ///         });
693        /// }
694        /// ```
695        ///
696        /// [`metrics_poll_count_histogram_resolution()`]:
697        ///     crate::runtime::Builder::metrics_poll_count_histogram_resolution
698        #[track_caller]
699        pub fn poll_count_histogram_bucket_range(&self, bucket: usize) -> Range<Duration> {
700            self.handle
701                .inner
702                .worker_metrics(0)
703                .poll_count_histogram
704                .as_ref()
705                .map(|histogram| {
706                    let range = histogram.bucket_range(bucket);
707                    std::ops::Range {
708                        start: Duration::from_nanos(range.start),
709                        end: Duration::from_nanos(range.end),
710                    }
711                })
712                .unwrap_or_default()
713        }
714
715        cfg_64bit_metrics! {
716            /// Returns the number of times the given worker polled tasks with a poll
717            /// duration within the given bucket's range.
718            ///
719            /// Each worker maintains its own histogram and the counts for each bucket
720            /// starts at zero when the runtime is created. Each time the worker polls a
721            /// task, it tracks the duration the task poll time took and increments the
722            /// associated bucket by 1.
723            ///
724            /// Each bucket is a monotonically increasing counter. It is never
725            /// decremented or reset to zero.
726            ///
727            /// # Arguments
728            ///
729            /// `worker` is the index of the worker being queried. The given value must
730            /// be between 0 and `num_workers()`. The index uniquely identifies a single
731            /// worker and will continue to identify the worker throughout the lifetime
732            /// of the runtime instance.
733            ///
734            /// `bucket` is the index of the bucket being queried. The bucket is scoped
735            /// to the worker. The range represented by the bucket can be queried by
736            /// calling [`poll_count_histogram_bucket_range()`]. Each worker maintains
737            /// identical bucket ranges.
738            ///
739            /// # Panics
740            ///
741            /// The method panics when `worker` represents an invalid worker, i.e. is
742            /// greater than or equal to `num_workers()` or if `bucket` represents an
743            /// invalid bucket.
744            ///
745            /// # Examples
746            ///
747            /// ```
748            /// use tokio::runtime::{self, Handle};
749            ///
750            /// fn main() {
751            ///     runtime::Builder::new_current_thread()
752            ///         .enable_metrics_poll_count_histogram()
753            ///         .build()
754            ///         .unwrap()
755            ///         .block_on(async {
756            ///             let metrics = Handle::current().metrics();
757            ///             let buckets = metrics.poll_count_histogram_num_buckets();
758            ///
759            ///             for worker in 0..metrics.num_workers() {
760            ///                 for i in 0..buckets {
761            ///                     let count = metrics.poll_count_histogram_bucket_count(worker, i);
762            ///                     println!("Poll count {}", count);
763            ///                 }
764            ///             }
765            ///         });
766            /// }
767            /// ```
768            ///
769            /// [`poll_count_histogram_bucket_range()`]: crate::runtime::RuntimeMetrics::poll_count_histogram_bucket_range
770            #[track_caller]
771            pub fn poll_count_histogram_bucket_count(&self, worker: usize, bucket: usize) -> u64 {
772                self.handle
773                    .inner
774                    .worker_metrics(worker)
775                    .poll_count_histogram
776                    .as_ref()
777                    .map(|histogram| histogram.get(bucket))
778                    .unwrap_or_default()
779            }
780
781            /// Returns the mean duration of task polls, in nanoseconds.
782            ///
783            /// This is an exponentially weighted moving average. Currently, this metric
784            /// is only provided by the multi-threaded runtime.
785            ///
786            /// # Arguments
787            ///
788            /// `worker` is the index of the worker being queried. The given value must
789            /// be between 0 and `num_workers()`. The index uniquely identifies a single
790            /// worker and will continue to identify the worker throughout the lifetime
791            /// of the runtime instance.
792            ///
793            /// # Panics
794            ///
795            /// The method panics when `worker` represents an invalid worker, i.e. is
796            /// greater than or equal to `num_workers()`.
797            ///
798            /// # Examples
799            ///
800            /// ```
801            /// use tokio::runtime::Handle;
802            ///
803            /// #[tokio::main]
804            /// async fn main() {
805            ///     let metrics = Handle::current().metrics();
806            ///
807            ///     let n = metrics.worker_mean_poll_time(0);
808            ///     println!("worker 0 has a mean poll time of {:?}", n);
809            /// }
810            /// ```
811            #[track_caller]
812            pub fn worker_mean_poll_time(&self, worker: usize) -> Duration {
813                let nanos = self
814                    .handle
815                    .inner
816                    .worker_metrics(worker)
817                    .mean_poll_time
818                    .load(Relaxed);
819                Duration::from_nanos(nanos)
820            }
821        }
822
823        /// Returns the number of tasks currently scheduled in the blocking
824        /// thread pool, spawned using `spawn_blocking`.
825        ///
826        /// This metric returns the **current** number of tasks pending in
827        /// blocking thread pool. As such, the returned value may increase
828        /// or decrease as new tasks are scheduled and processed.
829        ///
830        /// # Examples
831        ///
832        /// ```
833        /// use tokio::runtime::Handle;
834        ///
835        /// #[tokio::main]
836        /// async fn main() {
837        ///     let metrics = Handle::current().metrics();
838        ///
839        ///     let n = metrics.blocking_queue_depth();
840        ///     println!("{} tasks currently pending in the blocking thread pool", n);
841        /// }
842        /// ```
843        pub fn blocking_queue_depth(&self) -> usize {
844            self.handle.inner.blocking_queue_depth()
845        }
846
847        cfg_net! {
848            cfg_64bit_metrics! {
849                /// Returns the number of file descriptors that have been registered with the
850                /// runtime's I/O driver.
851                ///
852                /// # Examples
853                ///
854                /// ```
855                /// use tokio::runtime::Handle;
856                ///
857                /// #[tokio::main]
858                /// async fn main() {
859                ///     let metrics = Handle::current().metrics();
860                ///
861                ///     let registered_fds = metrics.io_driver_fd_registered_count();
862                ///     println!("{} fds have been registered with the runtime's I/O driver.", registered_fds);
863                ///
864                ///     let deregistered_fds = metrics.io_driver_fd_deregistered_count();
865                ///
866                ///     let current_fd_count = registered_fds - deregistered_fds;
867                ///     println!("{} fds are currently registered by the runtime's I/O driver.", current_fd_count);
868                /// }
869                /// ```
870                pub fn io_driver_fd_registered_count(&self) -> u64 {
871                    self.with_io_driver_metrics(|m| {
872                        m.fd_registered_count.load(Relaxed)
873                    })
874                }
875
876                /// Returns the number of file descriptors that have been deregistered by the
877                /// runtime's I/O driver.
878                ///
879                /// # Examples
880                ///
881                /// ```
882                /// use tokio::runtime::Handle;
883                ///
884                /// #[tokio::main]
885                /// async fn main() {
886                ///     let metrics = Handle::current().metrics();
887                ///
888                ///     let n = metrics.io_driver_fd_deregistered_count();
889                ///     println!("{} fds have been deregistered by the runtime's I/O driver.", n);
890                /// }
891                /// ```
892                pub fn io_driver_fd_deregistered_count(&self) -> u64 {
893                    self.with_io_driver_metrics(|m| {
894                        m.fd_deregistered_count.load(Relaxed)
895                    })
896                }
897
898                /// Returns the number of ready events processed by the runtime's
899                /// I/O driver.
900                ///
901                /// # Examples
902                ///
903                /// ```
904                /// use tokio::runtime::Handle;
905                ///
906                /// #[tokio::main]
907                /// async fn main() {
908                ///     let metrics = Handle::current().metrics();
909                ///
910                ///     let n = metrics.io_driver_ready_count();
911                ///     println!("{} ready events processed by the runtime's I/O driver.", n);
912                /// }
913                /// ```
914                pub fn io_driver_ready_count(&self) -> u64 {
915                    self.with_io_driver_metrics(|m| m.ready_count.load(Relaxed))
916                }
917
918                fn with_io_driver_metrics<F>(&self, f: F) -> u64
919                where
920                    F: Fn(&super::IoDriverMetrics) -> u64,
921                {
922                    // TODO: Investigate if this should return 0, most of our metrics always increase
923                    // thus this breaks that guarantee.
924                    self.handle
925                        .inner
926                        .driver()
927                        .io
928                        .as_ref()
929                        .map(|h| f(&h.metrics))
930                        .unwrap_or(0)
931                }
932            }
933        }
934    }
935}