From cba86cf1b1edea8cd131f168a99953e6e35739d2 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Thu, 23 May 2024 12:41:01 -0400 Subject: [PATCH] metrics: add MetricAtomicU64 and use in metrics (#6574) --- spellcheck.dic | 3 +- tokio/src/macros/cfg.rs | 28 +- tokio/src/runtime/metrics/histogram.rs | 15 +- tokio/src/runtime/metrics/io.rs | 14 +- tokio/src/runtime/metrics/runtime.rs | 1158 ++++++++++++------------ tokio/src/runtime/metrics/scheduler.rs | 15 +- tokio/src/runtime/metrics/worker.rs | 39 +- tokio/src/runtime/tests/queue.rs | 21 +- tokio/src/util/metric_atomics.rs | 47 + tokio/src/util/mod.rs | 2 + tokio/tests/rt_basic.rs | 2 +- tokio/tests/rt_metrics.rs | 7 +- tokio/tests/rt_threaded.rs | 2 +- tokio/tests/rt_threaded_alt.rs | 2 +- 14 files changed, 722 insertions(+), 633 deletions(-) create mode 100644 tokio/src/util/metric_atomics.rs diff --git a/spellcheck.dic b/spellcheck.dic index 4b9288118d2..238e24f7dc3 100644 --- a/spellcheck.dic +++ b/spellcheck.dic @@ -1,4 +1,4 @@ -284 +285 & + < @@ -34,6 +34,7 @@ amongst api APIs async +atomics awaitable backend backpressure diff --git a/tokio/src/macros/cfg.rs b/tokio/src/macros/cfg.rs index c67e0e8379f..f44599ff47a 100644 --- a/tokio/src/macros/cfg.rs +++ b/tokio/src/macros/cfg.rs @@ -218,19 +218,37 @@ macro_rules! cfg_macros { macro_rules! cfg_metrics { ($($item:item)*) => { $( - // For now, metrics is only disabled in loom tests. - // When stabilized, it might have a dedicated feature flag. - #[cfg(all(tokio_unstable, not(loom)))] + #[cfg(tokio_unstable)] #[cfg_attr(docsrs, doc(cfg(tokio_unstable)))] $item )* } } +/// Some metrics require 64-bit atomics. +macro_rules! cfg_64bit_metrics { + ($($item:item)*) => { + $( + #[cfg(target_has_atomic = "64")] + #[cfg_attr(docsrs, doc(cfg(target_has_atomic = "64")))] + $item + )* + } +} + +macro_rules! cfg_no_64bit_metrics { + ($($item:item)*) => { + $( + #[cfg(not(target_has_atomic = "64"))] + $item + )* + } +} + macro_rules! cfg_not_metrics { ($($item:item)*) => { $( - #[cfg(not(all(tokio_unstable, not(loom))))] + #[cfg(not(tokio_unstable))] $item )* } @@ -238,7 +256,7 @@ macro_rules! cfg_not_metrics { macro_rules! cfg_not_rt_and_metrics_and_net { ($($item:item)*) => { - $( #[cfg(not(all(feature = "net", feature = "rt", all(tokio_unstable, not(loom)))))]$item )* + $( #[cfg(not(all(feature = "net", feature = "rt", tokio_unstable)))]$item )* } } diff --git a/tokio/src/runtime/metrics/histogram.rs b/tokio/src/runtime/metrics/histogram.rs index 976f54fe852..f75ffa3b495 100644 --- a/tokio/src/runtime/metrics/histogram.rs +++ b/tokio/src/runtime/metrics/histogram.rs @@ -1,4 +1,5 @@ -use crate::loom::sync::atomic::{AtomicU64, Ordering::Relaxed}; +use crate::loom::sync::atomic::Ordering::Relaxed; +use crate::util::metric_atomics::MetricAtomicU64; use std::cmp; use std::ops::Range; @@ -6,7 +7,7 @@ use std::ops::Range; #[derive(Debug)] pub(crate) struct Histogram { /// The histogram buckets - buckets: Box<[AtomicU64]>, + buckets: Box<[MetricAtomicU64]>, /// Bucket scale, linear or log scale: HistogramScale, @@ -53,8 +54,10 @@ impl Histogram { self.buckets.len() } - pub(crate) fn get(&self, bucket: usize) -> u64 { - self.buckets[bucket].load(Relaxed) + cfg_64bit_metrics! { + pub(crate) fn get(&self, bucket: usize) -> u64 { + self.buckets[bucket].load(Relaxed) + } } pub(crate) fn bucket_range(&self, bucket: usize) -> Range { @@ -150,7 +153,7 @@ impl HistogramBuilder { Histogram { buckets: (0..self.num_buckets) - .map(|_| AtomicU64::new(0)) + .map(|_| MetricAtomicU64::new(0)) .collect::>() .into_boxed_slice(), resolution, @@ -165,7 +168,7 @@ impl Default for HistogramBuilder { } } -#[cfg(test)] +#[cfg(all(test, target_has_atomic = "64"))] mod test { use super::*; diff --git a/tokio/src/runtime/metrics/io.rs b/tokio/src/runtime/metrics/io.rs index 06efdd42d72..674fca5faec 100644 --- a/tokio/src/runtime/metrics/io.rs +++ b/tokio/src/runtime/metrics/io.rs @@ -1,24 +1,24 @@ #![cfg_attr(not(feature = "net"), allow(dead_code))] -use crate::loom::sync::atomic::{AtomicU64, Ordering::Relaxed}; +use crate::{loom::sync::atomic::Ordering::Relaxed, util::metric_atomics::MetricAtomicU64}; #[derive(Default)] pub(crate) struct IoDriverMetrics { - pub(super) fd_registered_count: AtomicU64, - pub(super) fd_deregistered_count: AtomicU64, - pub(super) ready_count: AtomicU64, + pub(super) fd_registered_count: MetricAtomicU64, + pub(super) fd_deregistered_count: MetricAtomicU64, + pub(super) ready_count: MetricAtomicU64, } impl IoDriverMetrics { pub(crate) fn incr_fd_count(&self) { - self.fd_registered_count.fetch_add(1, Relaxed); + self.fd_registered_count.add(1, Relaxed); } pub(crate) fn dec_fd_count(&self) { - self.fd_deregistered_count.fetch_add(1, Relaxed); + self.fd_deregistered_count.add(1, Relaxed); } pub(crate) fn incr_ready_count_by(&self, amt: u64) { - self.ready_count.fetch_add(amt, Relaxed); + self.ready_count.add(amt, Relaxed); } } diff --git a/tokio/src/runtime/metrics/runtime.rs b/tokio/src/runtime/metrics/runtime.rs index 66a3e51bb97..865a6406a6a 100644 --- a/tokio/src/runtime/metrics/runtime.rs +++ b/tokio/src/runtime/metrics/runtime.rs @@ -1,7 +1,9 @@ use crate::runtime::Handle; use std::ops::Range; -use std::sync::atomic::Ordering::Relaxed; +cfg_64bit_metrics! { + use std::sync::atomic::Ordering::Relaxed; +} use std::time::Duration; /// Handle to the runtime's metrics. @@ -112,407 +114,409 @@ impl RuntimeMetrics { self.handle.inner.num_idle_blocking_threads() } - /// 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 - .inner - .scheduler_metrics() - .remote_schedule_count - .load(Relaxed) - } + cfg_64bit_metrics! { + /// 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 + .inner + .scheduler_metrics() + .remote_schedule_count + .load(Relaxed) + } - /// Returns the number of times that tasks have been forced to yield back to the scheduler - /// after exhausting their task budgets. - /// - /// This count starts at zero when the runtime is created and increases by one each time a task yields due to exhausting its budget. - /// - /// The counter is monotonically increasing. It is never decremented or - /// reset to zero. - pub fn budget_forced_yield_count(&self) -> u64 { - self.handle - .inner - .scheduler_metrics() - .budget_forced_yield_count - .load(Relaxed) - } + /// Returns the number of times that tasks have been forced to yield back to the scheduler + /// after exhausting their task budgets. + /// + /// This count starts at zero when the runtime is created and increases by one each time a task yields due to exhausting its budget. + /// + /// The counter is monotonically increasing. It is never decremented or + /// reset to zero. + pub fn budget_forced_yield_count(&self) -> u64 { + self.handle + .inner + .scheduler_metrics() + .budget_forced_yield_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 identify 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 - .inner - .worker_metrics(worker) - .park_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 identify 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 + .inner + .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 identify 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 - .inner - .worker_metrics(worker) - .noop_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 identify 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 + .inner + .worker_metrics(worker) + .noop_count + .load(Relaxed) + } - /// Returns the number of tasks the given worker thread stole 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 `N` each time the worker has processed its scheduled queue - /// and successfully steals `N` 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 identify 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_steal_count(0); - /// println!("worker 0 has stolen {} tasks", n); - /// } - /// ``` - pub fn worker_steal_count(&self, worker: usize) -> u64 { - self.handle - .inner - .worker_metrics(worker) - .steal_count - .load(Relaxed) - } + /// Returns the number of tasks the given worker thread stole 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 `N` each time the worker has processed its scheduled queue + /// and successfully steals `N` 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 identify 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_steal_count(0); + /// println!("worker 0 has stolen {} tasks", n); + /// } + /// ``` + pub fn worker_steal_count(&self, worker: usize) -> u64 { + self.handle + .inner + .worker_metrics(worker) + .steal_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 identify 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_steal_operations(0); - /// println!("worker 0 has stolen tasks {} times", n); - /// } - /// ``` - pub fn worker_steal_operations(&self, worker: usize) -> u64 { - self.handle - .inner - .worker_metrics(worker) - .steal_operations - .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 identify 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_steal_operations(0); + /// println!("worker 0 has stolen tasks {} times", n); + /// } + /// ``` + pub fn worker_steal_operations(&self, worker: usize) -> u64 { + self.handle + .inner + .worker_metrics(worker) + .steal_operations + .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 identify 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 - .inner - .worker_metrics(worker) - .poll_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 identify 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 + .inner + .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 identify 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_total_busy_duration(0); - /// println!("worker 0 was busy for a total of {:?}", n); - /// } - /// ``` - pub fn worker_total_busy_duration(&self, worker: usize) -> Duration { - let nanos = self - .handle - .inner - .worker_metrics(worker) - .busy_duration_total - .load(Relaxed); - Duration::from_nanos(nanos) - } + /// 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 identify 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_total_busy_duration(0); + /// println!("worker 0 was busy for a total of {:?}", n); + /// } + /// ``` + pub fn worker_total_busy_duration(&self, worker: usize) -> Duration { + let nanos = self + .handle + .inner + .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 identify 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 - .inner - .worker_metrics(worker) - .local_schedule_count - .load(Relaxed) - } + /// 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 identify 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 + .inner + .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 overflow 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 identify 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 - .inner - .worker_metrics(worker) - .overflow_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 overflow 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 identify 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 + .inner + .worker_metrics(worker) + .overflow_count + .load(Relaxed) + } } /// Returns the number of tasks currently scheduled in the runtime's @@ -704,110 +708,112 @@ impl RuntimeMetrics { .unwrap_or_default() } - /// Returns the number of times the given worker polled tasks with a poll - /// duration within the given bucket's range. - /// - /// Each worker maintains its own histogram and the counts for each bucket - /// starts at zero when the runtime is created. Each time the worker polls a - /// task, it tracks the duration the task poll time took and increments the - /// associated bucket by 1. - /// - /// Each bucket is a monotonically increasing counter. 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 identify the worker throughout the lifetime - /// of the runtime instance. - /// - /// `bucket` is the index of the bucket being queried. The bucket is scoped - /// to the worker. The range represented by the bucket can be queried by - /// calling [`poll_count_histogram_bucket_range()`]. Each worker maintains - /// identical bucket ranges. - /// - /// # Panics - /// - /// The method panics when `worker` represents an invalid worker, i.e. is - /// greater than or equal to `num_workers()` or if `bucket` represents an - /// invalid bucket. - /// - /// # Examples - /// - /// ``` - /// use tokio::runtime::{self, Handle}; - /// - /// fn main() { - /// runtime::Builder::new_current_thread() - /// .enable_metrics_poll_count_histogram() - /// .build() - /// .unwrap() - /// .block_on(async { - /// let metrics = Handle::current().metrics(); - /// let buckets = metrics.poll_count_histogram_num_buckets(); - /// - /// for worker in 0..metrics.num_workers() { - /// for i in 0..buckets { - /// let count = metrics.poll_count_histogram_bucket_count(worker, i); - /// println!("Poll count {}", count); - /// } - /// } - /// }); - /// } - /// ``` - /// - /// [`poll_count_histogram_bucket_range()`]: crate::runtime::RuntimeMetrics::poll_count_histogram_bucket_range - #[track_caller] - pub fn poll_count_histogram_bucket_count(&self, worker: usize, bucket: usize) -> u64 { - self.handle - .inner - .worker_metrics(worker) - .poll_count_histogram - .as_ref() - .map(|histogram| histogram.get(bucket)) - .unwrap_or_default() - } + cfg_64bit_metrics! { + /// Returns the number of times the given worker polled tasks with a poll + /// duration within the given bucket's range. + /// + /// Each worker maintains its own histogram and the counts for each bucket + /// starts at zero when the runtime is created. Each time the worker polls a + /// task, it tracks the duration the task poll time took and increments the + /// associated bucket by 1. + /// + /// Each bucket is a monotonically increasing counter. 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 identify the worker throughout the lifetime + /// of the runtime instance. + /// + /// `bucket` is the index of the bucket being queried. The bucket is scoped + /// to the worker. The range represented by the bucket can be queried by + /// calling [`poll_count_histogram_bucket_range()`]. Each worker maintains + /// identical bucket ranges. + /// + /// # Panics + /// + /// The method panics when `worker` represents an invalid worker, i.e. is + /// greater than or equal to `num_workers()` or if `bucket` represents an + /// invalid bucket. + /// + /// # Examples + /// + /// ``` + /// use tokio::runtime::{self, Handle}; + /// + /// fn main() { + /// runtime::Builder::new_current_thread() + /// .enable_metrics_poll_count_histogram() + /// .build() + /// .unwrap() + /// .block_on(async { + /// let metrics = Handle::current().metrics(); + /// let buckets = metrics.poll_count_histogram_num_buckets(); + /// + /// for worker in 0..metrics.num_workers() { + /// for i in 0..buckets { + /// let count = metrics.poll_count_histogram_bucket_count(worker, i); + /// println!("Poll count {}", count); + /// } + /// } + /// }); + /// } + /// ``` + /// + /// [`poll_count_histogram_bucket_range()`]: crate::runtime::RuntimeMetrics::poll_count_histogram_bucket_range + #[track_caller] + pub fn poll_count_histogram_bucket_count(&self, worker: usize, bucket: usize) -> u64 { + self.handle + .inner + .worker_metrics(worker) + .poll_count_histogram + .as_ref() + .map(|histogram| histogram.get(bucket)) + .unwrap_or_default() + } - /// Returns the mean duration of task polls, in nanoseconds. - /// - /// This is an exponentially weighted moving average. Currently, this metric - /// is only provided by the multi-threaded runtime. - /// - /// # 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 identify 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_mean_poll_time(0); - /// println!("worker 0 has a mean poll time of {:?}", n); - /// } - /// ``` - #[track_caller] - pub fn worker_mean_poll_time(&self, worker: usize) -> Duration { - let nanos = self - .handle - .inner - .worker_metrics(worker) - .mean_poll_time - .load(Relaxed); - Duration::from_nanos(nanos) + /// Returns the mean duration of task polls, in nanoseconds. + /// + /// This is an exponentially weighted moving average. Currently, this metric + /// is only provided by the multi-threaded runtime. + /// + /// # 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 identify 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_mean_poll_time(0); + /// println!("worker 0 has a mean poll time of {:?}", n); + /// } + /// ``` + #[track_caller] + pub fn worker_mean_poll_time(&self, worker: usize) -> Duration { + let nanos = self + .handle + .inner + .worker_metrics(worker) + .mean_poll_time + .load(Relaxed); + Duration::from_nanos(nanos) + } } /// Returns the number of tasks currently scheduled in the blocking @@ -837,88 +843,90 @@ impl RuntimeMetrics { cfg_net! { impl RuntimeMetrics { - /// Returns the number of file descriptors that have been registered with the - /// runtime's I/O driver. - /// - /// # Examples - /// - /// ``` - /// use tokio::runtime::Handle; - /// - /// #[tokio::main] - /// async fn main() { - /// let metrics = Handle::current().metrics(); - /// - /// let registered_fds = metrics.io_driver_fd_registered_count(); - /// println!("{} fds have been registered with the runtime's I/O driver.", registered_fds); - /// - /// let deregistered_fds = metrics.io_driver_fd_deregistered_count(); - /// - /// let current_fd_count = registered_fds - deregistered_fds; - /// println!("{} fds are currently registered by the runtime's I/O driver.", current_fd_count); - /// } - /// ``` - pub fn io_driver_fd_registered_count(&self) -> u64 { - self.with_io_driver_metrics(|m| { - m.fd_registered_count.load(Relaxed) - }) - } + cfg_64bit_metrics! { + /// Returns the number of file descriptors that have been registered with the + /// runtime's I/O driver. + /// + /// # Examples + /// + /// ``` + /// use tokio::runtime::Handle; + /// + /// #[tokio::main] + /// async fn main() { + /// let metrics = Handle::current().metrics(); + /// + /// let registered_fds = metrics.io_driver_fd_registered_count(); + /// println!("{} fds have been registered with the runtime's I/O driver.", registered_fds); + /// + /// let deregistered_fds = metrics.io_driver_fd_deregistered_count(); + /// + /// let current_fd_count = registered_fds - deregistered_fds; + /// println!("{} fds are currently registered by the runtime's I/O driver.", current_fd_count); + /// } + /// ``` + pub fn io_driver_fd_registered_count(&self) -> u64 { + self.with_io_driver_metrics(|m| { + m.fd_registered_count.load(Relaxed) + }) + } - /// Returns the number of file descriptors that have been deregistered by the - /// runtime's I/O driver. - /// - /// # Examples - /// - /// ``` - /// use tokio::runtime::Handle; - /// - /// #[tokio::main] - /// async fn main() { - /// let metrics = Handle::current().metrics(); - /// - /// let n = metrics.io_driver_fd_deregistered_count(); - /// println!("{} fds have been deregistered by the runtime's I/O driver.", n); - /// } - /// ``` - pub fn io_driver_fd_deregistered_count(&self) -> u64 { - self.with_io_driver_metrics(|m| { - m.fd_deregistered_count.load(Relaxed) - }) - } + /// Returns the number of file descriptors that have been deregistered by the + /// runtime's I/O driver. + /// + /// # Examples + /// + /// ``` + /// use tokio::runtime::Handle; + /// + /// #[tokio::main] + /// async fn main() { + /// let metrics = Handle::current().metrics(); + /// + /// let n = metrics.io_driver_fd_deregistered_count(); + /// println!("{} fds have been deregistered by the runtime's I/O driver.", n); + /// } + /// ``` + pub fn io_driver_fd_deregistered_count(&self) -> u64 { + self.with_io_driver_metrics(|m| { + m.fd_deregistered_count.load(Relaxed) + }) + } - /// Returns the number of ready events processed by the runtime's - /// I/O driver. - /// - /// # Examples - /// - /// ``` - /// use tokio::runtime::Handle; - /// - /// #[tokio::main] - /// async fn main() { - /// let metrics = Handle::current().metrics(); - /// - /// let n = metrics.io_driver_ready_count(); - /// println!("{} ready events processed by the runtime's I/O driver.", n); - /// } - /// ``` - pub fn io_driver_ready_count(&self) -> u64 { - self.with_io_driver_metrics(|m| m.ready_count.load(Relaxed)) - } + /// Returns the number of ready events processed by the runtime's + /// I/O driver. + /// + /// # Examples + /// + /// ``` + /// use tokio::runtime::Handle; + /// + /// #[tokio::main] + /// async fn main() { + /// let metrics = Handle::current().metrics(); + /// + /// let n = metrics.io_driver_ready_count(); + /// println!("{} ready events processed by the runtime's I/O driver.", n); + /// } + /// ``` + pub fn io_driver_ready_count(&self) -> u64 { + self.with_io_driver_metrics(|m| m.ready_count.load(Relaxed)) + } - fn with_io_driver_metrics(&self, f: F) -> u64 - where - F: Fn(&super::IoDriverMetrics) -> u64, - { - // TODO: Investigate if this should return 0, most of our metrics always increase - // thus this breaks that guarantee. - self.handle - .inner - .driver() - .io - .as_ref() - .map(|h| f(&h.metrics)) - .unwrap_or(0) + fn with_io_driver_metrics(&self, f: F) -> u64 + where + F: Fn(&super::IoDriverMetrics) -> u64, + { + // TODO: Investigate if this should return 0, most of our metrics always increase + // thus this breaks that guarantee. + self.handle + .inner + .driver() + .io + .as_ref() + .map(|h| f(&h.metrics)) + .unwrap_or(0) + } } } } diff --git a/tokio/src/runtime/metrics/scheduler.rs b/tokio/src/runtime/metrics/scheduler.rs index d9f8edfaabc..8b64eced38c 100644 --- a/tokio/src/runtime/metrics/scheduler.rs +++ b/tokio/src/runtime/metrics/scheduler.rs @@ -1,4 +1,5 @@ -use crate::loom::sync::atomic::{AtomicU64, Ordering::Relaxed}; +use crate::loom::sync::atomic::Ordering::Relaxed; +use crate::util::metric_atomics::MetricAtomicU64; /// Retrieves metrics from the Tokio runtime. /// @@ -10,25 +11,25 @@ use crate::loom::sync::atomic::{AtomicU64, Ordering::Relaxed}; #[derive(Debug)] pub(crate) struct SchedulerMetrics { /// Number of tasks that are scheduled from outside the runtime. - pub(super) remote_schedule_count: AtomicU64, - pub(super) budget_forced_yield_count: AtomicU64, + pub(super) remote_schedule_count: MetricAtomicU64, + pub(super) budget_forced_yield_count: MetricAtomicU64, } impl SchedulerMetrics { pub(crate) fn new() -> SchedulerMetrics { SchedulerMetrics { - remote_schedule_count: AtomicU64::new(0), - budget_forced_yield_count: AtomicU64::new(0), + remote_schedule_count: MetricAtomicU64::new(0), + budget_forced_yield_count: MetricAtomicU64::new(0), } } /// Increment the number of tasks scheduled externally pub(crate) fn inc_remote_schedule_count(&self) { - self.remote_schedule_count.fetch_add(1, Relaxed); + self.remote_schedule_count.add(1, Relaxed); } /// Increment the number of tasks forced to yield due to budget exhaustion pub(crate) fn inc_budget_forced_yield_count(&self) { - self.budget_forced_yield_count.fetch_add(1, Relaxed); + self.budget_forced_yield_count.add(1, Relaxed); } } diff --git a/tokio/src/runtime/metrics/worker.rs b/tokio/src/runtime/metrics/worker.rs index cefe4d2abc6..fc7c4e6dfe4 100644 --- a/tokio/src/runtime/metrics/worker.rs +++ b/tokio/src/runtime/metrics/worker.rs @@ -1,7 +1,8 @@ +use crate::loom::sync::atomic::AtomicUsize; use crate::loom::sync::atomic::Ordering::Relaxed; -use crate::loom::sync::atomic::{AtomicU64, AtomicUsize}; use crate::runtime::metrics::Histogram; use crate::runtime::Config; +use crate::util::metric_atomics::MetricAtomicU64; /// Retrieve runtime worker metrics. /// @@ -14,31 +15,31 @@ use crate::runtime::Config; #[repr(align(128))] pub(crate) struct WorkerMetrics { /// Number of times the worker parked. - pub(crate) park_count: AtomicU64, + pub(crate) park_count: MetricAtomicU64, /// Number of times the worker woke then parked again without doing work. - pub(crate) noop_count: AtomicU64, + pub(crate) noop_count: MetricAtomicU64, /// Number of tasks the worker stole. - pub(crate) steal_count: AtomicU64, + pub(crate) steal_count: MetricAtomicU64, /// Number of times the worker stole - pub(crate) steal_operations: AtomicU64, + pub(crate) steal_operations: MetricAtomicU64, /// Number of tasks the worker polled. - pub(crate) poll_count: AtomicU64, + pub(crate) poll_count: MetricAtomicU64, /// EWMA task poll time, in nanoseconds. - pub(crate) mean_poll_time: AtomicU64, + pub(crate) mean_poll_time: MetricAtomicU64, /// Amount of time the worker spent doing work vs. parking. - pub(crate) busy_duration_total: AtomicU64, + pub(crate) busy_duration_total: MetricAtomicU64, /// Number of tasks scheduled for execution on the worker's local queue. - pub(crate) local_schedule_count: AtomicU64, + pub(crate) local_schedule_count: MetricAtomicU64, /// Number of tasks moved from the local queue to the global queue to free space. - pub(crate) overflow_count: AtomicU64, + pub(crate) overflow_count: MetricAtomicU64, /// Number of tasks currently in the local queue. Used only by the /// current-thread scheduler. @@ -60,15 +61,15 @@ impl WorkerMetrics { pub(crate) fn new() -> WorkerMetrics { WorkerMetrics { - park_count: AtomicU64::new(0), - noop_count: AtomicU64::new(0), - steal_count: AtomicU64::new(0), - steal_operations: AtomicU64::new(0), - poll_count: AtomicU64::new(0), - mean_poll_time: AtomicU64::new(0), - overflow_count: AtomicU64::new(0), - busy_duration_total: AtomicU64::new(0), - local_schedule_count: AtomicU64::new(0), + park_count: MetricAtomicU64::new(0), + noop_count: MetricAtomicU64::new(0), + steal_count: MetricAtomicU64::new(0), + steal_operations: MetricAtomicU64::new(0), + poll_count: MetricAtomicU64::new(0), + mean_poll_time: MetricAtomicU64::new(0), + overflow_count: MetricAtomicU64::new(0), + busy_duration_total: MetricAtomicU64::new(0), + local_schedule_count: MetricAtomicU64::new(0), queue_depth: AtomicUsize::new(0), poll_count_histogram: None, } diff --git a/tokio/src/runtime/tests/queue.rs b/tokio/src/runtime/tests/queue.rs index 5df92b7a291..55429b1b11b 100644 --- a/tokio/src/runtime/tests/queue.rs +++ b/tokio/src/runtime/tests/queue.rs @@ -7,18 +7,21 @@ use std::time::Duration; #[allow(unused)] macro_rules! assert_metrics { - ($stats:ident, $field:ident == $v:expr) => {{ - use crate::runtime::WorkerMetrics; - use std::sync::atomic::Ordering::Relaxed; + ($stats:ident, $field:ident == $v:expr) => { + #[cfg(target_has_atomic = "64")] + { + use crate::runtime::WorkerMetrics; + use std::sync::atomic::Ordering::Relaxed; - let worker = WorkerMetrics::new(); - $stats.submit(&worker); + let worker = WorkerMetrics::new(); + $stats.submit(&worker); - let expect = $v; - let actual = worker.$field.load(Relaxed); + let expect = $v; + let actual = worker.$field.load(Relaxed); - assert!(actual == expect, "expect = {}; actual = {}", expect, actual) - }}; + assert!(actual == expect, "expect = {}; actual = {}", expect, actual) + } + }; } fn new_stats() -> Stats { diff --git a/tokio/src/util/metric_atomics.rs b/tokio/src/util/metric_atomics.rs new file mode 100644 index 00000000000..3c080298ecf --- /dev/null +++ b/tokio/src/util/metric_atomics.rs @@ -0,0 +1,47 @@ +use std::sync::atomic::Ordering; + +cfg_64bit_metrics! { + use std::sync::atomic::AtomicU64; +} + +/// `AtomicU64` that is is a no-op on platforms without 64-bit atomics +/// +/// When used on platforms without 64-bit atomics, writes to this are no-ops. +/// The `load` method is only defined when 64-bit atomics are available. +#[derive(Debug, Default)] +pub(crate) struct MetricAtomicU64 { + #[cfg(target_has_atomic = "64")] + value: AtomicU64, +} + +// some of these are currently only used behind cfg_unstable +#[allow(dead_code)] +impl MetricAtomicU64 { + // Load is only defined when supported + cfg_64bit_metrics! { + pub(crate) fn load(&self, ordering: Ordering) -> u64 { + self.value.load(ordering) + } + } + + cfg_64bit_metrics! { + pub(crate) fn store(&self, val: u64, ordering: Ordering) { + self.value.store(val, ordering) + } + + pub(crate) fn new(value: u64) -> Self { + Self { value: AtomicU64::new(value) } + } + + pub(crate) fn add(&self, value: u64, ordering: Ordering) { + self.value.fetch_add(value, ordering); + } + } + + cfg_no_64bit_metrics! { + pub(crate) fn store(&self, _val: u64, _ordering: Ordering) { } + // on platforms without 64-bit atomics, fetch-add returns unit + pub(crate) fn add(&self, _value: u64, _ordering: Ordering) { } + pub(crate) fn new(_value: u64) -> Self { Self { } } + } +} diff --git a/tokio/src/util/mod.rs b/tokio/src/util/mod.rs index 7cf371195ff..3722b0bc2d4 100644 --- a/tokio/src/util/mod.rs +++ b/tokio/src/util/mod.rs @@ -5,6 +5,8 @@ cfg_io_driver! { #[cfg(feature = "rt")] pub(crate) mod atomic_cell; +pub(crate) mod metric_atomics; + #[cfg(any(feature = "rt", feature = "signal", feature = "process"))] pub(crate) mod once_cell; diff --git a/tokio/tests/rt_basic.rs b/tokio/tests/rt_basic.rs index a5204bd83f7..4c558c90e28 100644 --- a/tokio/tests/rt_basic.rs +++ b/tokio/tests/rt_basic.rs @@ -19,7 +19,7 @@ mod support { macro_rules! cfg_metrics { ($($t:tt)*) => { - #[cfg(tokio_unstable)] + #[cfg(all(tokio_unstable, target_has_atomic = "64"))] { $( $t )* } diff --git a/tokio/tests/rt_metrics.rs b/tokio/tests/rt_metrics.rs index 7f0c9ad8052..6a710a46ce6 100644 --- a/tokio/tests/rt_metrics.rs +++ b/tokio/tests/rt_metrics.rs @@ -1,6 +1,11 @@ #![allow(unknown_lints, unexpected_cfgs)] #![warn(rust_2018_idioms)] -#![cfg(all(feature = "full", tokio_unstable, not(target_os = "wasi")))] +#![cfg(all( + feature = "full", + tokio_unstable, + not(target_os = "wasi"), + target_has_atomic = "64" +))] use std::future::Future; use std::sync::{Arc, Barrier, Mutex}; diff --git a/tokio/tests/rt_threaded.rs b/tokio/tests/rt_threaded.rs index 26690550f93..a4742dd234e 100644 --- a/tokio/tests/rt_threaded.rs +++ b/tokio/tests/rt_threaded.rs @@ -18,7 +18,7 @@ use std::task::{Context, Poll, Waker}; macro_rules! cfg_metrics { ($($t:tt)*) => { - #[cfg(tokio_unstable)] + #[cfg(all(tokio_unstable, target_has_atomic = "64"))] { $( $t )* } diff --git a/tokio/tests/rt_threaded_alt.rs b/tokio/tests/rt_threaded_alt.rs index 9eed1fe78b6..33af45e68bb 100644 --- a/tokio/tests/rt_threaded_alt.rs +++ b/tokio/tests/rt_threaded_alt.rs @@ -19,7 +19,7 @@ use std::task::{Context, Poll, Waker}; macro_rules! cfg_metrics { ($($t:tt)*) => { - #[cfg(tokio_unstable)] + #[cfg(all(tokio_unstable, target_has_atomic = "64"))] { $( $t )* }