deno_runtime/
tokio_util.rs

1// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
2use std::fmt::Debug;
3use std::str::FromStr;
4
5use deno_core::unsync::MaskFutureAsSend;
6#[cfg(tokio_unstable)]
7use tokio_metrics::RuntimeMonitor;
8
9/// Default configuration for tokio. In the future, this method may have different defaults
10/// depending on the platform and/or CPU layout.
11const fn tokio_configuration() -> (u32, u32, usize) {
12  (61, 31, 1024)
13}
14
15fn tokio_env<T: FromStr>(name: &'static str, default: T) -> T
16where
17  <T as FromStr>::Err: Debug,
18{
19  match std::env::var(name) {
20    Ok(value) => value.parse().unwrap(),
21    Err(_) => default,
22  }
23}
24
25pub fn create_basic_runtime() -> tokio::runtime::Runtime {
26  let (event_interval, global_queue_interval, max_io_events_per_tick) =
27    tokio_configuration();
28
29  tokio::runtime::Builder::new_current_thread()
30    .enable_io()
31    .enable_time()
32    .event_interval(tokio_env("DENO_TOKIO_EVENT_INTERVAL", event_interval))
33    .global_queue_interval(tokio_env(
34      "DENO_TOKIO_GLOBAL_QUEUE_INTERVAL",
35      global_queue_interval,
36    ))
37    .max_io_events_per_tick(tokio_env(
38      "DENO_TOKIO_MAX_IO_EVENTS_PER_TICK",
39      max_io_events_per_tick,
40    ))
41    // This limits the number of threads for blocking operations (like for
42    // synchronous fs ops) or CPU bound tasks like when we run dprint in
43    // parallel for deno fmt.
44    // The default value is 512, which is an unhelpfully large thread pool. We
45    // don't ever want to have more than a couple dozen threads.
46    .max_blocking_threads(if cfg!(windows) {
47      // on windows, tokio uses blocking tasks for child process IO, make sure
48      // we have enough available threads for other tasks to run
49      4 * std::thread::available_parallelism()
50        .map(|n| n.get())
51        .unwrap_or(8)
52    } else {
53      32
54    })
55    .build()
56    .unwrap()
57}
58
59#[inline(always)]
60fn create_and_run_current_thread_inner<F, R>(
61  future: F,
62  metrics_enabled: bool,
63) -> R
64where
65  F: std::future::Future<Output = R> + 'static,
66  R: Send + 'static,
67{
68  let rt = create_basic_runtime();
69
70  // Since this is the main future, we want to box it in debug mode because it tends to be fairly
71  // large and the compiler won't optimize repeated copies. We also make this runtime factory
72  // function #[inline(always)] to avoid holding the unboxed, unused future on the stack.
73
74  #[cfg(debug_assertions)]
75  // SAFETY: this this is guaranteed to be running on a current-thread executor
76  let future = Box::pin(unsafe { MaskFutureAsSend::new(future) });
77
78  #[cfg(not(debug_assertions))]
79  // SAFETY: this this is guaranteed to be running on a current-thread executor
80  let future = unsafe { MaskFutureAsSend::new(future) };
81
82  #[cfg(tokio_unstable)]
83  let join_handle = if metrics_enabled {
84    rt.spawn(async move {
85      let metrics_interval: u64 = std::env::var("DENO_TOKIO_METRICS_INTERVAL")
86        .ok()
87        .and_then(|val| val.parse().ok())
88        .unwrap_or(1000);
89      let handle = tokio::runtime::Handle::current();
90      let runtime_monitor = RuntimeMonitor::new(&handle);
91      tokio::spawn(async move {
92        #[allow(clippy::print_stderr)]
93        for interval in runtime_monitor.intervals() {
94          eprintln!("{:#?}", interval);
95          // wait 500ms
96          tokio::time::sleep(std::time::Duration::from_millis(
97            metrics_interval,
98          ))
99          .await;
100        }
101      });
102      future.await
103    })
104  } else {
105    rt.spawn(future)
106  };
107
108  #[cfg(not(tokio_unstable))]
109  let join_handle = rt.spawn(future);
110
111  let r = rt.block_on(join_handle).unwrap().into_inner();
112  // Forcefully shutdown the runtime - we're done executing JS code at this
113  // point, but there might be outstanding blocking tasks that were created and
114  // latered "unrefed". They won't terminate on their own, so we're forcing
115  // termination of Tokio runtime at this point.
116  rt.shutdown_background();
117  r
118}
119
120#[inline(always)]
121pub fn create_and_run_current_thread<F, R>(future: F) -> R
122where
123  F: std::future::Future<Output = R> + 'static,
124  R: Send + 'static,
125{
126  create_and_run_current_thread_inner(future, false)
127}
128
129#[inline(always)]
130pub fn create_and_run_current_thread_with_maybe_metrics<F, R>(future: F) -> R
131where
132  F: std::future::Future<Output = R> + 'static,
133  R: Send + 'static,
134{
135  let metrics_enabled = std::env::var("DENO_TOKIO_METRICS").ok().is_some();
136  create_and_run_current_thread_inner(future, metrics_enabled)
137}