]> git.feebdaed.xyz Git - 0xmirror/tokio.git/commitdiff
metrics: properly annotate required features for 64-bit-only metrics (#7449)
authorJess Izen <44884346+jlizen@users.noreply.github.com>
Wed, 9 Jul 2025 13:03:22 +0000 (06:03 -0700)
committerGitHub <noreply@github.com>
Wed, 9 Jul 2025 13:03:22 +0000 (15:03 +0200)
tokio/src/macros/cfg.rs
tokio/src/runtime/metrics/runtime.rs

index 10da9570870b8714f751590847eca49e000f0db4..7c1dcc612de94efc96794264a51fffcaad183555 100644 (file)
@@ -1,5 +1,26 @@
 #![allow(unused_macros)]
 
+/// Allows specifying arbitrary combinations of features and config flags,
+/// which are also propagated to `docsrs` config.
+///
+/// Each contained item will have the annotations applied
+///
+/// ## Example usage:
+/// ```no-compile
+/// feature! {
+/// #![any(
+///     feature = "process",
+///     feature = "sync",
+///     feature = "rt",
+///     tokio_unstable
+/// )]
+///     /// docs
+///     pub struct MyStruct {};
+///     /// docs
+///     pub struct AnotherStruct {};
+/// }
+/// ```
+///
 macro_rules! feature {
     (
         #![$meta:meta]
index 188425e7e1134f7609b6a97a743b09a483153a65..04738344648f3329bab9ca0971c2b698ed635215 100644 (file)
@@ -342,346 +342,353 @@ impl RuntimeMetrics {
                 .worker_metrics(worker)
                 .thread_id()
         }
+    }
 
-        cfg_64bit_metrics! {
-            /// Returns the number of tasks spawned in this runtime since it was created.
-            ///
-            /// This count starts at zero when the runtime is created and increases by one each time a task is spawned.
-            ///
-            /// 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.spawned_tasks_count();
-            ///     println!("Runtime has had {} tasks spawned", n);
-            /// }
-            /// ```
-            pub fn spawned_tasks_count(&self) -> u64 {
-                self.handle.inner.spawned_tasks_count()
-            }
+    feature! {
+        #![all(
+            tokio_unstable,
+            target_has_atomic = "64"
+        )]
+        /// Returns the number of tasks spawned in this runtime since it was created.
+        ///
+        /// This count starts at zero when the runtime is created and increases by one each time a task is spawned.
+        ///
+        /// 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.spawned_tasks_count();
+        ///     println!("Runtime has had {} tasks spawned", n);
+        /// }
+        /// ```
+        pub fn spawned_tasks_count(&self) -> u64 {
+            self.handle.inner.spawned_tasks_count()
+        }
 
-            /// 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 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 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 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)
         }
+    }
+
+    cfg_unstable_metrics! {
 
         /// Renamed to [`RuntimeMetrics::global_queue_depth`]
         #[deprecated = "Renamed to global_queue_depth"]
@@ -877,121 +884,127 @@ impl RuntimeMetrics {
         pub fn poll_count_histogram_bucket_range(&self, bucket: usize) -> Range<Duration> {
             self.poll_time_histogram_bucket_range(bucket)
         }
+    }
 
-        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_time_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_time_histogram()
-            ///         .build()
-            ///         .unwrap()
-            ///         .block_on(async {
-            ///             let metrics = Handle::current().metrics();
-            ///             let buckets = metrics.poll_time_histogram_num_buckets();
-            ///
-            ///             for worker in 0..metrics.num_workers() {
-            ///                 for i in 0..buckets {
-            ///                     let count = metrics.poll_time_histogram_bucket_count(worker, i);
-            ///                     println!("Poll count {}", count);
-            ///                 }
-            ///             }
-            ///         });
-            /// }
-            /// ```
-            ///
-            /// [`poll_time_histogram_bucket_range()`]: crate::runtime::RuntimeMetrics::poll_time_histogram_bucket_range
-            #[track_caller]
-            pub fn poll_time_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()
-            }
+    feature! {
+    #![all(
+        tokio_unstable,
+        target_has_atomic = "64"
+    )]
+        /// 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_time_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_time_histogram()
+        ///         .build()
+        ///         .unwrap()
+        ///         .block_on(async {
+        ///             let metrics = Handle::current().metrics();
+        ///             let buckets = metrics.poll_time_histogram_num_buckets();
+        ///
+        ///             for worker in 0..metrics.num_workers() {
+        ///                 for i in 0..buckets {
+        ///                     let count = metrics.poll_time_histogram_bucket_count(worker, i);
+        ///                     println!("Poll count {}", count);
+        ///                 }
+        ///             }
+        ///         });
+        /// }
+        /// ```
+        ///
+        /// [`poll_time_histogram_bucket_range()`]: crate::runtime::RuntimeMetrics::poll_time_histogram_bucket_range
+        #[track_caller]
+        pub fn poll_time_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()
+        }
 
-            #[doc(hidden)]
-            #[deprecated(note = "use `poll_time_histogram_bucket_count` instead")]
-            pub fn poll_count_histogram_bucket_count(&self, worker: usize, bucket: usize) -> u64 {
-                self.poll_time_histogram_bucket_count(worker, bucket)
-            }
+        #[doc(hidden)]
+        #[deprecated(note = "use `poll_time_histogram_bucket_count` instead")]
+        pub fn poll_count_histogram_bucket_count(&self, worker: usize, bucket: usize) -> u64 {
+            self.poll_time_histogram_bucket_count(worker, bucket)
+        }
 
-            /// 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)
         }
+    }
 
+    cfg_unstable_metrics! {
         /// Returns the number of tasks currently scheduled in the blocking
         /// thread pool, spawned using `spawn_blocking`.
         ///
@@ -1015,93 +1028,96 @@ impl RuntimeMetrics {
         pub fn blocking_queue_depth(&self) -> usize {
             self.handle.inner.blocking_queue_depth()
         }
+    }
 
-        cfg_net! {
-            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)
-                    })
-                }
+    feature! {
+        #![all(
+            tokio_unstable,
+            target_has_atomic = "64",
+            feature = "net"
+        )]
+            /// 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<F>(&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<F>(&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)
             }
-        }
     }
 }