Skip to main content

tokio/runtime/
mod.rs

1//! The Tokio runtime.
2//!
3//! Unlike other Rust programs, asynchronous applications require runtime
4//! support. In particular, the following runtime services are necessary:
5//!
6//! * An **I/O event loop**, called the driver, which drives I/O resources and
7//!   dispatches I/O events to tasks that depend on them.
8//! * A **scheduler** to execute [tasks] that use these I/O resources.
9//! * A **timer** for scheduling work to run after a set period of time.
10//!
11//! Tokio's [`Runtime`] bundles all of these services as a single type, allowing
12//! them to be started, shut down, and configured together. However, often it is
13//! not required to configure a [`Runtime`] manually, and a user may just use the
14//! [`tokio::main`] attribute macro, which creates a [`Runtime`] under the hood.
15//!
16//! # Choose your runtime
17//!
18//! Here is the rules of thumb to choose the right runtime for your application.
19//!
20//! ```plaintext
21//!    +------------------------------------------------------+
22//!    | Do you want work-stealing or multi-thread scheduler? |
23//!    +------------------------------------------------------+
24//!                    | Yes              | No
25//!                    |                  |
26//!                    |                  |
27//!                    v                  |
28//!      +------------------------+       |
29//!      | Multi-threaded Runtime |       |
30//!      +------------------------+       |
31//!                                       |
32//!                                       V
33//!                      +--------------------------------+
34//!                      | Do you execute `!Send` Future? |
35//!                      +--------------------------------+
36//!                            | Yes                 | No
37//!                            |                     |
38//!                            V                     |
39//!                    +---------------+             |
40//!                    | Local Runtime |             |
41//!                    +---------------+             |
42//!                                                  |
43//!                                                  v
44//!                                      +------------------------+
45//!                                      | Current-thread Runtime |
46//!                                      +------------------------+
47//! ```
48//!
49//! The above decision tree is not exhaustive. there are other factors that
50//! may influence your decision.
51//!
52//! ## Bridging with sync code
53//!
54//! See <https://tokio.rs/tokio/topics/bridging> for details.
55//!
56//! ## NUMA awareness
57//!
58//! The tokio runtime is not NUMA (Non-Uniform Memory Access) aware.
59//! You may want to start multiple runtimes instead of a single runtime
60//! for better performance on NUMA systems.
61//!
62//! # Usage
63//!
64//! When no fine tuning is required, the [`tokio::main`] attribute macro can be
65//! used.
66//!
67//! ```no_run
68//! # #[cfg(not(target_family = "wasm"))]
69//! # {
70//! use tokio::net::TcpListener;
71//! use tokio::io::{AsyncReadExt, AsyncWriteExt};
72//!
73//! #[tokio::main]
74//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
75//!     let listener = TcpListener::bind("127.0.0.1:8080").await?;
76//!
77//!     loop {
78//!         let (mut socket, _) = listener.accept().await?;
79//!
80//!         tokio::spawn(async move {
81//!             let mut buf = [0; 1024];
82//!
83//!             // In a loop, read data from the socket and write the data back.
84//!             loop {
85//!                 let n = match socket.read(&mut buf).await {
86//!                     // socket closed
87//!                     Ok(0) => return,
88//!                     Ok(n) => n,
89//!                     Err(e) => {
90//!                         println!("failed to read from socket; err = {:?}", e);
91//!                         return;
92//!                     }
93//!                 };
94//!
95//!                 // Write the data back
96//!                 if let Err(e) = socket.write_all(&buf[0..n]).await {
97//!                     println!("failed to write to socket; err = {:?}", e);
98//!                     return;
99//!                 }
100//!             }
101//!         });
102//!     }
103//! }
104//! # }
105//! ```
106//!
107//! From within the context of the runtime, additional tasks are spawned using
108//! the [`tokio::spawn`] function. Futures spawned using this function will be
109//! executed on the same thread pool used by the [`Runtime`].
110//!
111//! A [`Runtime`] instance can also be used directly.
112//!
113//! ```no_run
114//! # #[cfg(not(target_family = "wasm"))]
115//! # {
116//! use tokio::net::TcpListener;
117//! use tokio::io::{AsyncReadExt, AsyncWriteExt};
118//! use tokio::runtime::Runtime;
119//!
120//! fn main() -> Result<(), Box<dyn std::error::Error>> {
121//!     // Create the runtime
122//!     let rt  = Runtime::new()?;
123//!
124//!     // Spawn the root task
125//!     rt.block_on(async {
126//!         let listener = TcpListener::bind("127.0.0.1:8080").await?;
127//!
128//!         loop {
129//!             let (mut socket, _) = listener.accept().await?;
130//!
131//!             tokio::spawn(async move {
132//!                 let mut buf = [0; 1024];
133//!
134//!                 // In a loop, read data from the socket and write the data back.
135//!                 loop {
136//!                     let n = match socket.read(&mut buf).await {
137//!                         // socket closed
138//!                         Ok(0) => return,
139//!                         Ok(n) => n,
140//!                         Err(e) => {
141//!                             println!("failed to read from socket; err = {:?}", e);
142//!                             return;
143//!                         }
144//!                     };
145//!
146//!                     // Write the data back
147//!                     if let Err(e) = socket.write_all(&buf[0..n]).await {
148//!                         println!("failed to write to socket; err = {:?}", e);
149//!                         return;
150//!                     }
151//!                 }
152//!             });
153//!         }
154//!     })
155//! }
156//! # }
157//! ```
158//!
159//! ## Runtime Configurations
160//!
161//! Tokio provides multiple task scheduling strategies, suitable for different
162//! applications. The [runtime builder] or `#[tokio::main]` attribute may be
163//! used to select which scheduler to use.
164//!
165//! #### Multi-Thread Scheduler
166//!
167//! The multi-thread scheduler executes futures on a _thread pool_, using a
168//! work-stealing strategy. By default, it will start a worker thread for each
169//! CPU core available on the system. This tends to be the ideal configuration
170//! for most applications. The multi-thread scheduler requires the `rt-multi-thread`
171//! feature flag, and is selected by default:
172//! ```
173//! # #[cfg(not(target_family = "wasm"))]
174//! # {
175//! use tokio::runtime;
176//!
177//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
178//! let threaded_rt = runtime::Runtime::new()?;
179//! # Ok(()) }
180//! # }
181//! ```
182//!
183//! Most applications should use the multi-thread scheduler, except in some
184//! niche use-cases, such as when running only a single thread is required.
185//!
186//! #### Current-Thread Scheduler
187//!
188//! The current-thread scheduler provides a _single-threaded_ future executor.
189//! All tasks will be created and executed on the current thread. This requires
190//! the `rt` feature flag.
191//! ```
192//! use tokio::runtime;
193//!
194//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
195//! let rt = runtime::Builder::new_current_thread()
196//!     .build()?;
197//! # Ok(()) }
198//! ```
199//!
200//! #### Resource drivers
201//!
202//! When configuring a runtime by hand, no resource drivers are enabled by
203//! default. In this case, attempting to use networking types or time types will
204//! fail. In order to enable these types, the resource drivers must be enabled.
205//! This is done with [`Builder::enable_io`] and [`Builder::enable_time`]. As a
206//! shorthand, [`Builder::enable_all`] enables both resource drivers.
207//!
208//! ## Driving the runtime
209//!
210//! A Tokio runtime can only execute tasks if the runtime is running. Normally
211//! this is not an issue as the default configuration of a runtime is always running,
212//! but alternate configurations such as the current-thread runtime require that
213//! [`Runtime::block_on`] is called.
214//!
215//! - A multi-threaded runtime is always running because it spawns its own worker
216//!   threads.
217//! - A current-thread runtime does not spawn any worker threads, so it can only
218//!   execute tasks when you provide a thread by calling [`Runtime::block_on`].
219//! - A [`LocalSet`](crate::task::LocalSet) only executes local tasks spawned on
220//!   it when the `LocalSet` is `.awaited` or otherwise driven using one of its
221//!   methods for this purpose.
222//!
223//! Please be aware that [`Handle::block_on`] does not drive the runtime.
224//! There must be at least one call to [`Runtime::block_on`] when using the current
225//! thread runtime. [`Handle::block_on`] is not enough.
226//!
227//! ## Lifetime of spawned threads
228//!
229//! The runtime may spawn threads depending on its configuration and usage. The
230//! multi-thread scheduler spawns threads to schedule tasks and for `spawn_blocking`
231//! calls.
232//!
233//! While the `Runtime` is active, threads may shut down after periods of being
234//! idle. Once `Runtime` is dropped, all runtime threads have usually been
235//! terminated, but in the presence of unstoppable spawned work are not
236//! guaranteed to have been terminated. See the
237//! [struct level documentation](Runtime#shutdown) for more details.
238//!
239//! ## Unix `fork`
240//!
241//! User code that calls `fork(2)` without immediately calling `exec` must not
242//! reuse Tokio in the child process. Tokio supports this kind of fork only in
243//! two cases:
244//!
245//! - The fork happens before the parent process has used Tokio in any way.
246//! - The child process does not use Tokio after the fork.
247//!
248//! Creating or using a Tokio runtime in a child process after the parent has
249//! used Tokio is not supported, even if the runtime in the child is newly
250//! created. Some Tokio modules, including process and signal handling, use
251//! process-global state that cannot currently be reset after `fork`.
252//!
253//! [tasks]: crate::task
254//! [`Runtime`]: Runtime
255//! [`tokio::spawn`]: crate::spawn
256//! [`tokio::main`]: ../attr.main.html
257//! [runtime builder]: crate::runtime::Builder
258//! [`Runtime::new`]: crate::runtime::Runtime::new
259//! [`Builder::enable_io`]: crate::runtime::Builder::enable_io
260//! [`Builder::enable_time`]: crate::runtime::Builder::enable_time
261//! [`Builder::enable_all`]: crate::runtime::Builder::enable_all
262//!
263//! # Detailed runtime behavior
264//!
265//! This section gives more details into how the Tokio runtime will schedule
266//! tasks for execution.
267//!
268//! At its most basic level, a runtime has a collection of tasks that need to be
269//! scheduled. It will repeatedly remove a task from that collection and
270//! schedule it (by calling [`poll`]). When the collection is empty, the thread
271//! will go to sleep until a task is added to the collection.
272//!
273//! However, the above is not sufficient to guarantee a well-behaved runtime.
274//! For example, the runtime might have a single task that is always ready to be
275//! scheduled, and schedule that task every time. This is a problem because it
276//! starves other tasks by not scheduling them. To solve this, Tokio provides
277//! the following fairness guarantee:
278//!
279//! > If the total number of tasks does not grow without bound, and no task is
280//! > [blocking the thread], then it is guaranteed that tasks are scheduled
281//! > fairly.
282//!
283//! Or, more formally:
284//!
285//! > Under the following two assumptions:
286//! >
287//! > * There is some number `MAX_TASKS` such that the total number of tasks on
288//! >   the runtime at any specific point in time never exceeds `MAX_TASKS`.
289//! > * There is some number `MAX_SCHEDULE` such that calling [`poll`] on any
290//! >   task spawned on the runtime returns within `MAX_SCHEDULE` time units.
291//! >
292//! > Then, there is some number `MAX_DELAY` such that when a task is woken, it
293//! > will be scheduled by the runtime within `MAX_DELAY` time units.
294//!
295//! (Here, `MAX_TASKS` and `MAX_SCHEDULE` can be any number and the user of
296//! the runtime may choose them. The `MAX_DELAY` number is controlled by the
297//! runtime, and depends on the value of `MAX_TASKS` and `MAX_SCHEDULE`.)
298//!
299//! Other than the above fairness guarantee, there is no guarantee about the
300//! order in which tasks are scheduled. There is also no guarantee that the
301//! runtime is equally fair to all tasks. For example, if the runtime has two
302//! tasks A and B that are both ready, then the runtime may schedule A five
303//! times before it schedules B. This is the case even if A yields using
304//! [`yield_now`]. All that is guaranteed is that it will schedule B eventually.
305//!
306//! Normally, tasks are scheduled only if they have been woken by calling
307//! [`wake`] on their waker. However, this is not guaranteed, and Tokio may
308//! schedule tasks that have not been woken under some circumstances. This is
309//! called a spurious wakeup.
310//!
311//! ## IO and timers
312//!
313//! Beyond just scheduling tasks, the runtime must also manage IO resources and
314//! timers. It does this by periodically checking whether there are any IO
315//! resources or timers that are ready, and waking the relevant task so that
316//! it will be scheduled.
317//!
318//! These checks are performed periodically between scheduling tasks. Under the
319//! same assumptions as the previous fairness guarantee, Tokio guarantees that
320//! it will wake tasks with an IO or timer event within some maximum number of
321//! time units.
322//!
323//! ## Current thread runtime (behavior at the time of writing)
324//!
325//! This section describes how the [current thread runtime] behaves today. This
326//! behavior may change in future versions of Tokio.
327//!
328//! The current thread runtime maintains two FIFO queues of tasks that are ready
329//! to be scheduled: the global queue and the local queue. The runtime will prefer
330//! to choose the next task to schedule from the local queue, and will only pick a
331//! task from the global queue if the local queue is empty, or if it has picked
332//! a task from the local queue 31 times in a row. The number 31 can be
333//! changed using the [`global_queue_interval`] setting.
334//!
335//! The runtime will check for new IO or timer events whenever there are no
336//! tasks ready to be scheduled, or when it has scheduled 61 tasks in a row. The
337//! number 61 may be changed using the [`event_interval`] setting.
338//!
339//! When a task is woken from within a task running on the runtime, then the
340//! woken task is added directly to the local queue. Otherwise, the task is
341//! added to the global queue. The current thread runtime does not use [the lifo
342//! slot optimization].
343//!
344//! ## Multi threaded runtime (behavior at the time of writing)
345//!
346//! This section describes how the [multi thread runtime] behaves today. This
347//! behavior may change in future versions of Tokio.
348//!
349//! A multi thread runtime has a fixed number of worker threads, which are all
350//! created on startup. The multi thread runtime maintains one global queue, and
351//! a local queue for each worker thread. The local queue of a worker thread can
352//! fit at most 256 tasks. If more than 256 tasks are added to the local queue,
353//! then half of them are moved to the global queue to make space.
354//!
355//! The runtime will prefer to choose the next task to schedule from the local
356//! queue, and will only pick a task from the global queue if the local queue is
357//! empty, or if it has picked a task from the local queue
358//! [`global_queue_interval`] times in a row. If the value of
359//! [`global_queue_interval`] is not explicitly set using the runtime builder,
360//! then the runtime will dynamically compute it using a heuristic that targets
361//! 10ms intervals between each check of the global queue (based on the
362//! [`worker_mean_poll_time`] metric).
363//!
364//! If both the local queue and global queue is empty, then the worker thread
365//! will attempt to steal tasks from the local queue of another worker thread.
366//! Stealing is done by moving half of the tasks in one local queue to another
367//! local queue.
368//!
369//! The runtime will check for new IO or timer events whenever there are no
370//! tasks ready to be scheduled, or when it has scheduled 61 tasks in a row. The
371//! number 61 may be changed using the [`event_interval`] setting.
372//!
373//! The multi thread runtime uses [the lifo slot optimization]: Whenever a task
374//! wakes up another task, the other task is added to the worker thread's lifo
375//! slot instead of being added to a queue. If there was already a task in the
376//! lifo slot when this happened, then the lifo slot is replaced, and the task
377//! that used to be in the lifo slot is placed in the thread's local queue.
378//! When the runtime finishes scheduling a task, it will schedule the task in
379//! the lifo slot immediately, if any. When the lifo slot is used, the [coop
380//! budget] is not reset. Furthermore, if a worker thread uses the lifo slot
381//! three times in a row, it is temporarily disabled until the worker thread has
382//! scheduled a task that didn't come from the lifo slot. The lifo slot can be
383//! disabled using the [`disable_lifo_slot`] setting. The lifo slot is separate
384//! from the local queue, so other worker threads cannot steal the task in the
385//! lifo slot.
386//!
387//! When a task is woken from a thread that is not a worker thread, then the
388//! task is placed in the global queue.
389//!
390//! # Performance tuning
391//!
392//! ## File descriptor table pre-warming
393//!
394//! On Linux, file descriptor table growth can stall worker threads. See the
395//! [`prewarm-fd-table`] example.
396//!
397//! [`poll`]: std::future::Future::poll
398//! [`wake`]: std::task::Waker::wake
399//! [`yield_now`]: crate::task::yield_now
400//! [blocking the thread]: https://ryhl.io/blog/async-what-is-blocking/
401//! [current thread runtime]: crate::runtime::Builder::new_current_thread
402//! [multi thread runtime]: crate::runtime::Builder::new_multi_thread
403//! [`global_queue_interval`]: crate::runtime::Builder::global_queue_interval
404//! [`event_interval`]: crate::runtime::Builder::event_interval
405//! [`disable_lifo_slot`]: crate::runtime::Builder::disable_lifo_slot
406//! [the lifo slot optimization]: crate::runtime::Builder::disable_lifo_slot
407//! [coop budget]: crate::task::coop#cooperative-scheduling
408//! [`worker_mean_poll_time`]: crate::runtime::RuntimeMetrics::worker_mean_poll_time
409//! [`prewarm-fd-table`]: https://github.com/tokio-rs/tokio/blob/master/examples/prewarm-fd-table.rs
410
411// At the top due to macros
412#[cfg(test)]
413#[cfg(not(target_family = "wasm"))]
414#[macro_use]
415mod tests;
416
417pub(crate) mod context;
418
419pub(crate) mod park;
420
421pub(crate) mod driver;
422
423pub(crate) mod scheduler;
424
425cfg_io_driver_impl! {
426    pub(crate) mod io;
427}
428
429cfg_process_driver! {
430    mod process;
431}
432
433#[allow(dead_code)]
434#[derive(Debug, Copy, Clone, PartialEq)]
435pub(crate) enum TimerFlavor {
436    Traditional,
437    #[cfg(all(tokio_unstable, feature = "rt-multi-thread"))]
438    Alternative,
439}
440
441cfg_time! {
442    pub(crate) mod time;
443
444    #[cfg(all(tokio_unstable, feature = "rt-multi-thread"))]
445    pub(crate) mod time_alt;
446
447    use crate::time::Instant;
448
449    use std::task::{Context, Poll};
450    use std::pin::Pin;
451
452    #[derive(Debug)]
453    pub(crate) enum Timer {
454        Traditional(time::TimerEntry),
455
456        #[cfg(all(tokio_unstable, feature = "rt-multi-thread"))]
457        Alternative(time_alt::Timer),
458    }
459
460    impl Timer {
461        #[cfg_attr(not(all(tokio_unstable, feature = "rt-multi-thread")), allow(unused_variables))]
462        #[track_caller]
463        pub(crate) fn new(handle: scheduler::Handle, deadline: Instant) -> Self {
464            match handle.timer_flavor() {
465                TimerFlavor::Traditional => {
466                    Timer::Traditional(time::TimerEntry::new(handle))
467                }
468                #[cfg(all(tokio_unstable, feature = "rt-multi-thread"))]
469                TimerFlavor::Alternative => {
470                    Timer::Alternative(time_alt::Timer::new(handle, deadline))
471                }
472            }
473        }
474
475        pub(crate) fn init(self: Pin<&mut Self>, deadline: Instant) {
476            // Safety: we never move the inner entries.
477            let this = unsafe { self.get_unchecked_mut() };
478            match this {
479                // Safety: we never move the inner entries.
480                Timer::Traditional(entry) => unsafe {
481                    Pin::new_unchecked(entry).init(deadline)
482                }
483                #[cfg(all(tokio_unstable, feature = "rt-multi-thread"))]
484                Timer::Alternative(_) => {},
485            }
486        }
487
488        pub(crate) fn is_elapsed(&self) -> bool {
489            match self {
490                Timer::Traditional(entry) => entry.is_elapsed(),
491                #[cfg(all(tokio_unstable, feature = "rt-multi-thread"))]
492                Timer::Alternative(entry) => entry.is_elapsed(),
493            }
494        }
495
496        #[cfg_attr(not(all(tokio_unstable, feature = "rt-multi-thread")), allow(unused_variables))]
497        pub(crate) fn reset(self: Pin<&mut Self>, handle: scheduler::Handle, deadline: Instant) {
498            // Safety: we never move the inner entries.
499            let this = unsafe { self.get_unchecked_mut() };
500            match this {
501                // Safety: we never move the inner entries.
502                Timer::Traditional(entry) => unsafe {
503                    Pin::new_unchecked(entry).reset(deadline)
504                }
505                // Safety: we never move the inner entries.
506                #[cfg(all(tokio_unstable, feature = "rt-multi-thread"))]
507                Timer::Alternative(entry) => unsafe {
508                    Pin::new_unchecked(entry).set(time_alt::Timer::new(handle, deadline))
509                },
510            }
511        }
512
513        pub(crate) fn poll_elapsed(
514            self: Pin<&mut Self>,
515            cx: &mut Context<'_>,
516        ) -> Poll<Result<(), crate::time::error::Error>> {
517            // Safety: we never move the inner entries.
518            let this = unsafe { self.get_unchecked_mut() };
519            match this {
520                // Safety: we never move the inner entries.
521                Timer::Traditional(entry) => unsafe {
522                    Pin::new_unchecked(entry).poll_elapsed(cx)
523                }
524                // Safety: we never move the inner entries.
525                #[cfg(all(tokio_unstable, feature = "rt-multi-thread"))]
526                Timer::Alternative(entry) => unsafe {
527                    Pin::new_unchecked(entry).poll_elapsed(cx).map(Ok)
528                }
529            }
530        }
531    }
532}
533
534cfg_signal_internal_and_unix! {
535    pub(crate) mod signal;
536}
537
538cfg_rt! {
539    pub(crate) mod task;
540
541    mod config;
542    use config::Config;
543
544    mod blocking;
545    #[cfg_attr(target_os = "wasi", allow(unused_imports))]
546    pub(crate) use blocking::spawn_blocking;
547
548    cfg_trace! {
549        pub(crate) use blocking::Mandatory;
550    }
551
552    cfg_fs! {
553        pub(crate) use blocking::spawn_mandatory_blocking;
554    }
555
556    mod builder;
557    pub use self::builder::Builder;
558    cfg_unstable! {
559        pub use self::builder::UnhandledPanic;
560        pub use crate::util::rand::RngSeed;
561
562        /// Returns the index of the current worker thread, if called from a
563        /// runtime worker thread.
564        ///
565        /// The returned value is a 0-based index matching the worker indices
566        /// used by [`RuntimeMetrics`] methods such as
567        /// [`worker_total_busy_duration`](RuntimeMetrics::worker_total_busy_duration).
568        ///
569        /// Returns `None` when called from outside a runtime worker thread
570        /// (for example, from a blocking thread or a non-Tokio thread). On the
571        /// multi-thread runtime, the thread that calls [`Runtime::block_on`] is
572        /// not a worker thread, so this also returns `None` there.
573        ///
574        /// For the current-thread runtime and [`LocalRuntime`], this always
575        /// returns `Some(0)` (including inside `block_on`, since the calling
576        /// thread *is* the worker thread).
577        ///
578        /// Note that the result may change across `.await` points, as the
579        /// task may be moved to a different worker thread by the scheduler.
580        ///
581        /// # Examples
582        ///
583        /// ```
584        /// # #[cfg(not(target_family = "wasm"))]
585        /// # {
586        /// #[tokio::main(flavor = "multi_thread", worker_threads = 4)]
587        /// async fn main() {
588        ///     let index = tokio::spawn(async {
589        ///         tokio::runtime::worker_index()
590        ///     }).await.unwrap();
591        ///     println!("Task ran on worker {:?}", index);
592        /// }
593        /// # }
594        /// ```
595        pub fn worker_index() -> Option<usize> {
596            context::worker_index()
597        }
598    }
599
600    cfg_taskdump! {
601        pub mod dump;
602        pub use dump::Dump;
603    }
604
605    mod task_hooks;
606    pub(crate) use task_hooks::{TaskHooks, TaskCallback};
607    cfg_unstable! {
608        pub use task_hooks::TaskMeta;
609    }
610    #[cfg(not(tokio_unstable))]
611    pub(crate) use task_hooks::TaskMeta;
612
613    mod handle;
614    pub use handle::{EnterGuard, Handle, TryCurrentError};
615
616    mod runtime;
617    pub use runtime::{Runtime, RuntimeFlavor, is_rt_shutdown_err};
618
619    mod local_runtime;
620    pub use local_runtime::{LocalRuntime, LocalOptions};
621
622    mod id;
623    pub use id::Id;
624
625
626    /// Boundary value to prevent stack overflow caused by a large-sized
627    /// Future being placed in the stack.
628    pub(crate) const BOX_FUTURE_THRESHOLD: usize = if cfg!(debug_assertions)  {
629        2048
630    } else {
631        16384
632    };
633
634    mod thread_id;
635    pub(crate) use thread_id::ThreadId;
636
637    pub(crate) mod metrics;
638    pub use metrics::RuntimeMetrics;
639
640    cfg_unstable_metrics! {
641        pub use metrics::{HistogramScale, HistogramConfiguration, LogHistogram, LogHistogramBuilder, InvalidHistogramConfiguration} ;
642
643        cfg_net! {
644            pub(crate) use metrics::IoDriverMetrics;
645        }
646    }
647
648    pub(crate) use metrics::{MetricsBatch, SchedulerMetrics, WorkerMetrics, HistogramBuilder};
649
650    /// After thread starts / before thread stops
651    type Callback = std::sync::Arc<dyn Fn() + Send + Sync>;
652}