1use std::sync::OnceLock;
5use std::sync::atomic::{AtomicUsize, Ordering};
6use std::thread;
7use std::time::Duration;
8
9use futures::Future;
10use net_traits::AsyncRuntime;
11use tokio::runtime::{Builder, Handle, Runtime};
12
13pub struct AsyncRuntimeHolder {
16 runtime: Option<Runtime>,
17}
18
19impl AsyncRuntimeHolder {
20 pub(crate) fn new(runtime: Runtime) -> Self {
21 Self {
22 runtime: Some(runtime),
23 }
24 }
25}
26
27impl AsyncRuntime for AsyncRuntimeHolder {
28 fn shutdown(&mut self) {
29 self.runtime
30 .take()
31 .expect("Runtime should have been initialized on start-up.")
32 .shutdown_timeout(Duration::from_millis(100))
33 }
34}
35
36static ASYNC_RUNTIME_HANDLE: OnceLock<Handle> = OnceLock::new();
39
40pub fn init_async_runtime() -> Box<dyn AsyncRuntime> {
41 let runtime = Builder::new_multi_thread()
43 .thread_name_fn(|| {
44 static ATOMIC_ID: AtomicUsize = AtomicUsize::new(0);
45 let id = ATOMIC_ID.fetch_add(1, Ordering::Relaxed);
46 format!("tokio-runtime-{}", id)
47 })
48 .worker_threads(
49 thread::available_parallelism()
50 .map(|i| i.get())
51 .unwrap_or(servo_config::pref!(threadpools_fallback_worker_num) as usize)
52 .min(servo_config::pref!(threadpools_async_runtime_workers_max).max(1) as usize),
53 )
54 .enable_io()
55 .enable_time()
56 .build()
57 .expect("Unable to build tokio-runtime runtime");
58
59 ASYNC_RUNTIME_HANDLE
61 .set(runtime.handle().clone())
62 .expect("Runtime handle should be initialized once on start-up");
63
64 Box::new(AsyncRuntimeHolder::new(runtime))
66}
67
68pub fn spawn_task<F>(task: F)
70where
71 F: Future + 'static + std::marker::Send,
72 F::Output: Send + 'static,
73{
74 ASYNC_RUNTIME_HANDLE
75 .get()
76 .expect("Runtime handle should be initialized on start-up")
77 .spawn(task);
78}
79
80pub fn spawn_blocking_task<F, R>(task: F) -> F::Output
82where
83 F: Future,
84{
85 ASYNC_RUNTIME_HANDLE
86 .get()
87 .expect("Runtime handle should be initialized on start-up")
88 .block_on(task)
89}