async_global_executor/
config.rs

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