Skip to main content

nuclei/
compat.rs

1//! The executor API of earlier nuclei versions: [`init`], the
2//! [`GlobalExecutorConfig`] builder, and growing or shrinking the default
3//! runtime's workers. Each maps onto the process default
4//! [`Runtime`](crate::Runtime).
5//!
6//! It stays at the crate root, with [`Proactor`](crate::Proactor) and
7//! [`drive`](crate::drive), so code written against those versions builds;
8//! new code reaches for [`Builder`](crate::Builder) (with
9//! [`build_global`](crate::Builder::build_global), which answers its
10//! error), [`RuntimeHandle`](crate::RuntimeHandle) and
11//! [`block_on`](crate::block_on). Its shapes are kept as they were:
12//! [`spawn_more_threads`] is async though nothing in it waits, and
13//! [`stop_thread`] answers a [`Task`] that dropping cancels before it stops
14//! anything.
15
16use std::fmt;
17use std::io;
18
19use crate::rt::runtime::{self, Builder};
20use crate::rt::task::Task;
21use crate::rt::workers::Workers;
22
23/// How the process default runtime is set up by [`init_with_config`].
24#[derive(Default)]
25pub struct GlobalExecutorConfig {
26    env_var: Option<&'static str>,
27    min_threads: Option<usize>,
28    max_threads: Option<usize>,
29    thread_name_fn: Option<Box<dyn Fn() -> String + Send + Sync>>,
30}
31
32impl fmt::Debug for GlobalExecutorConfig {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        f.debug_struct("GlobalExecutorConfig")
35            .field("env_var", &self.env_var)
36            .field("min_threads", &self.min_threads)
37            .field("max_threads", &self.max_threads)
38            .finish_non_exhaustive()
39    }
40}
41
42impl GlobalExecutorConfig {
43    /// Reads the number of workers to start from the environment variable
44    /// `env_var`, before [`with_min_threads`](Self::with_min_threads).
45    pub fn with_env_var(mut self, env_var: &'static str) -> Self {
46        self.env_var = Some(env_var);
47        self
48    }
49
50    /// Starts this many workers (default: one per core), the fewest
51    /// [`stop_thread`] leaves running.
52    pub fn with_min_threads(mut self, min_threads: usize) -> Self {
53        self.min_threads = Some(min_threads);
54        self
55    }
56
57    /// The most workers the runtime may run, [`spawn_more_threads`]
58    /// included (default: four times the minimum). Zero runs every task on
59    /// the thread inside `block_on`, with no worker at all.
60    pub fn with_max_threads(mut self, max_threads: usize) -> Self {
61        self.max_threads = Some(max_threads);
62        self
63    }
64
65    /// Names each worker thread.
66    pub fn with_thread_name_fn(
67        mut self,
68        thread_name_fn: impl Fn() -> String + Send + Sync + 'static,
69    ) -> Self {
70        self.thread_name_fn = Some(Box::new(thread_name_fn));
71        self
72    }
73
74    /// The runtime builder this configuration describes.
75    fn builder(self) -> Builder {
76        let from_env = self
77            .env_var
78            .and_then(|name| std::env::var(name).ok())
79            .and_then(|threads| threads.parse::<usize>().ok());
80        let min = from_env
81            .or(self.min_threads)
82            .unwrap_or_else(|| std::thread::available_parallelism().map_or(1, usize::from));
83        let mut builder = match self.max_threads {
84            Some(0) => Builder::new().worker_threads(0),
85            max => {
86                let min = min.max(1);
87                Builder::new()
88                    .worker_threads(min)
89                    .max_worker_threads(max.unwrap_or(min.saturating_mul(4)).max(min))
90            }
91        };
92        if let Some(name) = self.thread_name_fn {
93            builder = builder.thread_name_fn(name);
94        }
95        builder
96    }
97}
98
99/// Starts the process default runtime now, with every setting at its
100/// default, unless it is running already.
101pub fn init() {
102    runtime::default_runtime();
103}
104
105/// Starts the process default runtime now, as `config` says, unless it is
106/// running already (then `config` is not used).
107///
108/// A runtime that cannot start is reported at the `error` level of the
109/// [`log`](https://docs.rs/log) facade, and the default runtime stays
110/// unstarted: the next call that needs it starts it with every setting at
111/// its default. [`Builder::build_global`](crate::Builder::build_global)
112/// answers the error instead.
113pub fn init_with_config(config: GlobalExecutorConfig) {
114    match config.builder().build_global() {
115        Ok(_) => {}
116        Err(err) if err.kind() == io::ErrorKind::AlreadyExists => {}
117        Err(err) => log::error!("the default runtime could not start as configured: {err}"),
118    }
119}
120
121/// Starts up to `count` more workers on the runtime this thread is inside
122/// (or the default one), within its maximum; returns how many started.
123///
124/// # Errors
125///
126/// When a worker's poller cannot be made or its thread cannot start.
127pub async fn spawn_more_threads(count: usize) -> io::Result<usize> {
128    Workers::start(&runtime::current(), count)
129}
130
131/// Stops one worker of the runtime this thread is inside (or the default
132/// one), unless only the runtime's minimum runs: it takes no new task from
133/// now on and exits once the tasks it has are done. The task completes with
134/// whether a worker was stopped, once it takes no new task.
135pub fn stop_thread() -> Task<bool> {
136    crate::runtime::spawn(async { runtime::current().workers.stop_one() })
137}
138
139/// Stops the worker this task runs on, as [`stop_thread`] stops one: false
140/// on a thread that is not a worker of its runtime, or when only the
141/// minimum runs.
142pub fn stop_current_thread() -> Task<bool> {
143    crate::runtime::spawn_local(async {
144        let inner = runtime::current();
145        crate::rt::context::current(|current| {
146            current
147                .filter(|shard| shard.worker_index(inner.id()).is_some())
148                .is_some_and(|shard| inner.workers.stop(&shard.shared))
149        })
150    })
151}