async_global_executor/
config.rs

1use std::{
2    fmt,
3    sync::{
4        atomic::{AtomicUsize, Ordering},
5        OnceLock,
6    },
7};
8
9pub(crate) static GLOBAL_EXECUTOR_CONFIG: OnceLock<Config> = OnceLock::new();
10
11/// Configuration to init the thread pool for the multi-threaded global executor.
12#[derive(Default)]
13pub struct GlobalExecutorConfig {
14    /// The environment variable from which we'll try to parse the number of threads to spawn.
15    env_var: Option<&'static str>,
16    /// The minimum number of threads to spawn.
17    min_threads: Option<usize>,
18    /// The maximum number of threads to spawn.
19    max_threads: Option<usize>,
20    /// The closure function used to get the name of the thread. The name can be used for identification in panic messages.
21    thread_name_fn: Option<Box<dyn Fn() -> String + Send + Sync>>,
22}
23
24impl fmt::Debug for GlobalExecutorConfig {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        f.debug_struct("GlobalExecutorConfig")
27            .field("env_var", &self.env_var)
28            .field("min_threads", &self.min_threads)
29            .field("max_threads", &self.max_threads)
30            .finish()
31    }
32}
33
34impl GlobalExecutorConfig {
35    /// Use the specified environment variable to find the number of threads to spawn.
36    pub fn with_env_var(mut self, env_var: &'static str) -> Self {
37        self.env_var = Some(env_var);
38        self
39    }
40
41    /// Use the specified value as the minimum number of threads.
42    pub fn with_min_threads(mut self, min_threads: usize) -> Self {
43        self.min_threads = Some(min_threads);
44        self
45    }
46
47    /// Use the specified value as the maximum number of threads for async tasks.
48    /// To limit the maximum number of threads for blocking tasks, please use the
49    /// `BLOCKING_MAX_THREADS` environment variable.
50    pub fn with_max_threads(mut self, max_threads: usize) -> Self {
51        self.max_threads = Some(max_threads);
52        self
53    }
54
55    /// Use the specified prefix to name the threads.
56    pub fn with_thread_name_fn(
57        mut self,
58        thread_name_fn: impl Fn() -> String + Send + Sync + 'static,
59    ) -> Self {
60        self.thread_name_fn = Some(Box::new(thread_name_fn));
61        self
62    }
63
64    pub(crate) fn seal(self) -> Config {
65        let min_threads = std::env::var(self.env_var.unwrap_or("ASYNC_GLOBAL_EXECUTOR_THREADS"))
66            .ok()
67            .and_then(|threads| threads.parse().ok())
68            .or(self.min_threads)
69            .unwrap_or_else(|| std::thread::available_parallelism().map_or(1, usize::from))
70            .max(1);
71        let max_threads = self.max_threads.unwrap_or(min_threads * 4).max(min_threads);
72        Config {
73            min_threads,
74            max_threads,
75            thread_name_fn: self.thread_name_fn.unwrap_or_else(|| {
76                Box::new(|| {
77                    static GLOBAL_EXECUTOR_NEXT_THREAD: AtomicUsize = AtomicUsize::new(1);
78                    format!(
79                        "async-global-executor-{}",
80                        GLOBAL_EXECUTOR_NEXT_THREAD.fetch_add(1, Ordering::SeqCst)
81                    )
82                })
83            }),
84        }
85    }
86}
87
88// The actual configuration, computed from the given GlobalExecutorConfig
89pub(crate) struct Config {
90    pub(crate) min_threads: usize,
91    pub(crate) max_threads: usize,
92    pub(crate) thread_name_fn: Box<dyn Fn() -> String + Send + Sync>,
93}
94
95impl Default for Config {
96    fn default() -> Self {
97        GlobalExecutorConfig::default().seal()
98    }
99}