1 | use once_cell::sync::OnceCell; |
2 | use std::{ |
3 | fmt, |
4 | sync::atomic::{AtomicUsize, Ordering}, |
5 | }; |
6 | |
7 | pub(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)] |
11 | pub 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 | |
22 | impl fmt::Debug for GlobalExecutorConfig { |
23 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
24 | f&mut DebugStruct<'_, '_>.debug_struct("GlobalExecutorConfig" ) |
25 | .field("env_var" , &self.env_var) |
26 | .field("min_threads" , &self.min_threads) |
27 | .field(name:"max_threads" , &self.max_threads) |
28 | .finish() |
29 | } |
30 | } |
31 | |
32 | impl 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 |
87 | pub(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 | |
93 | impl Default for Config { |
94 | fn default() -> Self { |
95 | GlobalExecutorConfig::default().seal() |
96 | } |
97 | } |
98 | |