| 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 | |