tokio/runtime/builder.rs
1#![cfg_attr(loom, allow(unused_imports))]
2
3use crate::runtime::handle::Handle;
4use crate::runtime::{
5 blocking, driver, Callback, HistogramBuilder, Runtime, TaskCallback, TimerFlavor,
6};
7#[cfg(tokio_unstable)]
8use crate::runtime::{metrics::HistogramConfiguration, TaskMeta};
9
10use crate::runtime::{LocalOptions, LocalRuntime};
11use crate::util::rand::{RngSeed, RngSeedGenerator};
12
13use crate::runtime::blocking::BlockingPool;
14use crate::runtime::scheduler::CurrentThread;
15use std::fmt;
16use std::io;
17use std::thread::ThreadId;
18use std::time::Duration;
19
20/// Builds Tokio Runtime with custom configuration values.
21///
22/// Methods can be chained in order to set the configuration values. The
23/// Runtime is constructed by calling [`build`].
24///
25/// New instances of `Builder` are obtained via [`Builder::new_multi_thread`]
26/// or [`Builder::new_current_thread`].
27///
28/// See function level documentation for details on the various configuration
29/// settings.
30///
31/// [`build`]: method@Self::build
32/// [`Builder::new_multi_thread`]: method@Self::new_multi_thread
33/// [`Builder::new_current_thread`]: method@Self::new_current_thread
34///
35/// # Examples
36///
37/// ```
38/// # #[cfg(not(target_family = "wasm"))]
39/// # {
40/// use tokio::runtime::Builder;
41///
42/// fn main() {
43/// // build runtime
44/// let runtime = Builder::new_multi_thread()
45/// .worker_threads(4)
46/// .thread_name("my-custom-name")
47/// .thread_stack_size(3 * 1024 * 1024)
48/// .build()
49/// .unwrap();
50///
51/// // use runtime ...
52/// }
53/// # }
54/// ```
55pub struct Builder {
56 /// Runtime type
57 kind: Kind,
58
59 /// Name of the runtime.
60 name: Option<String>,
61
62 /// Whether or not to enable the I/O driver
63 enable_io: bool,
64 nevents: usize,
65 nevents_busy: Option<usize>,
66
67 /// Whether or not to enable the time driver
68 enable_time: bool,
69
70 /// Whether or not the clock should start paused.
71 start_paused: bool,
72
73 /// The number of worker threads, used by Runtime.
74 ///
75 /// Only used when not using the current-thread executor.
76 worker_threads: Option<usize>,
77
78 /// Cap on thread usage.
79 max_blocking_threads: usize,
80
81 /// Name fn used for threads spawned by the runtime.
82 pub(super) thread_name: ThreadNameFn,
83
84 /// Stack size used for threads spawned by the runtime.
85 pub(super) thread_stack_size: Option<usize>,
86
87 /// Callback to run after each thread starts.
88 pub(super) after_start: Option<Callback>,
89
90 /// To run before each worker thread stops
91 pub(super) before_stop: Option<Callback>,
92
93 /// To run before each worker thread is parked.
94 pub(super) before_park: Option<Callback>,
95
96 /// To run after each thread is unparked.
97 pub(super) after_unpark: Option<Callback>,
98
99 /// To run before each task is spawned.
100 pub(super) before_spawn: Option<TaskCallback>,
101
102 /// To run before each poll
103 #[cfg(tokio_unstable)]
104 pub(super) before_poll: Option<TaskCallback>,
105
106 /// To run after each poll
107 #[cfg(tokio_unstable)]
108 pub(super) after_poll: Option<TaskCallback>,
109
110 /// To run after each task is terminated.
111 pub(super) after_termination: Option<TaskCallback>,
112
113 /// Customizable keep alive timeout for `BlockingPool`
114 pub(super) keep_alive: Option<Duration>,
115
116 /// How many ticks before pulling a task from the global/remote queue?
117 ///
118 /// When `None`, the value is unspecified and behavior details are left to
119 /// the scheduler. Each scheduler flavor could choose to either pick its own
120 /// default value or use some other strategy to decide when to poll from the
121 /// global queue. For example, the multi-threaded scheduler uses a
122 /// self-tuning strategy based on mean task poll times.
123 pub(super) global_queue_interval: Option<u32>,
124
125 /// How many ticks before yielding to the driver for timer and I/O events?
126 pub(super) event_interval: u32,
127
128 /// When true, the multi-threade scheduler LIFO slot should not be used.
129 ///
130 /// This option should only be exposed as unstable.
131 pub(super) disable_lifo_slot: bool,
132
133 /// Specify a random number generator seed to provide deterministic results
134 pub(super) seed_generator: RngSeedGenerator,
135
136 /// When true, enables task poll count histogram instrumentation.
137 pub(super) metrics_poll_count_histogram_enable: bool,
138
139 /// Configures the task poll count histogram
140 pub(super) metrics_poll_count_histogram: HistogramBuilder,
141
142 /// When true, enables task schedule latency instrumentation.
143 pub(super) track_task_schedule_latency: bool,
144
145 /// When true, enables the task schedule latency histogram.
146 pub(super) metrics_schedule_latency_histogram_enabled: bool,
147
148 /// Configures the task schedule latency histogram.
149 pub(super) metrics_schedule_latency_histogram: HistogramBuilder,
150
151 #[cfg(tokio_unstable)]
152 pub(super) unhandled_panic: UnhandledPanic,
153
154 timer_flavor: TimerFlavor,
155
156 /// Whether or not to enable eager hand-off for the I/O and time drivers (in
157 /// `tokio_unstable`).
158 enable_eager_driver_handoff: bool,
159
160 /// When true, the blocking pool uses the sharded queue implementation.
161 pub(super) sharded_blocking_queue: bool,
162}
163
164cfg_unstable! {
165 /// How the runtime should respond to unhandled panics.
166 ///
167 /// Instances of `UnhandledPanic` are passed to `Builder::unhandled_panic`
168 /// to configure the runtime behavior when a spawned task panics.
169 ///
170 /// See [`Builder::unhandled_panic`] for more details.
171 #[derive(Debug, Clone)]
172 #[non_exhaustive]
173 pub enum UnhandledPanic {
174 /// The runtime should ignore panics on spawned tasks.
175 ///
176 /// The panic is forwarded to the task's [`JoinHandle`] and all spawned
177 /// tasks continue running normally.
178 ///
179 /// This is the default behavior.
180 ///
181 /// # Examples
182 ///
183 /// ```
184 /// # #[cfg(not(target_family = "wasm"))]
185 /// # {
186 /// use tokio::runtime::{self, UnhandledPanic};
187 ///
188 /// # pub fn main() {
189 /// let rt = runtime::Builder::new_current_thread()
190 /// .unhandled_panic(UnhandledPanic::Ignore)
191 /// .build()
192 /// .unwrap();
193 ///
194 /// let task1 = rt.spawn(async { panic!("boom"); });
195 /// let task2 = rt.spawn(async {
196 /// // This task completes normally
197 /// "done"
198 /// });
199 ///
200 /// rt.block_on(async {
201 /// // The panic on the first task is forwarded to the `JoinHandle`
202 /// assert!(task1.await.is_err());
203 ///
204 /// // The second task completes normally
205 /// assert!(task2.await.is_ok());
206 /// })
207 /// # }
208 /// # }
209 /// ```
210 ///
211 /// [`JoinHandle`]: struct@crate::task::JoinHandle
212 Ignore,
213
214 /// The runtime should immediately shutdown if a spawned task panics.
215 ///
216 /// The runtime will immediately shutdown even if the panicked task's
217 /// [`JoinHandle`] is still available. All further spawned tasks will be
218 /// immediately dropped and call to [`Runtime::block_on`] will panic.
219 ///
220 /// # Examples
221 ///
222 /// ```should_panic
223 /// use tokio::runtime::{self, UnhandledPanic};
224 ///
225 /// # pub fn main() {
226 /// let rt = runtime::Builder::new_current_thread()
227 /// .unhandled_panic(UnhandledPanic::ShutdownRuntime)
228 /// .build()
229 /// .unwrap();
230 ///
231 /// rt.spawn(async { panic!("boom"); });
232 /// rt.spawn(async {
233 /// // This task never completes.
234 /// });
235 ///
236 /// rt.block_on(async {
237 /// // Do some work
238 /// # loop { tokio::task::yield_now().await; }
239 /// })
240 /// # }
241 /// ```
242 ///
243 /// [`JoinHandle`]: struct@crate::task::JoinHandle
244 ShutdownRuntime,
245 }
246}
247
248pub(crate) type ThreadNameFn = std::sync::Arc<dyn Fn() -> String + Send + Sync + 'static>;
249
250/// The default for the `sharded_blocking_queue` option: enabled iff the
251/// `TOKIO_UNSTABLE_SHARDED_BLOCKING_QUEUE` environment variable is set to a
252/// value other than `0`.
253fn sharded_blocking_queue_default() -> bool {
254 match std::env::var_os("TOKIO_UNSTABLE_SHARDED_BLOCKING_QUEUE") {
255 Some(value) => !value.is_empty() && value != "0",
256 None => false,
257 }
258}
259
260#[derive(Clone, Copy)]
261pub(crate) enum Kind {
262 CurrentThread,
263 #[cfg(feature = "rt-multi-thread")]
264 MultiThread,
265}
266
267impl Builder {
268 /// Returns a new builder with the current thread scheduler selected.
269 ///
270 /// Configuration methods can be chained on the return value.
271 ///
272 /// To spawn non-`Send` tasks on the resulting runtime, combine it with a
273 /// [`LocalSet`], or call [`build_local`] to create a [`LocalRuntime`].
274 ///
275 /// [`LocalSet`]: crate::task::LocalSet
276 /// [`LocalRuntime`]: crate::runtime::LocalRuntime
277 /// [`build_local`]: crate::runtime::Builder::build_local
278 pub fn new_current_thread() -> Builder {
279 #[cfg(loom)]
280 const EVENT_INTERVAL: u32 = 4;
281 // The number `61` is fairly arbitrary. I believe this value was copied from golang.
282 #[cfg(not(loom))]
283 const EVENT_INTERVAL: u32 = 61;
284
285 Builder::new(Kind::CurrentThread, EVENT_INTERVAL)
286 }
287
288 /// Returns a new builder with the multi thread scheduler selected.
289 ///
290 /// Configuration methods can be chained on the return value.
291 #[cfg(feature = "rt-multi-thread")]
292 #[cfg_attr(docsrs, doc(cfg(feature = "rt-multi-thread")))]
293 pub fn new_multi_thread() -> Builder {
294 // The number `61` is fairly arbitrary. I believe this value was copied from golang.
295 Builder::new(Kind::MultiThread, 61)
296 }
297
298 /// Returns a new runtime builder initialized with default configuration
299 /// values.
300 ///
301 /// Configuration methods can be chained on the return value.
302 pub(crate) fn new(kind: Kind, event_interval: u32) -> Builder {
303 Builder {
304 kind,
305
306 // Default runtime name
307 name: None,
308
309 // I/O defaults to "off"
310 enable_io: false,
311 nevents: 1024,
312 nevents_busy: None,
313
314 // Time defaults to "off"
315 enable_time: false,
316
317 // The clock starts not-paused
318 start_paused: false,
319
320 // Read from environment variable first in multi-threaded mode.
321 // Default to lazy auto-detection (one thread per CPU core)
322 worker_threads: None,
323
324 max_blocking_threads: 512,
325
326 // Default thread name
327 thread_name: std::sync::Arc::new(|| "tokio-rt-worker".into()),
328
329 // Do not set a stack size by default
330 thread_stack_size: None,
331
332 // No worker thread callbacks
333 after_start: None,
334 before_stop: None,
335 before_park: None,
336 after_unpark: None,
337
338 before_spawn: None,
339 after_termination: None,
340
341 #[cfg(tokio_unstable)]
342 before_poll: None,
343 #[cfg(tokio_unstable)]
344 after_poll: None,
345
346 keep_alive: None,
347
348 // Defaults for these values depend on the scheduler kind, so we get them
349 // as parameters.
350 global_queue_interval: None,
351 event_interval,
352
353 seed_generator: RngSeedGenerator::new(RngSeed::new()),
354
355 #[cfg(tokio_unstable)]
356 unhandled_panic: UnhandledPanic::Ignore,
357
358 metrics_poll_count_histogram_enable: false,
359
360 metrics_poll_count_histogram: HistogramBuilder::default(),
361
362 track_task_schedule_latency: false,
363
364 metrics_schedule_latency_histogram_enabled: false,
365
366 metrics_schedule_latency_histogram: HistogramBuilder::default(),
367
368 disable_lifo_slot: false,
369
370 timer_flavor: TimerFlavor::Traditional,
371
372 // Eager driver handoff is disabled by default.
373 enable_eager_driver_handoff: false,
374
375 sharded_blocking_queue: sharded_blocking_queue_default(),
376 }
377 }
378
379 /// Enables both I/O and time drivers.
380 ///
381 /// Doing this is a shorthand for calling `enable_io` and `enable_time`
382 /// individually. If additional components are added to Tokio in the future,
383 /// `enable_all` will include these future components.
384 ///
385 /// # Examples
386 ///
387 /// ```
388 /// # #[cfg(not(target_family = "wasm"))]
389 /// # {
390 /// use tokio::runtime;
391 ///
392 /// let rt = runtime::Builder::new_multi_thread()
393 /// .enable_all()
394 /// .build()
395 /// .unwrap();
396 /// # }
397 /// ```
398 pub fn enable_all(&mut self) -> &mut Self {
399 #[cfg(any(
400 feature = "net",
401 all(unix, feature = "process"),
402 all(unix, feature = "signal")
403 ))]
404 self.enable_io();
405
406 #[cfg(all(
407 tokio_unstable,
408 feature = "io-uring",
409 feature = "rt",
410 feature = "fs",
411 target_os = "linux",
412 ))]
413 self.enable_io_uring();
414
415 #[cfg(feature = "time")]
416 self.enable_time();
417
418 self
419 }
420
421 /// Enables the alternative timer implementation, which is disabled by default.
422 ///
423 /// The alternative timer implementation is an unstable feature that may
424 /// provide better performance on multi-threaded runtimes with a large number
425 /// of worker threads.
426 ///
427 /// This option only applies to multi-threaded runtimes. Attempting to use
428 /// this option with any other runtime type will have no effect.
429 ///
430 /// [Click here to share your experience with the alternative timer](https://github.com/tokio-rs/tokio/issues/7745)
431 ///
432 /// # Examples
433 ///
434 /// ```
435 /// # #[cfg(not(target_family = "wasm"))]
436 /// # {
437 /// use tokio::runtime;
438 ///
439 /// let rt = runtime::Builder::new_multi_thread()
440 /// .enable_alt_timer()
441 /// .build()
442 /// .unwrap();
443 /// # }
444 /// ```
445 #[cfg(all(tokio_unstable, feature = "time", feature = "rt-multi-thread"))]
446 #[cfg_attr(
447 docsrs,
448 doc(cfg(all(tokio_unstable, feature = "time", feature = "rt-multi-thread")))
449 )]
450 pub fn enable_alt_timer(&mut self) -> &mut Self {
451 self.enable_time();
452 self.timer_flavor = TimerFlavor::Alternative;
453 self
454 }
455
456 /// Enable eager hand-off of the I/O and time drivers for multi-threaded
457 /// runtimes, which is disabled by default.
458 ///
459 /// When this option is enabled, a worker thread which has parked on the I/O
460 /// or time driver will notify another worker thread once it is preparing to
461 /// begin polling a task from the run queue, so that the notified worker can
462 /// begin polling the I/O or time driver. This can reduce the latency with
463 /// which I/O and timer notifications are processed, especially when some
464 /// tasks have polls that take a long time to complete. In addition, it can
465 /// reduce the risk of a deadlock which may occur when a task blocks the
466 /// worker thread which is holding the I/O or time driver until some other
467 /// task, which is waiting for a notification from *that* driver, unblocks
468 /// it.
469 ///
470 /// This option is disabled by default, as enabling it may potentially
471 /// increase contention due to extra synchronization in cross-driver
472 /// wakeups.
473 ///
474 /// This option only applies to multi-threaded runtimes. Attempting to use
475 /// this option with any other runtime type will have no effect.
476 ///
477 /// **Note**: This is an [unstable API][unstable]. Eager driver hand-off is
478 /// an experimental feature whose behavior may be removed or changed in 1.x
479 /// releases. See [the documentation on unstable features][unstable] for
480 /// details.
481 ///
482 /// [unstable]: crate#unstable-features
483 #[cfg(all(tokio_unstable, feature = "rt-multi-thread"))]
484 #[cfg_attr(docsrs, doc(cfg(all(tokio_unstable, feature = "rt-multi-thread"))))]
485 pub fn enable_eager_driver_handoff(&mut self) -> &mut Self {
486 self.enable_eager_driver_handoff = true;
487 self
488 }
489
490 /// Enables the sharded `spawn_blocking` queue, which is disabled by
491 /// default.
492 ///
493 /// By default, the blocking pool's task queue is protected by a single
494 /// mutex, which can become a point of contention when many threads spawn
495 /// blocking tasks concurrently. When this option is enabled, tasks are
496 /// instead distributed across several independently-locked queue shards.
497 ///
498 /// The sharded queue can also be enabled by setting the
499 /// `TOKIO_UNSTABLE_SHARDED_BLOCKING_QUEUE` environment variable to any
500 /// value other than `0`.
501 ///
502 /// [Click here to share your experience with the sharded queue](https://github.com/tokio-rs/tokio/issues/8067)
503 ///
504 /// **Note**: This is an [unstable API][unstable]. The sharded
505 /// `spawn_blocking` queue is an experimental feature that may be removed
506 /// or become the default behavior in 1.x releases. See
507 /// [the documentation on unstable features][unstable] for details.
508 ///
509 /// # Examples
510 ///
511 /// ```
512 /// # #[cfg(not(target_family = "wasm"))]
513 /// # {
514 /// use tokio::runtime;
515 ///
516 /// let rt = runtime::Builder::new_multi_thread()
517 /// .enable_sharded_blocking_queue()
518 /// .build()
519 /// .unwrap();
520 /// # }
521 /// ```
522 ///
523 /// [unstable]: crate#unstable-features
524 #[cfg(tokio_unstable)]
525 #[cfg_attr(docsrs, doc(cfg(tokio_unstable)))]
526 pub fn enable_sharded_blocking_queue(&mut self) -> &mut Self {
527 self.sharded_blocking_queue = true;
528 self
529 }
530
531 /// Sets the number of worker threads the `Runtime` will use.
532 ///
533 /// This can be any number above 0 though it is advised to keep this value
534 /// on the smaller side.
535 ///
536 /// This will override the value read from environment variable `TOKIO_WORKER_THREADS`.
537 ///
538 /// # Default
539 ///
540 /// The default value is the number of cores available to the system.
541 ///
542 /// Currently, this is determined by calling
543 /// [`std::thread::available_parallelism`]. Please see its documentation
544 /// for caveats.
545 ///
546 /// When using the `current_thread` runtime this method has no effect.
547 ///
548 /// # Examples
549 ///
550 /// ## Multi threaded runtime with 4 threads
551 ///
552 /// ```
553 /// # #[cfg(not(target_family = "wasm"))]
554 /// # {
555 /// use tokio::runtime;
556 ///
557 /// // This will spawn a work-stealing runtime with 4 worker threads.
558 /// let rt = runtime::Builder::new_multi_thread()
559 /// .worker_threads(4)
560 /// .build()
561 /// .unwrap();
562 ///
563 /// rt.spawn(async move {});
564 /// # }
565 /// ```
566 ///
567 /// ## Current thread runtime (will only run on the current thread via `Runtime::block_on`)
568 ///
569 /// ```
570 /// use tokio::runtime;
571 ///
572 /// // Create a runtime that _must_ be driven from a call
573 /// // to `Runtime::block_on`.
574 /// let rt = runtime::Builder::new_current_thread()
575 /// .build()
576 /// .unwrap();
577 ///
578 /// // This will run the runtime and future on the current thread
579 /// rt.block_on(async move {});
580 /// ```
581 ///
582 /// # Panics
583 ///
584 /// This will panic if `val` is not larger than `0`.
585 #[track_caller]
586 pub fn worker_threads(&mut self, val: usize) -> &mut Self {
587 assert!(val > 0, "Worker threads cannot be set to 0");
588 self.worker_threads = Some(val);
589 self
590 }
591
592 /// Specifies the limit for additional threads spawned by the Runtime.
593 ///
594 /// These threads are used for blocking operations like tasks spawned
595 /// through [`spawn_blocking`], this includes but is not limited to:
596 /// - [`fs`] operations
597 /// - dns resolution through [`ToSocketAddrs`]
598 /// - writing to [`Stdout`] or [`Stderr`]
599 /// - reading from [`Stdin`]
600 ///
601 /// Unlike the [`worker_threads`], they are not always active and will exit
602 /// if left idle for too long. You can change this timeout duration with [`thread_keep_alive`].
603 ///
604 /// It's recommended to not set this limit too low in order to avoid hanging on operations
605 /// requiring [`spawn_blocking`].
606 ///
607 /// The default value is 512.
608 ///
609 /// # Queue Behavior
610 ///
611 /// When a blocking task is submitted, it will be inserted into a queue. If available, one of
612 /// the idle threads will be notified to run the task. Otherwise, if the threshold set by this
613 /// method has not been reached, a new thread will be spawned. If no idle thread is available
614 /// and no more threads are allowed to be spawned, the task will remain in the queue until one
615 /// of the busy threads pick it up. Note that since the queue does not apply any backpressure,
616 /// it could potentially grow unbounded.
617 ///
618 /// # Panics
619 ///
620 /// This will panic if `val` is not larger than `0`.
621 ///
622 /// # Upgrading from 0.x
623 ///
624 /// In old versions `max_threads` limited both blocking and worker threads, but the
625 /// current `max_blocking_threads` does not include async worker threads in the count.
626 ///
627 /// [`spawn_blocking`]: fn@crate::task::spawn_blocking
628 /// [`fs`]: mod@crate::fs
629 /// [`ToSocketAddrs`]: trait@crate::net::ToSocketAddrs
630 /// [`Stdout`]: struct@crate::io::Stdout
631 /// [`Stdin`]: struct@crate::io::Stdin
632 /// [`Stderr`]: struct@crate::io::Stderr
633 /// [`worker_threads`]: Self::worker_threads
634 /// [`thread_keep_alive`]: Self::thread_keep_alive
635 #[track_caller]
636 #[cfg_attr(docsrs, doc(alias = "max_threads"))]
637 pub fn max_blocking_threads(&mut self, val: usize) -> &mut Self {
638 assert!(val > 0, "Max blocking threads cannot be set to 0");
639 self.max_blocking_threads = val;
640 self
641 }
642
643 /// Sets name of threads spawned by the `Runtime`'s thread pool.
644 ///
645 /// The default name is "tokio-rt-worker".
646 ///
647 /// # Examples
648 ///
649 /// ```
650 /// # #[cfg(not(target_family = "wasm"))]
651 /// # {
652 /// # use tokio::runtime;
653 ///
654 /// # pub fn main() {
655 /// let rt = runtime::Builder::new_multi_thread()
656 /// .thread_name("my-pool")
657 /// .build();
658 /// # }
659 /// # }
660 /// ```
661 pub fn thread_name(&mut self, val: impl Into<String>) -> &mut Self {
662 let val = val.into();
663 self.thread_name = std::sync::Arc::new(move || val.clone());
664 self
665 }
666
667 /// Sets the name of the runtime.
668 ///
669 /// # Examples
670 ///
671 /// ```
672 /// # #[cfg(not(target_family = "wasm"))]
673 /// # {
674 /// # use tokio::runtime;
675 ///
676 /// # pub fn main() {
677 /// let rt = runtime::Builder::new_multi_thread()
678 /// .name("my-runtime")
679 /// .build();
680 /// # }
681 /// # }
682 /// ```
683 /// # Panics
684 ///
685 /// This function will panic if an empty value is passed as an argument.
686 ///
687 #[track_caller]
688 pub fn name(&mut self, val: impl Into<String>) -> &mut Self {
689 let val = val.into();
690 assert!(!val.trim().is_empty(), "runtime name shouldn't be empty");
691 self.name = Some(val);
692 self
693 }
694
695 /// Sets a function used to generate the name of threads spawned by the `Runtime`'s thread pool.
696 ///
697 /// The default name fn is `|| "tokio-rt-worker".into()`.
698 ///
699 /// # Examples
700 ///
701 /// ```
702 /// # #[cfg(not(target_family = "wasm"))]
703 /// # {
704 /// # use tokio::runtime;
705 /// # use std::sync::atomic::{AtomicUsize, Ordering};
706 /// # pub fn main() {
707 /// let rt = runtime::Builder::new_multi_thread()
708 /// .thread_name_fn(|| {
709 /// static ATOMIC_ID: AtomicUsize = AtomicUsize::new(0);
710 /// let id = ATOMIC_ID.fetch_add(1, Ordering::SeqCst);
711 /// format!("my-pool-{}", id)
712 /// })
713 /// .build();
714 /// # }
715 /// # }
716 /// ```
717 pub fn thread_name_fn<F>(&mut self, f: F) -> &mut Self
718 where
719 F: Fn() -> String + Send + Sync + 'static,
720 {
721 self.thread_name = std::sync::Arc::new(f);
722 self
723 }
724
725 /// Sets the stack size (in bytes) for worker threads.
726 ///
727 /// The actual stack size may be greater than this value if the platform
728 /// specifies minimal stack size.
729 ///
730 /// The default stack size for spawned threads is 2 MiB, though this
731 /// particular stack size is subject to change in the future.
732 ///
733 /// # Examples
734 ///
735 /// ```
736 /// # #[cfg(not(target_family = "wasm"))]
737 /// # {
738 /// # use tokio::runtime;
739 ///
740 /// # pub fn main() {
741 /// let rt = runtime::Builder::new_multi_thread()
742 /// .thread_stack_size(32 * 1024)
743 /// .build();
744 /// # }
745 /// # }
746 /// ```
747 pub fn thread_stack_size(&mut self, val: usize) -> &mut Self {
748 self.thread_stack_size = Some(val);
749 self
750 }
751
752 /// Executes function `f` after each thread is started but before it starts
753 /// doing work.
754 ///
755 /// This is intended for bookkeeping and monitoring use cases.
756 ///
757 /// # Examples
758 ///
759 /// ```
760 /// # #[cfg(not(target_family = "wasm"))]
761 /// # {
762 /// # use tokio::runtime;
763 /// # pub fn main() {
764 /// let runtime = runtime::Builder::new_multi_thread()
765 /// .on_thread_start(|| {
766 /// println!("thread started");
767 /// })
768 /// .build();
769 /// # }
770 /// # }
771 /// ```
772 #[cfg(not(loom))]
773 pub fn on_thread_start<F>(&mut self, f: F) -> &mut Self
774 where
775 F: Fn() + Send + Sync + 'static,
776 {
777 self.after_start = Some(std::sync::Arc::new(f));
778 self
779 }
780
781 /// Executes function `f` before each thread stops.
782 ///
783 /// This is intended for bookkeeping and monitoring use cases.
784 ///
785 /// # Examples
786 ///
787 /// ```
788 /// # #[cfg(not(target_family = "wasm"))]
789 /// # {
790 /// # use tokio::runtime;
791 /// # pub fn main() {
792 /// let runtime = runtime::Builder::new_multi_thread()
793 /// .on_thread_stop(|| {
794 /// println!("thread stopping");
795 /// })
796 /// .build();
797 /// # }
798 /// # }
799 /// ```
800 #[cfg(not(loom))]
801 pub fn on_thread_stop<F>(&mut self, f: F) -> &mut Self
802 where
803 F: Fn() + Send + Sync + 'static,
804 {
805 self.before_stop = Some(std::sync::Arc::new(f));
806 self
807 }
808
809 /// Executes function `f` just before a thread is parked (goes idle).
810 /// `f` is called within the Tokio context, so functions like [`tokio::spawn`](crate::spawn)
811 /// can be called, and may result in this thread being unparked immediately.
812 ///
813 /// This can be used to start work only when the executor is idle, or for bookkeeping
814 /// and monitoring purposes.
815 ///
816 /// Note: There can only be one park callback for a runtime; calling this function
817 /// more than once replaces the last callback defined, rather than adding to it.
818 ///
819 /// # Examples
820 ///
821 /// ## Multithreaded executor
822 /// ```
823 /// # #[cfg(not(target_family = "wasm"))]
824 /// # {
825 /// # use std::sync::Arc;
826 /// # use std::sync::atomic::{AtomicBool, Ordering};
827 /// # use tokio::runtime;
828 /// # use tokio::sync::Barrier;
829 /// # pub fn main() {
830 /// let once = AtomicBool::new(true);
831 /// let barrier = Arc::new(Barrier::new(2));
832 ///
833 /// let runtime = runtime::Builder::new_multi_thread()
834 /// .worker_threads(1)
835 /// .on_thread_park({
836 /// let barrier = barrier.clone();
837 /// move || {
838 /// let barrier = barrier.clone();
839 /// if once.swap(false, Ordering::Relaxed) {
840 /// tokio::spawn(async move { barrier.wait().await; });
841 /// }
842 /// }
843 /// })
844 /// .build()
845 /// .unwrap();
846 ///
847 /// runtime.block_on(async {
848 /// barrier.wait().await;
849 /// })
850 /// # }
851 /// # }
852 /// ```
853 /// ## Current thread executor
854 /// ```
855 /// # use std::sync::Arc;
856 /// # use std::sync::atomic::{AtomicBool, Ordering};
857 /// # use tokio::runtime;
858 /// # use tokio::sync::Barrier;
859 /// # pub fn main() {
860 /// let once = AtomicBool::new(true);
861 /// let barrier = Arc::new(Barrier::new(2));
862 ///
863 /// let runtime = runtime::Builder::new_current_thread()
864 /// .on_thread_park({
865 /// let barrier = barrier.clone();
866 /// move || {
867 /// let barrier = barrier.clone();
868 /// if once.swap(false, Ordering::Relaxed) {
869 /// tokio::spawn(async move { barrier.wait().await; });
870 /// }
871 /// }
872 /// })
873 /// .build()
874 /// .unwrap();
875 ///
876 /// runtime.block_on(async {
877 /// barrier.wait().await;
878 /// })
879 /// # }
880 /// ```
881 #[cfg(not(loom))]
882 pub fn on_thread_park<F>(&mut self, f: F) -> &mut Self
883 where
884 F: Fn() + Send + Sync + 'static,
885 {
886 self.before_park = Some(std::sync::Arc::new(f));
887 self
888 }
889
890 /// Executes function `f` just after a thread unparks (starts executing tasks).
891 ///
892 /// This is intended for bookkeeping and monitoring use cases; note that work
893 /// in this callback will increase latencies when the application has allowed one or
894 /// more runtime threads to go idle.
895 ///
896 /// Note: There can only be one unpark callback for a runtime; calling this function
897 /// more than once replaces the last callback defined, rather than adding to it.
898 ///
899 /// # Examples
900 ///
901 /// ```
902 /// # #[cfg(not(target_family = "wasm"))]
903 /// # {
904 /// # use tokio::runtime;
905 /// # pub fn main() {
906 /// let runtime = runtime::Builder::new_multi_thread()
907 /// .on_thread_unpark(|| {
908 /// println!("thread unparking");
909 /// })
910 /// .build();
911 ///
912 /// runtime.unwrap().block_on(async {
913 /// tokio::task::yield_now().await;
914 /// println!("Hello from Tokio!");
915 /// })
916 /// # }
917 /// # }
918 /// ```
919 #[cfg(not(loom))]
920 pub fn on_thread_unpark<F>(&mut self, f: F) -> &mut Self
921 where
922 F: Fn() + Send + Sync + 'static,
923 {
924 self.after_unpark = Some(std::sync::Arc::new(f));
925 self
926 }
927
928 /// Executes function `f` just before a task is spawned.
929 ///
930 /// `f` is called within the Tokio context, so functions like
931 /// [`tokio::spawn`](crate::spawn) can be called, and may result in this callback being
932 /// invoked immediately.
933 ///
934 /// This can be used for bookkeeping or monitoring purposes.
935 ///
936 /// Note: There can only be one spawn callback for a runtime; calling this function more
937 /// than once replaces the last callback defined, rather than adding to it.
938 ///
939 /// This *does not* support [`LocalSet`](crate::task::LocalSet) at this time.
940 ///
941 /// **Note**: This is an [unstable API][unstable]. The public API of this type
942 /// may break in 1.x releases. See [the documentation on unstable
943 /// features][unstable] for details.
944 ///
945 /// [unstable]: crate#unstable-features
946 ///
947 /// # Examples
948 ///
949 /// ```
950 /// # use tokio::runtime;
951 /// # pub fn main() {
952 /// let runtime = runtime::Builder::new_current_thread()
953 /// .on_task_spawn(|_| {
954 /// println!("spawning task");
955 /// })
956 /// .build()
957 /// .unwrap();
958 ///
959 /// runtime.block_on(async {
960 /// tokio::task::spawn(std::future::ready(()));
961 ///
962 /// for _ in 0..64 {
963 /// tokio::task::yield_now().await;
964 /// }
965 /// })
966 /// # }
967 /// ```
968 #[cfg(all(not(loom), tokio_unstable))]
969 #[cfg_attr(docsrs, doc(cfg(tokio_unstable)))]
970 pub fn on_task_spawn<F>(&mut self, f: F) -> &mut Self
971 where
972 F: Fn(&TaskMeta<'_>) + Send + Sync + 'static,
973 {
974 self.before_spawn = Some(std::sync::Arc::new(f));
975 self
976 }
977
978 /// Executes function `f` just before a task is polled
979 ///
980 /// `f` is called within the Tokio context, so functions like
981 /// [`tokio::spawn`](crate::spawn) can be called, and may result in this callback being
982 /// invoked immediately.
983 ///
984 /// When task schedule latency tracking is enabled, the latency is available
985 /// from `TaskMeta::schedule_latency`.
986 ///
987 /// **Note**: This is an [unstable API][unstable]. The public API of this type
988 /// may break in 1.x releases. See [the documentation on unstable
989 /// features][unstable] for details.
990 ///
991 /// [unstable]: crate#unstable-features
992 ///
993 /// # Examples
994 ///
995 /// ```
996 /// # #[cfg(not(target_family = "wasm"))]
997 /// # {
998 /// # use std::sync::{atomic::AtomicUsize, Arc};
999 /// # use tokio::task::yield_now;
1000 /// # pub fn main() {
1001 /// let poll_start_counter = Arc::new(AtomicUsize::new(0));
1002 /// let poll_start = poll_start_counter.clone();
1003 /// let rt = tokio::runtime::Builder::new_multi_thread()
1004 /// .enable_all()
1005 /// .on_before_task_poll(move |meta| {
1006 /// println!("task {} is about to be polled", meta.id())
1007 /// })
1008 /// .build()
1009 /// .unwrap();
1010 /// let task = rt.spawn(async {
1011 /// yield_now().await;
1012 /// });
1013 /// let _ = rt.block_on(task);
1014 ///
1015 /// # }
1016 /// # }
1017 /// ```
1018 #[cfg(tokio_unstable)]
1019 #[cfg_attr(docsrs, doc(cfg(tokio_unstable)))]
1020 pub fn on_before_task_poll<F>(&mut self, f: F) -> &mut Self
1021 where
1022 F: Fn(&TaskMeta<'_>) + Send + Sync + 'static,
1023 {
1024 self.before_poll = Some(std::sync::Arc::new(f));
1025 self
1026 }
1027
1028 /// Executes function `f` just after a task is polled
1029 ///
1030 /// `f` is called within the Tokio context, so functions like
1031 /// [`tokio::spawn`](crate::spawn) can be called, and may result in this callback being
1032 /// invoked immediately.
1033 ///
1034 /// When task schedule latency tracking is enabled, the latency is available
1035 /// from `TaskMeta::schedule_latency`.
1036 ///
1037 /// **Note**: This is an [unstable API][unstable]. The public API of this type
1038 /// may break in 1.x releases. See [the documentation on unstable
1039 /// features][unstable] for details.
1040 ///
1041 /// [unstable]: crate#unstable-features
1042 ///
1043 /// # Examples
1044 ///
1045 /// ```
1046 /// # #[cfg(not(target_family = "wasm"))]
1047 /// # {
1048 /// # use std::sync::{atomic::AtomicUsize, Arc};
1049 /// # use tokio::task::yield_now;
1050 /// # pub fn main() {
1051 /// let poll_stop_counter = Arc::new(AtomicUsize::new(0));
1052 /// let poll_stop = poll_stop_counter.clone();
1053 /// let rt = tokio::runtime::Builder::new_multi_thread()
1054 /// .enable_all()
1055 /// .on_after_task_poll(move |meta| {
1056 /// println!("task {} completed polling", meta.id());
1057 /// })
1058 /// .build()
1059 /// .unwrap();
1060 /// let task = rt.spawn(async {
1061 /// yield_now().await;
1062 /// });
1063 /// let _ = rt.block_on(task);
1064 ///
1065 /// # }
1066 /// # }
1067 /// ```
1068 #[cfg(tokio_unstable)]
1069 #[cfg_attr(docsrs, doc(cfg(tokio_unstable)))]
1070 pub fn on_after_task_poll<F>(&mut self, f: F) -> &mut Self
1071 where
1072 F: Fn(&TaskMeta<'_>) + Send + Sync + 'static,
1073 {
1074 self.after_poll = Some(std::sync::Arc::new(f));
1075 self
1076 }
1077
1078 /// Executes function `f` just after a task is terminated.
1079 ///
1080 /// `f` is called within the Tokio context, so functions like
1081 /// [`tokio::spawn`](crate::spawn) can be called.
1082 ///
1083 /// This can be used for bookkeeping or monitoring purposes.
1084 ///
1085 /// Note: There can only be one task termination callback for a runtime; calling this
1086 /// function more than once replaces the last callback defined, rather than adding to it.
1087 ///
1088 /// This *does not* support [`LocalSet`](crate::task::LocalSet) at this time.
1089 ///
1090 /// **Note**: This is an [unstable API][unstable]. The public API of this type
1091 /// may break in 1.x releases. See [the documentation on unstable
1092 /// features][unstable] for details.
1093 ///
1094 /// [unstable]: crate#unstable-features
1095 ///
1096 /// # Examples
1097 ///
1098 /// ```
1099 /// # use tokio::runtime;
1100 /// # pub fn main() {
1101 /// let runtime = runtime::Builder::new_current_thread()
1102 /// .on_task_terminate(|_| {
1103 /// println!("killing task");
1104 /// })
1105 /// .build()
1106 /// .unwrap();
1107 ///
1108 /// runtime.block_on(async {
1109 /// tokio::task::spawn(std::future::ready(()));
1110 ///
1111 /// for _ in 0..64 {
1112 /// tokio::task::yield_now().await;
1113 /// }
1114 /// })
1115 /// # }
1116 /// ```
1117 #[cfg(all(not(loom), tokio_unstable))]
1118 #[cfg_attr(docsrs, doc(cfg(tokio_unstable)))]
1119 pub fn on_task_terminate<F>(&mut self, f: F) -> &mut Self
1120 where
1121 F: Fn(&TaskMeta<'_>) + Send + Sync + 'static,
1122 {
1123 self.after_termination = Some(std::sync::Arc::new(f));
1124 self
1125 }
1126
1127 /// Creates the configured `Runtime`.
1128 ///
1129 /// The returned `Runtime` instance is ready to spawn tasks.
1130 ///
1131 /// # Examples
1132 ///
1133 /// ```
1134 /// # #[cfg(not(target_family = "wasm"))]
1135 /// # {
1136 /// use tokio::runtime::Builder;
1137 ///
1138 /// let rt = Builder::new_multi_thread().build().unwrap();
1139 ///
1140 /// rt.block_on(async {
1141 /// println!("Hello from the Tokio runtime");
1142 /// });
1143 /// # }
1144 /// ```
1145 ///
1146 /// # Errors
1147 ///
1148 /// Returns an error if the I/O driver or other OS resources required by the
1149 /// runtime cannot be initialized.
1150 pub fn build(&mut self) -> io::Result<Runtime> {
1151 match &self.kind {
1152 Kind::CurrentThread => self.build_current_thread_runtime(),
1153 #[cfg(feature = "rt-multi-thread")]
1154 Kind::MultiThread => self.build_threaded_runtime(),
1155 }
1156 }
1157
1158 /// Creates the configured [`LocalRuntime`].
1159 ///
1160 /// The returned [`LocalRuntime`] instance is ready to spawn tasks.
1161 ///
1162 /// # Panics
1163 ///
1164 /// This will panic if the runtime is configured with [`new_multi_thread()`].
1165 ///
1166 /// [`new_multi_thread()`]: Builder::new_multi_thread
1167 ///
1168 /// # Examples
1169 ///
1170 /// ```
1171 /// use tokio::runtime::{Builder, LocalOptions};
1172 ///
1173 /// let rt = Builder::new_current_thread()
1174 /// .build_local(LocalOptions::default())
1175 /// .unwrap();
1176 ///
1177 /// rt.spawn_local(async {
1178 /// println!("Hello from the Tokio runtime");
1179 /// });
1180 /// ```
1181 ///
1182 /// # Errors
1183 ///
1184 /// Returns an error if the I/O driver or other OS resources required by the
1185 /// runtime cannot be initialized.
1186 pub fn build_local(&mut self, _options: LocalOptions) -> io::Result<LocalRuntime> {
1187 match &self.kind {
1188 Kind::CurrentThread => self.build_current_thread_local_runtime(),
1189 #[cfg(feature = "rt-multi-thread")]
1190 Kind::MultiThread => panic!("multi_thread is not supported for LocalRuntime"),
1191 }
1192 }
1193
1194 fn get_cfg(&self) -> driver::Cfg {
1195 driver::Cfg {
1196 enable_pause_time: match self.kind {
1197 Kind::CurrentThread => true,
1198 #[cfg(feature = "rt-multi-thread")]
1199 Kind::MultiThread => false,
1200 },
1201 enable_io: self.enable_io,
1202 enable_time: self.enable_time,
1203 start_paused: self.start_paused,
1204 nevents: self.nevents,
1205 nevents_busy: self.nevents_busy,
1206 timer_flavor: self.timer_flavor,
1207 }
1208 }
1209
1210 /// Sets a custom timeout for a thread in the blocking pool.
1211 ///
1212 /// By default, the timeout for a thread is set to 10 seconds. This can
1213 /// be overridden using `.thread_keep_alive()`.
1214 ///
1215 /// # Example
1216 ///
1217 /// ```
1218 /// # #[cfg(not(target_family = "wasm"))]
1219 /// # {
1220 /// # use tokio::runtime;
1221 /// # use std::time::Duration;
1222 /// # pub fn main() {
1223 /// let rt = runtime::Builder::new_multi_thread()
1224 /// .thread_keep_alive(Duration::from_millis(100))
1225 /// .build();
1226 /// # }
1227 /// # }
1228 /// ```
1229 pub fn thread_keep_alive(&mut self, duration: Duration) -> &mut Self {
1230 self.keep_alive = Some(duration);
1231 self
1232 }
1233
1234 /// Sets the number of scheduler ticks after which the scheduler will poll the global
1235 /// task queue.
1236 ///
1237 /// A scheduler "tick" roughly corresponds to one `poll` invocation on a task.
1238 ///
1239 /// By default the global queue interval is 31 for the current-thread scheduler. Please see
1240 /// [the module documentation] for the default behavior of the multi-thread scheduler.
1241 ///
1242 /// Schedulers have a local queue of already-claimed tasks, and a global queue of incoming
1243 /// tasks. Setting the interval to a smaller value increases the fairness of the scheduler,
1244 /// at the cost of more synchronization overhead. That can be beneficial for prioritizing
1245 /// getting started on new work, especially if tasks frequently yield rather than complete
1246 /// or await on further I/O. Setting the interval to `1` will prioritize the global queue and
1247 /// tasks from the local queue will be executed only if the global queue is empty.
1248 /// Conversely, a higher value prioritizes existing work, and is a good choice when most
1249 /// tasks quickly complete polling.
1250 ///
1251 /// [the module documentation]: crate::runtime#multi-threaded-runtime-behavior-at-the-time-of-writing
1252 ///
1253 /// # Panics
1254 ///
1255 /// This function will panic if 0 is passed as an argument.
1256 ///
1257 /// # Examples
1258 ///
1259 /// ```
1260 /// # #[cfg(not(target_family = "wasm"))]
1261 /// # {
1262 /// # use tokio::runtime;
1263 /// # pub fn main() {
1264 /// let rt = runtime::Builder::new_multi_thread()
1265 /// .global_queue_interval(31)
1266 /// .build();
1267 /// # }
1268 /// # }
1269 /// ```
1270 #[track_caller]
1271 pub fn global_queue_interval(&mut self, val: u32) -> &mut Self {
1272 assert!(val > 0, "global_queue_interval must be greater than 0");
1273 self.global_queue_interval = Some(val);
1274 self
1275 }
1276
1277 /// Sets the number of scheduler ticks after which the scheduler will poll for
1278 /// external events (timers, I/O, and so on).
1279 ///
1280 /// A scheduler "tick" roughly corresponds to one `poll` invocation on a task.
1281 ///
1282 /// By default, the event interval is `61` for all scheduler types.
1283 ///
1284 /// Setting the event interval determines the effective "priority" of delivering
1285 /// these external events (which may wake up additional tasks), compared to
1286 /// executing tasks that are currently ready to run. A smaller value is useful
1287 /// when tasks frequently spend a long time in polling, or infrequently yield,
1288 /// which can result in overly long delays picking up I/O events. Conversely,
1289 /// picking up new events requires extra synchronization and syscall overhead,
1290 /// so if tasks generally complete their polling quickly, a higher event interval
1291 /// will minimize that overhead while still keeping the scheduler responsive to
1292 /// events.
1293 ///
1294 /// # Panics
1295 ///
1296 /// This function will panic if 0 is passed as an argument.
1297 ///
1298 /// # Examples
1299 ///
1300 /// ```
1301 /// # #[cfg(not(target_family = "wasm"))]
1302 /// # {
1303 /// # use tokio::runtime;
1304 /// # pub fn main() {
1305 /// let rt = runtime::Builder::new_multi_thread()
1306 /// .event_interval(31)
1307 /// .build();
1308 /// # }
1309 /// # }
1310 /// ```
1311 #[track_caller]
1312 pub fn event_interval(&mut self, val: u32) -> &mut Self {
1313 assert!(val > 0, "event_interval must be greater than 0");
1314 self.event_interval = val;
1315 self
1316 }
1317
1318 cfg_unstable! {
1319 /// Configure how the runtime responds to an unhandled panic on a
1320 /// spawned task.
1321 ///
1322 /// By default, an unhandled panic (i.e. a panic not caught by
1323 /// [`std::panic::catch_unwind`]) has no impact on the runtime's
1324 /// execution. The panic's error value is forwarded to the task's
1325 /// [`JoinHandle`] and all other spawned tasks continue running.
1326 ///
1327 /// The `unhandled_panic` option enables configuring this behavior.
1328 ///
1329 /// * `UnhandledPanic::Ignore` is the default behavior. Panics on
1330 /// spawned tasks have no impact on the runtime's execution.
1331 /// * `UnhandledPanic::ShutdownRuntime` will force the runtime to
1332 /// shutdown immediately when a spawned task panics even if that
1333 /// task's `JoinHandle` has not been dropped. All other spawned tasks
1334 /// will immediately terminate and further calls to
1335 /// [`Runtime::block_on`] will panic.
1336 ///
1337 /// # Panics
1338 /// This method panics if called with [`UnhandledPanic::ShutdownRuntime`]
1339 /// on a runtime other than the current thread runtime.
1340 ///
1341 /// # Unstable
1342 ///
1343 /// This option is currently unstable and its implementation is
1344 /// incomplete. The API may change or be removed in the future. See
1345 /// issue [tokio-rs/tokio#4516] for more details.
1346 ///
1347 /// # Examples
1348 ///
1349 /// The following demonstrates a runtime configured to shutdown on
1350 /// panic. The first spawned task panics and results in the runtime
1351 /// shutting down. The second spawned task never has a chance to
1352 /// execute. The call to `block_on` will panic due to the runtime being
1353 /// forcibly shutdown.
1354 ///
1355 /// ```should_panic
1356 /// use tokio::runtime::{self, UnhandledPanic};
1357 ///
1358 /// # pub fn main() {
1359 /// let rt = runtime::Builder::new_current_thread()
1360 /// .unhandled_panic(UnhandledPanic::ShutdownRuntime)
1361 /// .build()
1362 /// .unwrap();
1363 ///
1364 /// rt.spawn(async { panic!("boom"); });
1365 /// rt.spawn(async {
1366 /// // This task never completes.
1367 /// });
1368 ///
1369 /// rt.block_on(async {
1370 /// // Do some work
1371 /// # loop { tokio::task::yield_now().await; }
1372 /// })
1373 /// # }
1374 /// ```
1375 ///
1376 /// [`JoinHandle`]: struct@crate::task::JoinHandle
1377 /// [tokio-rs/tokio#4516]: https://github.com/tokio-rs/tokio/issues/4516
1378 pub fn unhandled_panic(&mut self, behavior: UnhandledPanic) -> &mut Self {
1379 if !matches!(self.kind, Kind::CurrentThread) && matches!(behavior, UnhandledPanic::ShutdownRuntime) {
1380 panic!("UnhandledPanic::ShutdownRuntime is only supported in current thread runtime");
1381 }
1382
1383 self.unhandled_panic = behavior;
1384 self
1385 }
1386
1387 /// Disables the LIFO task scheduler heuristic.
1388 ///
1389 /// The multi-threaded scheduler includes a heuristic for optimizing
1390 /// message-passing patterns. This heuristic results in the **last**
1391 /// scheduled task being polled first.
1392 ///
1393 /// To implement this heuristic, each worker thread has a slot which
1394 /// holds the task that should be polled next. However, this slot cannot
1395 /// be stolen by other worker threads, which can result in lower total
1396 /// throughput when tasks tend to have longer poll times.
1397 ///
1398 /// This configuration option will disable this heuristic resulting in
1399 /// all scheduled tasks being pushed into the worker-local queue, which
1400 /// is stealable.
1401 ///
1402 /// Consider trying this option when the task "scheduled" time is high
1403 /// but the runtime is underutilized. Use [tokio-rs/tokio-metrics] to
1404 /// collect this data.
1405 ///
1406 /// # Unstable
1407 ///
1408 /// This configuration option is considered a workaround for the LIFO
1409 /// slot not being stealable. When the slot becomes stealable, we will
1410 /// revisit whether or not this option is necessary. See
1411 /// issue [tokio-rs/tokio#4941].
1412 ///
1413 /// # Examples
1414 ///
1415 /// ```
1416 /// # #[cfg(not(target_family = "wasm"))]
1417 /// # {
1418 /// use tokio::runtime;
1419 ///
1420 /// let rt = runtime::Builder::new_multi_thread()
1421 /// .disable_lifo_slot()
1422 /// .build()
1423 /// .unwrap();
1424 /// # }
1425 /// ```
1426 ///
1427 /// [tokio-rs/tokio-metrics]: https://github.com/tokio-rs/tokio-metrics
1428 /// [tokio-rs/tokio#4941]: https://github.com/tokio-rs/tokio/issues/4941
1429 pub fn disable_lifo_slot(&mut self) -> &mut Self {
1430 self.disable_lifo_slot = true;
1431 self
1432 }
1433
1434 /// Specifies the random number generation seed to use within all
1435 /// threads associated with the runtime being built.
1436 ///
1437 /// This option is intended to make certain parts of the runtime
1438 /// deterministic (e.g. the [`tokio::select!`] macro). In the case of
1439 /// [`tokio::select!`] it will ensure that the order that branches are
1440 /// polled is deterministic.
1441 ///
1442 /// In addition to the code specifying `rng_seed` and interacting with
1443 /// the runtime, the internals of Tokio and the Rust compiler may affect
1444 /// the sequences of random numbers. In order to ensure repeatable
1445 /// results, the version of Tokio, the versions of all other
1446 /// dependencies that interact with Tokio, and the Rust compiler version
1447 /// should also all remain constant.
1448 ///
1449 /// # Examples
1450 ///
1451 /// ```
1452 /// # use tokio::runtime::{self, RngSeed};
1453 /// # pub fn main() {
1454 /// let seed = RngSeed::from_bytes(b"place your seed here");
1455 /// let rt = runtime::Builder::new_current_thread()
1456 /// .rng_seed(seed)
1457 /// .build();
1458 /// # }
1459 /// ```
1460 ///
1461 /// [`tokio::select!`]: crate::select
1462 pub fn rng_seed(&mut self, seed: RngSeed) -> &mut Self {
1463 self.seed_generator = RngSeedGenerator::new(seed);
1464 self
1465 }
1466 }
1467
1468 cfg_unstable_metrics! {
1469 /// Enables tracking the distribution of task poll times.
1470 ///
1471 /// Task poll times are not instrumented by default as doing so requires
1472 /// calling [`Instant::now()`] twice per task poll, which could add
1473 /// measurable overhead. Use the [`Handle::metrics()`] to access the
1474 /// metrics data.
1475 ///
1476 /// The histogram uses fixed bucket sizes. In other words, the histogram
1477 /// buckets are not dynamic based on input values. Use the
1478 /// `metrics_poll_time_histogram` builder methods to configure the
1479 /// histogram details.
1480 ///
1481 /// By default, a linear histogram with 10 buckets each 100 microseconds wide will be used.
1482 /// This has an extremely low memory footprint, but may not provide enough granularity. For
1483 /// better granularity with low memory usage, use [`metrics_poll_time_histogram_configuration()`]
1484 /// to select [`LogHistogram`] instead.
1485 ///
1486 /// # Examples
1487 ///
1488 /// ```
1489 /// # #[cfg(not(target_family = "wasm"))]
1490 /// # {
1491 /// use tokio::runtime;
1492 ///
1493 /// let rt = runtime::Builder::new_multi_thread()
1494 /// .enable_metrics_poll_time_histogram()
1495 /// .build()
1496 /// .unwrap();
1497 /// # // Test default values here
1498 /// # fn us(n: u64) -> std::time::Duration { std::time::Duration::from_micros(n) }
1499 /// # let m = rt.handle().metrics();
1500 /// # assert_eq!(m.poll_time_histogram_num_buckets(), 10);
1501 /// # assert_eq!(m.poll_time_histogram_bucket_range(0), us(0)..us(100));
1502 /// # assert_eq!(m.poll_time_histogram_bucket_range(1), us(100)..us(200));
1503 /// # }
1504 /// ```
1505 ///
1506 /// [`Handle::metrics()`]: crate::runtime::Handle::metrics
1507 /// [`Instant::now()`]: std::time::Instant::now
1508 /// [`LogHistogram`]: crate::runtime::LogHistogram
1509 /// [`metrics_poll_time_histogram_configuration()`]: Builder::metrics_poll_time_histogram_configuration
1510 pub fn enable_metrics_poll_time_histogram(&mut self) -> &mut Self {
1511 self.metrics_poll_count_histogram_enable = true;
1512 self
1513 }
1514
1515 /// Deprecated. Use [`enable_metrics_poll_time_histogram()`] instead.
1516 ///
1517 /// [`enable_metrics_poll_time_histogram()`]: Builder::enable_metrics_poll_time_histogram
1518 #[deprecated(note = "`poll_count_histogram` related methods have been renamed `poll_time_histogram` to better reflect their functionality.")]
1519 #[doc(hidden)]
1520 pub fn enable_metrics_poll_count_histogram(&mut self) -> &mut Self {
1521 self.enable_metrics_poll_time_histogram()
1522 }
1523
1524 /// Sets the histogram scale for tracking the distribution of task poll
1525 /// times.
1526 ///
1527 /// Tracking the distribution of task poll times can be done using a
1528 /// linear or log scale. When using linear scale, each histogram bucket
1529 /// will represent the same range of poll times. When using log scale,
1530 /// each histogram bucket will cover a range twice as big as the
1531 /// previous bucket.
1532 ///
1533 /// **Default:** linear scale.
1534 ///
1535 /// # Examples
1536 ///
1537 /// ```
1538 /// # #[cfg(not(target_family = "wasm"))]
1539 /// # {
1540 /// use tokio::runtime::{self, HistogramScale};
1541 ///
1542 /// # #[allow(deprecated)]
1543 /// let rt = runtime::Builder::new_multi_thread()
1544 /// .enable_metrics_poll_time_histogram()
1545 /// .metrics_poll_count_histogram_scale(HistogramScale::Log)
1546 /// .build()
1547 /// .unwrap();
1548 /// # }
1549 /// ```
1550 #[deprecated(note = "use `metrics_poll_time_histogram_configuration`")]
1551 pub fn metrics_poll_count_histogram_scale(&mut self, histogram_scale: crate::runtime::HistogramScale) -> &mut Self {
1552 self.metrics_poll_count_histogram.legacy_mut(|b|b.scale = histogram_scale);
1553 self
1554 }
1555
1556 /// Configure the histogram for tracking poll times
1557 ///
1558 /// By default, a linear histogram with 10 buckets each 100 microseconds wide will be used.
1559 /// This has an extremely low memory footprint, but may not provide enough granularity. For
1560 /// better granularity with low memory usage, use [`LogHistogram`] instead.
1561 ///
1562 /// # Examples
1563 /// Configure a [`LogHistogram`] with [default configuration]:
1564 /// ```
1565 /// # #[cfg(not(target_family = "wasm"))]
1566 /// # {
1567 /// use tokio::runtime;
1568 /// use tokio::runtime::{HistogramConfiguration, LogHistogram};
1569 ///
1570 /// let rt = runtime::Builder::new_multi_thread()
1571 /// .enable_metrics_poll_time_histogram()
1572 /// .metrics_poll_time_histogram_configuration(
1573 /// HistogramConfiguration::log(LogHistogram::default())
1574 /// )
1575 /// .build()
1576 /// .unwrap();
1577 /// # }
1578 /// ```
1579 ///
1580 /// Configure a linear histogram with 100 buckets, each 10μs wide
1581 /// ```
1582 /// # #[cfg(not(target_family = "wasm"))]
1583 /// # {
1584 /// use tokio::runtime;
1585 /// use std::time::Duration;
1586 /// use tokio::runtime::HistogramConfiguration;
1587 ///
1588 /// let rt = runtime::Builder::new_multi_thread()
1589 /// .enable_metrics_poll_time_histogram()
1590 /// .metrics_poll_time_histogram_configuration(
1591 /// HistogramConfiguration::linear(Duration::from_micros(10), 100)
1592 /// )
1593 /// .build()
1594 /// .unwrap();
1595 /// # }
1596 /// ```
1597 ///
1598 /// Configure a [`LogHistogram`] with the following settings:
1599 /// - Measure times from 100ns to 120s
1600 /// - Max error of 0.1
1601 /// - No more than 1024 buckets
1602 /// ```
1603 /// # #[cfg(not(target_family = "wasm"))]
1604 /// # {
1605 /// use std::time::Duration;
1606 /// use tokio::runtime;
1607 /// use tokio::runtime::{HistogramConfiguration, LogHistogram};
1608 ///
1609 /// let rt = runtime::Builder::new_multi_thread()
1610 /// .enable_metrics_poll_time_histogram()
1611 /// .metrics_poll_time_histogram_configuration(
1612 /// HistogramConfiguration::log(LogHistogram::builder()
1613 /// .max_value(Duration::from_secs(120))
1614 /// .min_value(Duration::from_nanos(100))
1615 /// .max_error(0.1)
1616 /// .max_buckets(1024)
1617 /// .expect("configuration uses 488 buckets")
1618 /// )
1619 /// )
1620 /// .build()
1621 /// .unwrap();
1622 /// # }
1623 /// ```
1624 ///
1625 /// When migrating from the legacy histogram ([`HistogramScale::Log`]) and wanting
1626 /// to match the previous behavior, use `precision_exact(0)`. This creates a histogram
1627 /// where each bucket is twice the size of the previous bucket.
1628 /// ```rust
1629 /// use std::time::Duration;
1630 /// use tokio::runtime::{HistogramConfiguration, LogHistogram};
1631 /// let rt = tokio::runtime::Builder::new_current_thread()
1632 /// .enable_all()
1633 /// .enable_metrics_poll_time_histogram()
1634 /// .metrics_poll_time_histogram_configuration(HistogramConfiguration::log(
1635 /// LogHistogram::builder()
1636 /// .min_value(Duration::from_micros(20))
1637 /// .max_value(Duration::from_millis(4))
1638 /// // Set `precision_exact` to `0` to match `HistogramScale::Log`
1639 /// .precision_exact(0)
1640 /// .max_buckets(10)
1641 /// .unwrap(),
1642 /// ))
1643 /// .build()
1644 /// .unwrap();
1645 /// ```
1646 ///
1647 /// [`LogHistogram`]: crate::runtime::LogHistogram
1648 /// [default configuration]: crate::runtime::LogHistogramBuilder
1649 /// [`HistogramScale::Log`]: crate::runtime::HistogramScale::Log
1650 pub fn metrics_poll_time_histogram_configuration(&mut self, configuration: HistogramConfiguration) -> &mut Self {
1651 self.metrics_poll_count_histogram.histogram_type = configuration.inner;
1652 self
1653 }
1654
1655 /// Sets the histogram resolution for tracking the distribution of task
1656 /// poll times.
1657 ///
1658 /// The resolution is the histogram's first bucket's range. When using a
1659 /// linear histogram scale, each bucket will cover the same range. When
1660 /// using a log scale, each bucket will cover a range twice as big as
1661 /// the previous bucket. In the log case, the resolution represents the
1662 /// smallest bucket range.
1663 ///
1664 /// Note that, when using log scale, the resolution is rounded up to the
1665 /// nearest power of 2 in nanoseconds.
1666 ///
1667 /// **Default:** 100 microseconds.
1668 ///
1669 /// # Examples
1670 ///
1671 /// ```
1672 /// # #[cfg(not(target_family = "wasm"))]
1673 /// # {
1674 /// use tokio::runtime;
1675 /// use std::time::Duration;
1676 ///
1677 /// # #[allow(deprecated)]
1678 /// let rt = runtime::Builder::new_multi_thread()
1679 /// .enable_metrics_poll_time_histogram()
1680 /// .metrics_poll_count_histogram_resolution(Duration::from_micros(100))
1681 /// .build()
1682 /// .unwrap();
1683 /// # }
1684 /// ```
1685 #[deprecated(note = "use `metrics_poll_time_histogram_configuration`")]
1686 pub fn metrics_poll_count_histogram_resolution(&mut self, resolution: Duration) -> &mut Self {
1687 assert!(resolution > Duration::from_secs(0));
1688 // Sanity check the argument and also make the cast below safe.
1689 assert!(resolution <= Duration::from_secs(1));
1690
1691 let resolution = resolution.as_nanos() as u64;
1692
1693 self.metrics_poll_count_histogram.legacy_mut(|b|b.resolution = resolution);
1694 self
1695 }
1696
1697 /// Sets the number of buckets for the histogram tracking the
1698 /// distribution of task poll times.
1699 ///
1700 /// The last bucket tracks all greater values that fall out of other
1701 /// ranges. So, configuring the histogram using a linear scale,
1702 /// resolution of 50ms, and 10 buckets, the 10th bucket will track task
1703 /// polls that take more than 450ms to complete.
1704 ///
1705 /// **Default:** 10
1706 ///
1707 /// # Examples
1708 ///
1709 /// ```
1710 /// # #[cfg(not(target_family = "wasm"))]
1711 /// # {
1712 /// use tokio::runtime;
1713 ///
1714 /// # #[allow(deprecated)]
1715 /// let rt = runtime::Builder::new_multi_thread()
1716 /// .enable_metrics_poll_time_histogram()
1717 /// .metrics_poll_count_histogram_buckets(15)
1718 /// .build()
1719 /// .unwrap();
1720 /// # }
1721 /// ```
1722 #[deprecated(note = "use `metrics_poll_time_histogram_configuration`")]
1723 pub fn metrics_poll_count_histogram_buckets(&mut self, buckets: usize) -> &mut Self {
1724 self.metrics_poll_count_histogram.legacy_mut(|b|b.num_buckets = buckets);
1725 self
1726 }
1727 }
1728
1729 fn build_current_thread_runtime(&mut self) -> io::Result<Runtime> {
1730 use crate::runtime::runtime::Scheduler;
1731
1732 let (scheduler, handle, blocking_pool) =
1733 self.build_current_thread_runtime_components(None)?;
1734
1735 Ok(Runtime::from_parts(
1736 Scheduler::CurrentThread(scheduler),
1737 handle,
1738 blocking_pool,
1739 ))
1740 }
1741
1742 fn build_current_thread_local_runtime(&mut self) -> io::Result<LocalRuntime> {
1743 use crate::runtime::local_runtime::LocalRuntimeScheduler;
1744
1745 let tid = std::thread::current().id();
1746
1747 let (scheduler, handle, blocking_pool) =
1748 self.build_current_thread_runtime_components(Some(tid))?;
1749
1750 Ok(LocalRuntime::from_parts(
1751 LocalRuntimeScheduler::CurrentThread(scheduler),
1752 handle,
1753 blocking_pool,
1754 ))
1755 }
1756
1757 fn build_current_thread_runtime_components(
1758 &mut self,
1759 local_tid: Option<ThreadId>,
1760 ) -> io::Result<(CurrentThread, Handle, BlockingPool)> {
1761 use crate::runtime::scheduler;
1762 use crate::runtime::Config;
1763
1764 let mut cfg = self.get_cfg();
1765 cfg.timer_flavor = TimerFlavor::Traditional;
1766 let (driver, driver_handle) = driver::Driver::new(cfg)?;
1767
1768 // Blocking pool
1769 let blocking_pool = blocking::create_blocking_pool(self, self.max_blocking_threads, 0);
1770 let blocking_spawner = blocking_pool.spawner().clone();
1771
1772 // Generate a rng seed for this runtime.
1773 let seed_generator_1 = self.seed_generator.next_generator();
1774 let seed_generator_2 = self.seed_generator.next_generator();
1775
1776 // And now put a single-threaded scheduler on top of the timer. When
1777 // there are no futures ready to do something, it'll let the timer or
1778 // the reactor to generate some new stimuli for the futures to continue
1779 // in their life.
1780 let (scheduler, handle) = CurrentThread::new(
1781 driver,
1782 driver_handle,
1783 blocking_spawner,
1784 seed_generator_2,
1785 Config {
1786 before_park: self.before_park.clone(),
1787 after_unpark: self.after_unpark.clone(),
1788 before_spawn: self.before_spawn.clone(),
1789 #[cfg(tokio_unstable)]
1790 before_poll: self.before_poll.clone(),
1791 #[cfg(tokio_unstable)]
1792 after_poll: self.after_poll.clone(),
1793 after_termination: self.after_termination.clone(),
1794 global_queue_interval: self.global_queue_interval,
1795 event_interval: self.event_interval,
1796 #[cfg(tokio_unstable)]
1797 unhandled_panic: self.unhandled_panic.clone(),
1798 disable_lifo_slot: self.disable_lifo_slot,
1799 // This setting never makes sense for a current thread runtime,
1800 // as it only configures how the I/O driver is stolen across
1801 // workers.
1802 enable_eager_driver_handoff: false,
1803 seed_generator: seed_generator_1,
1804 metrics_poll_count_histogram: self.metrics_poll_count_histogram_builder(),
1805 track_task_schedule_latency: self.track_task_schedule_latency,
1806 metrics_schedule_latency_histogram: self
1807 .metrics_schedule_latency_histogram_builder(),
1808 },
1809 local_tid,
1810 self.name.clone(),
1811 );
1812
1813 let handle = Handle {
1814 inner: scheduler::Handle::CurrentThread(handle),
1815 };
1816
1817 Ok((scheduler, handle, blocking_pool))
1818 }
1819
1820 fn metrics_poll_count_histogram_builder(&self) -> Option<HistogramBuilder> {
1821 if self.metrics_poll_count_histogram_enable {
1822 Some(self.metrics_poll_count_histogram.clone())
1823 } else {
1824 None
1825 }
1826 }
1827
1828 fn metrics_schedule_latency_histogram_builder(&self) -> Option<HistogramBuilder> {
1829 if self.metrics_schedule_latency_histogram_enabled {
1830 Some(self.metrics_schedule_latency_histogram.clone())
1831 } else {
1832 None
1833 }
1834 }
1835
1836 /// Enables the I/O driver.
1837 ///
1838 /// Doing this enables using net, process, signal, and some I/O types on
1839 /// the runtime.
1840 ///
1841 /// # Examples
1842 ///
1843 /// ```
1844 /// use tokio::runtime;
1845 ///
1846 /// let rt = runtime::Builder::new_current_thread()
1847 /// .enable_io()
1848 /// .build()
1849 /// .unwrap();
1850 /// ```
1851 pub fn enable_io(&mut self) -> &mut Self {
1852 self.enable_io = true;
1853 self
1854 }
1855
1856 /// Sets the max number of I/O events processed per tick.
1857 ///
1858 /// To take a smaller batch on polls that do not wait, see
1859 /// [`max_io_events_per_busy_tick`].
1860 ///
1861 /// [`max_io_events_per_busy_tick`]: Builder::max_io_events_per_busy_tick
1862 ///
1863 /// # Panics
1864 ///
1865 /// Panics if `capacity` is zero.
1866 ///
1867 /// # Examples
1868 ///
1869 /// ```
1870 /// use tokio::runtime;
1871 ///
1872 /// let rt = runtime::Builder::new_current_thread()
1873 /// .enable_io()
1874 /// .max_io_events_per_tick(1024)
1875 /// .build()
1876 /// .unwrap();
1877 /// ```
1878 #[track_caller]
1879 pub fn max_io_events_per_tick(&mut self, capacity: usize) -> &mut Self {
1880 assert!(capacity > 0, "max_io_events_per_tick must be non-zero");
1881 self.nevents = capacity;
1882 self
1883 }
1884
1885 /// Sets the max number of I/O events a worker processes when it polls
1886 /// the driver while it still has tasks to run.
1887 ///
1888 /// A busy worker polls the driver every [`event_interval`] tasks, and
1889 /// every task that poll wakes goes to its local queue. A large batch
1890 /// overflows that queue, and under sustained overload the overflow
1891 /// grows until requests time out. A small busy batch leaves the rest
1892 /// in the kernel. An idle worker still takes up to
1893 /// [`max_io_events_per_tick`] events.
1894 ///
1895 /// The runtime treats any poll that does not wait as busy. That
1896 /// includes a park with a timer that has already expired, because the
1897 /// worker runs that timer's task next.
1898 ///
1899 /// A multi-thread worker's local queue holds 256 tasks, so set
1900 /// `max_io_events_per_tick` to at most 256 as well, with room for the
1901 /// tasks those tasks wake.
1902 ///
1903 /// The default is to use the same value as [`max_io_events_per_tick`].
1904 ///
1905 /// [`event_interval`]: Builder::event_interval
1906 /// [`max_io_events_per_tick`]: Builder::max_io_events_per_tick
1907 ///
1908 /// # Panics
1909 ///
1910 /// Panics if `capacity` is zero.
1911 ///
1912 /// # Examples
1913 ///
1914 /// ```
1915 /// use tokio::runtime;
1916 ///
1917 /// let rt = runtime::Builder::new_current_thread()
1918 /// .enable_io()
1919 /// .max_io_events_per_tick(128)
1920 /// .max_io_events_per_busy_tick(8)
1921 /// .build()
1922 /// .unwrap();
1923 /// ```
1924 #[track_caller]
1925 pub fn max_io_events_per_busy_tick(&mut self, capacity: usize) -> &mut Self {
1926 assert!(capacity > 0, "max_io_events_per_busy_tick must be non-zero");
1927 self.nevents_busy = Some(capacity);
1928 self
1929 }
1930}
1931
1932cfg_time! {
1933 impl Builder {
1934 /// Enables the time driver.
1935 ///
1936 /// Doing this enables using `tokio::time` on the runtime.
1937 ///
1938 /// # Examples
1939 ///
1940 /// ```
1941 /// # #[cfg(not(target_family = "wasm"))]
1942 /// # {
1943 /// use tokio::runtime;
1944 ///
1945 /// let rt = runtime::Builder::new_multi_thread()
1946 /// .enable_time()
1947 /// .build()
1948 /// .unwrap();
1949 /// # }
1950 /// ```
1951 pub fn enable_time(&mut self) -> &mut Self {
1952 self.enable_time = true;
1953 self
1954 }
1955 }
1956}
1957
1958cfg_io_uring! {
1959 impl Builder {
1960 /// Enables the tokio's io_uring driver.
1961 ///
1962 /// Doing this enables using io_uring operations on the runtime.
1963 ///
1964 /// # Examples
1965 ///
1966 /// ```
1967 /// use tokio::runtime;
1968 ///
1969 /// let rt = runtime::Builder::new_multi_thread()
1970 /// .enable_io_uring()
1971 /// .build()
1972 /// .unwrap();
1973 /// ```
1974 #[cfg_attr(docsrs, doc(cfg(feature = "io-uring")))]
1975 pub fn enable_io_uring(&mut self) -> &mut Self {
1976 // Currently, the uring flag is equivalent to `enable_io`.
1977 self.enable_io = true;
1978 self
1979 }
1980 }
1981}
1982
1983cfg_test_util! {
1984 impl Builder {
1985 /// Controls if the runtime's clock starts paused or advancing.
1986 ///
1987 /// Pausing time requires the current-thread runtime; construction of
1988 /// the runtime will panic otherwise.
1989 ///
1990 /// # Examples
1991 ///
1992 /// ```
1993 /// use tokio::runtime;
1994 ///
1995 /// let rt = runtime::Builder::new_current_thread()
1996 /// .enable_time()
1997 /// .start_paused(true)
1998 /// .build()
1999 /// .unwrap();
2000 /// ```
2001 pub fn start_paused(&mut self, start_paused: bool) -> &mut Self {
2002 self.start_paused = start_paused;
2003 self
2004 }
2005 }
2006}
2007
2008cfg_schedule_latency! {
2009 impl Builder {
2010 /// Enables tracking task schedule latency.
2011 ///
2012 /// Task schedule latency is measured from a task's most recent
2013 /// transition to the scheduled state until immediately before it is
2014 /// polled. Waking a task that is already scheduled does not reset the
2015 /// measurement. Once enabled, the latency is available to task poll
2016 /// hooks through [`TaskMeta::schedule_latency`].
2017 ///
2018 /// Task schedule latencies are not tracked by default as doing so
2019 /// requires calling [`Instant::now()`] when a task is scheduled and
2020 /// when it is polled, which could add measurable overhead.
2021 ///
2022 /// The [`enable_metrics_schedule_latency_histogram`] method also
2023 /// enables tracking and records the latencies in a histogram.
2024 ///
2025 /// **This feature is only supported on 64-bit targets.**
2026 ///
2027 /// # Examples
2028 ///
2029 /// ```
2030 /// # use tokio::runtime;
2031 /// let runtime = runtime::Builder::new_current_thread()
2032 /// .track_task_schedule_latency()
2033 /// .on_before_task_poll(|meta| {
2034 /// if let Some(latency) = meta.schedule_latency() {
2035 /// println!("task schedule latency: {latency:?}");
2036 /// }
2037 /// })
2038 /// .build()
2039 /// .unwrap();
2040 /// ```
2041 ///
2042 /// [`TaskMeta::schedule_latency`]: crate::runtime::TaskMeta::schedule_latency
2043 /// [`Instant::now()`]: std::time::Instant::now
2044 /// [`enable_metrics_schedule_latency_histogram`]: Builder::enable_metrics_schedule_latency_histogram
2045 pub fn track_task_schedule_latency(&mut self) -> &mut Self {
2046 self.track_task_schedule_latency = true;
2047 self
2048 }
2049
2050 /// Enables tracking the distribution of task schedule latencies. Task
2051 /// schedule latency is the time between when a task is scheduled for
2052 /// execution and when it is polled.
2053 ///
2054 /// **This feature is only supported on 64-bit targets.**
2055 ///
2056 /// Task schedule latencies are not instrumented by default as doing
2057 /// so requires calling [`Instant::now()`] when a task is scheduled
2058 /// and when it is polled, which could add measurable overhead. Use
2059 /// the [`Handle::metrics()`] to access the metrics data.
2060 ///
2061 /// By default, a linear histogram with 10 buckets each 100 microseconds wide will be used.
2062 /// This has an extremely low memory footprint, but may not provide enough granularity. For
2063 /// better granularity with low memory usage, use [`metrics_schedule_latency_histogram_configuration()`]
2064 /// to select [`LogHistogram`] instead.
2065 ///
2066 /// On the multi-thread runtime, each task polled from the LIFO slot is
2067 /// recorded as a separate schedule-latency sample. Task poll hooks
2068 /// receive the same per-task latency through [`TaskMeta::schedule_latency`].
2069 ///
2070 /// # Examples
2071 ///
2072 /// ```
2073 /// # #[cfg(not(target_family = "wasm"))]
2074 /// # {
2075 /// use tokio::runtime;
2076 ///
2077 /// let rt = runtime::Builder::new_multi_thread()
2078 /// .enable_metrics_schedule_latency_histogram()
2079 /// .build()
2080 /// .unwrap();
2081 /// # // Test default values here
2082 /// # fn us(n: u64) -> std::time::Duration { std::time::Duration::from_micros(n) }
2083 /// # let m = rt.handle().metrics();
2084 /// # assert_eq!(m.schedule_latency_histogram_num_buckets(), 10);
2085 /// # assert_eq!(m.schedule_latency_histogram_bucket_range(0), us(0)..us(100));
2086 /// # assert_eq!(m.schedule_latency_histogram_bucket_range(1), us(100)..us(200));
2087 /// # }
2088 /// ```
2089 ///
2090 /// [`Handle::metrics()`]: crate::runtime::Handle::metrics
2091 /// [`Instant::now()`]: std::time::Instant::now
2092 /// [`LogHistogram`]: crate::runtime::LogHistogram
2093 /// [`metrics_schedule_latency_histogram_configuration()`]: Builder::metrics_schedule_latency_histogram_configuration
2094 pub fn enable_metrics_schedule_latency_histogram(&mut self) -> &mut Self {
2095 self.track_task_schedule_latency = true;
2096 self.metrics_schedule_latency_histogram_enabled = true;
2097 self
2098 }
2099
2100 /// Configure the histogram for tracking task schedule latencies.
2101 ///
2102 /// Tracking of task schedule latencies must be enabled with
2103 /// [`enable_metrics_schedule_latency_histogram()`] for this function
2104 /// to have any effect.
2105 ///
2106 /// By default, a linear histogram with 10 buckets each 100 microseconds wide will be used.
2107 /// This has an extremely low memory footprint, but may not provide enough granularity. For
2108 /// better granularity with low memory usage, use [`LogHistogram`] instead.
2109 ///
2110 /// # Examples
2111 /// Configure a [`LogHistogram`] with [default configuration]:
2112 /// ```
2113 /// # #[cfg(not(target_family = "wasm"))]
2114 /// # {
2115 /// use tokio::runtime;
2116 /// use tokio::runtime::{HistogramConfiguration, LogHistogram};
2117 ///
2118 /// let rt = runtime::Builder::new_multi_thread()
2119 /// .enable_metrics_schedule_latency_histogram()
2120 /// .metrics_schedule_latency_histogram_configuration(
2121 /// HistogramConfiguration::log(LogHistogram::default())
2122 /// )
2123 /// .build()
2124 /// .unwrap();
2125 /// # }
2126 /// ```
2127 ///
2128 /// Configure a linear histogram with 100 buckets, each 10μs wide
2129 /// ```
2130 /// # #[cfg(not(target_family = "wasm"))]
2131 /// # {
2132 /// use tokio::runtime;
2133 /// use std::time::Duration;
2134 /// use tokio::runtime::HistogramConfiguration;
2135 ///
2136 /// let rt = runtime::Builder::new_multi_thread()
2137 /// .enable_metrics_schedule_latency_histogram()
2138 /// .metrics_schedule_latency_histogram_configuration(
2139 /// HistogramConfiguration::linear(Duration::from_micros(10), 100)
2140 /// )
2141 /// .build()
2142 /// .unwrap();
2143 /// # }
2144 /// ```
2145 ///
2146 /// Configure a [`LogHistogram`] with the following settings:
2147 /// - Measure times from 100ns to 120s
2148 /// - Max error of 0.1
2149 /// - No more than 1024 buckets
2150 /// ```
2151 /// # #[cfg(not(target_family = "wasm"))]
2152 /// # {
2153 /// use std::time::Duration;
2154 /// use tokio::runtime;
2155 /// use tokio::runtime::{HistogramConfiguration, LogHistogram};
2156 ///
2157 /// let rt = runtime::Builder::new_multi_thread()
2158 /// .enable_metrics_schedule_latency_histogram()
2159 /// .metrics_schedule_latency_histogram_configuration(
2160 /// HistogramConfiguration::log(LogHistogram::builder()
2161 /// .max_value(Duration::from_secs(120))
2162 /// .min_value(Duration::from_nanos(100))
2163 /// .max_error(0.1)
2164 /// .max_buckets(1024)
2165 /// .expect("configuration uses 488 buckets")
2166 /// )
2167 /// )
2168 /// .build()
2169 /// .unwrap();
2170 /// # }
2171 /// ```
2172 ///
2173 /// [`LogHistogram`]: crate::runtime::LogHistogram
2174 /// [`enable_metrics_schedule_latency_histogram()`]: Builder::enable_metrics_schedule_latency_histogram
2175 pub fn metrics_schedule_latency_histogram_configuration(&mut self, configuration: HistogramConfiguration) -> &mut Self {
2176 self.metrics_schedule_latency_histogram.histogram_type = configuration.inner;
2177 self
2178 }
2179 }
2180}
2181
2182cfg_rt_multi_thread! {
2183 impl Builder {
2184 fn build_threaded_runtime(&mut self) -> io::Result<Runtime> {
2185 use crate::loom::sys::num_cpus;
2186 use crate::runtime::{Config, runtime::Scheduler};
2187 use crate::runtime::scheduler::{self, MultiThread};
2188
2189 let worker_threads = self.worker_threads.unwrap_or_else(num_cpus);
2190
2191 let (driver, driver_handle) = driver::Driver::new(self.get_cfg())?;
2192
2193 // Create the blocking pool
2194 let blocking_pool =
2195 blocking::create_blocking_pool(self, self.max_blocking_threads + worker_threads, worker_threads);
2196 let blocking_spawner = blocking_pool.spawner().clone();
2197
2198 // Generate a rng seed for this runtime.
2199 let seed_generator_1 = self.seed_generator.next_generator();
2200 let seed_generator_2 = self.seed_generator.next_generator();
2201
2202 let (scheduler, handle, launch) = MultiThread::new(
2203 worker_threads,
2204 driver,
2205 driver_handle,
2206 blocking_spawner,
2207 seed_generator_2,
2208 Config {
2209 before_park: self.before_park.clone(),
2210 after_unpark: self.after_unpark.clone(),
2211 before_spawn: self.before_spawn.clone(),
2212 #[cfg(tokio_unstable)]
2213 before_poll: self.before_poll.clone(),
2214 #[cfg(tokio_unstable)]
2215 after_poll: self.after_poll.clone(),
2216 after_termination: self.after_termination.clone(),
2217 global_queue_interval: self.global_queue_interval,
2218 event_interval: self.event_interval,
2219 #[cfg(tokio_unstable)]
2220 unhandled_panic: self.unhandled_panic.clone(),
2221 disable_lifo_slot: self.disable_lifo_slot,
2222 enable_eager_driver_handoff: self.enable_eager_driver_handoff,
2223 seed_generator: seed_generator_1,
2224 metrics_poll_count_histogram: self.metrics_poll_count_histogram_builder(),
2225 track_task_schedule_latency: self.track_task_schedule_latency,
2226 metrics_schedule_latency_histogram: self.metrics_schedule_latency_histogram_builder(),
2227 },
2228 self.timer_flavor,
2229 self.name.clone(),
2230 );
2231
2232 let handle = Handle { inner: scheduler::Handle::MultiThread(handle) };
2233
2234 // Spawn the thread pool workers
2235 let _enter = handle.enter();
2236 launch.launch();
2237
2238 Ok(Runtime::from_parts(Scheduler::MultiThread(scheduler), handle, blocking_pool))
2239 }
2240 }
2241}
2242
2243impl fmt::Debug for Builder {
2244 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
2245 let mut debug = fmt.debug_struct("Builder");
2246
2247 if let Some(name) = &self.name {
2248 debug.field("name", name);
2249 }
2250
2251 debug
2252 .field("worker_threads", &self.worker_threads)
2253 .field("max_blocking_threads", &self.max_blocking_threads)
2254 .field(
2255 "thread_name",
2256 &"<dyn Fn() -> String + Send + Sync + 'static>",
2257 )
2258 .field("thread_stack_size", &self.thread_stack_size)
2259 .field("after_start", &self.after_start.as_ref().map(|_| "..."))
2260 .field("before_stop", &self.before_stop.as_ref().map(|_| "..."))
2261 .field("before_park", &self.before_park.as_ref().map(|_| "..."))
2262 .field("after_unpark", &self.after_unpark.as_ref().map(|_| "..."))
2263 .field(
2264 "enable_eager_driver_handoff",
2265 &self.enable_eager_driver_handoff,
2266 );
2267
2268 if self.name.is_none() {
2269 debug.finish_non_exhaustive()
2270 } else {
2271 debug.finish()
2272 }
2273 }
2274}