madsim_real_tokio/runtime/metrics/
runtime.rs

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