Skip to main content

ntex_rt/
builder.rs

1use std::{fmt, future::Future, io, marker::PhantomData, panic, rc::Rc, sync::Arc, time};
2
3use crate::{driver::Runner, signals, system::System, system::SystemConfig};
4
5#[derive(Debug, Clone)]
6/// Builder struct for a ntex runtime.
7///
8/// Either use `Builder::build` to create a system and start actors.
9/// Alternatively, use `Builder::run` to start the runtime and
10/// run a function in its context.
11pub struct Builder {
12    /// Name of the System. Defaults to "ntex" if unset.
13    name: String,
14    /// New thread stack size
15    stack_size: usize,
16    /// Arbiters ping interval
17    ping_interval: usize,
18    /// Arbiter ping response threshold
19    ping_threshold: usize,
20    /// Signal handling
21    signals: bool,
22    /// Panic handling
23    panics: bool,
24    /// Thread pool config
25    pool_limit: usize,
26    pool_recv_timeout: time::Duration,
27    /// testing flag
28    testing: bool,
29}
30
31impl Builder {
32    pub(super) fn new() -> Self {
33        Builder {
34            name: "ntex".into(),
35            stack_size: 0,
36            ping_interval: 2000,
37            ping_threshold: 1000,
38            signals: false,
39            panics: false,
40            testing: false,
41            pool_limit: 256,
42            pool_recv_timeout: time::Duration::from_secs(60),
43        }
44    }
45
46    #[must_use]
47    /// Sets the name of the System.
48    pub fn name<N: AsRef<str>>(mut self, name: N) -> Self {
49        self.name = name.as_ref().into();
50        self
51    }
52
53    #[doc(hidden)]
54    #[deprecated(since = "3.17.0")]
55    #[must_use]
56    /// Sets the option `stop_on_panic`
57    ///
58    /// It controls whether the System is stopped when an
59    /// uncaught panic is thrown from a worker thread.
60    ///
61    /// Defaults is set to false.
62    pub fn stop_on_panic(self, _: bool) -> Self {
63        self
64    }
65
66    #[must_use]
67    /// Set signals handling.
68    ///
69    /// By default, signal handling is disabled.
70    pub fn signals(mut self, eanbled: bool) -> Self {
71        self.signals = eanbled;
72        self
73    }
74
75    #[must_use]
76    /// Enables panic handling.
77    ///
78    /// When panic handling is enabled, the application can receive
79    /// `Signal::Panic(PanicReason::Panic(..))` signals.
80    /// By default, panic handling is disabled.
81    pub fn panic_handling(mut self, eanbled: bool) -> Self {
82        self.panics = eanbled;
83        self
84    }
85
86    #[doc(hidden)]
87    #[must_use]
88    /// Disable signal handling.
89    ///
90    /// By default, signal handling is disabled.
91    pub fn disable_signals(mut self) -> Self {
92        self.signals = false;
93        self
94    }
95
96    #[doc(hidden)]
97    #[must_use]
98    /// Enable signal handling.
99    ///
100    /// By default, signal handling is enabled.
101    pub fn enable_signals(mut self) -> Self {
102        self.signals = true;
103        self
104    }
105
106    #[must_use]
107    /// Sets the size of the stack (in bytes) for the new worker thread.
108    pub fn stack_size(mut self, size: usize) -> Self {
109        self.stack_size = size;
110        self
111    }
112
113    #[must_use]
114    /// Sets ping interval for spawned arbiters.
115    ///
116    /// Interval is in milliseconds. By default 2000 milliseconds is set.
117    /// To disable pings set value to zero.
118    pub fn ping_interval(mut self, interval: usize) -> Self {
119        self.ping_interval = interval;
120        self
121    }
122
123    #[must_use]
124    /// Sets the ping response threshold.
125    ///
126    /// If a response takes too long, an attempt is made to create a backtrace
127    /// for the busy arbiter.
128    ///
129    /// The interval is specified in milliseconds. The default is 1000 milliseconds.
130    pub fn ping_threshold(mut self, interval: usize) -> Self {
131        self.ping_threshold = interval;
132        self
133    }
134
135    #[must_use]
136    /// Set the thread number limit of the inner thread pool, if exists. The
137    /// default value is 256.
138    pub fn thread_pool_limit(mut self, value: usize) -> Self {
139        self.pool_limit = value;
140        self
141    }
142
143    #[must_use]
144    /// Mark system as testing
145    pub fn testing(mut self) -> Self {
146        self.testing = true;
147        self.signals = false;
148        self.panics = false;
149        self
150    }
151
152    #[must_use]
153    /// Set the waiting timeout of the inner thread, if exists. The default is
154    /// 60 seconds.
155    pub fn thread_pool_recv_timeout<T>(mut self, timeout: T) -> Self
156    where
157        time::Duration: From<T>,
158    {
159        self.pool_recv_timeout = timeout.into();
160        self
161    }
162
163    /// Create new System.
164    ///
165    /// This method panics if it can not create runtime
166    pub fn build<R: Runner>(self, runner: R) -> SystemRunner {
167        let config = SystemConfig {
168            name: self.name.clone(),
169            testing: self.testing,
170            stack_size: self.stack_size,
171            ping_interval: self.ping_interval,
172            ping_threshold: self.ping_threshold,
173            pool_limit: self.pool_limit,
174            pool_recv_timeout: self.pool_recv_timeout,
175            runner: Arc::new(runner),
176        };
177        self.build_with(config)
178    }
179
180    /// Create new System.
181    ///
182    /// This method panics if it can not create runtime
183    pub fn build_with(self, config: SystemConfig) -> SystemRunner {
184        let runner = config.runner.clone();
185
186        // init system arbiter and run configuration method
187        SystemRunner {
188            config,
189            runner,
190            signals: self.signals,
191            panics: self.panics,
192            _t: PhantomData,
193        }
194    }
195}
196
197/// Helper object that runs System's event loop
198#[must_use = "SystemRunner must be run"]
199pub struct SystemRunner {
200    config: SystemConfig,
201    runner: Arc<dyn Runner>,
202    signals: bool,
203    panics: bool,
204    _t: PhantomData<Rc<()>>,
205}
206
207impl SystemRunner {
208    /// This function will start event loop and will finish once the
209    /// `System::stop()` function is called.
210    pub fn run_until_stop(self) -> io::Result<()> {
211        self.run(|| Ok(()))
212    }
213
214    /// This function will start event loop and will finish once the
215    /// `System::stop()` function is called.
216    pub fn run<F>(self, f: F) -> io::Result<()>
217    where
218        F: FnOnce() -> io::Result<()> + 'static,
219    {
220        log::info!("Starting {:?} system", self.config.name);
221
222        let SystemRunner {
223            config,
224            runner,
225            signals,
226            panics,
227            ..
228        } = self;
229
230        if panics {
231            signals::enable_panic_handling();
232        }
233
234        // run loop
235        crate::driver::block_on_panic(runner.as_ref(), async move {
236            let (system, stop) = System::start(config);
237            if signals {
238                system.enable_signals();
239            }
240
241            f()?;
242
243            match stop.await {
244                Ok(code) => {
245                    if code != 0 {
246                        Err(io::Error::other(format!("Non-zero exit code: {code}")))
247                    } else {
248                        Ok(())
249                    }
250                }
251                Err(_) => Err(io::Error::other("Closed")),
252            }
253        })
254    }
255
256    #[allow(clippy::missing_panics_doc)]
257    /// Execute a future and wait for result.
258    pub fn block_on<F, R>(self, fut: F) -> R
259    where
260        F: Future<Output = R> + 'static,
261        R: 'static,
262    {
263        let SystemRunner {
264            config,
265            runner,
266            signals,
267            panics,
268            ..
269        } = self;
270
271        if panics {
272            signals::enable_panic_handling();
273        }
274
275        crate::driver::block_on_panic(runner.as_ref(), async move {
276            let (system, _) = System::start(config);
277            if signals {
278                system.enable_signals();
279            }
280
281            let loc = current_location();
282            ntex_error::set_backtrace_start(loc.file(), loc.line() + 2);
283            fut.await
284        })
285    }
286
287    #[cfg(feature = "tokio")]
288    /// Execute a future and wait for result.
289    pub async fn run_local<F, R>(self, fut: F) -> R
290    where
291        F: Future<Output = R> + 'static,
292        R: 'static,
293    {
294        let SystemRunner { config, .. } = self;
295
296        // run loop
297        let result = tok_io::task::LocalSet::new()
298            .run_until(async move {
299                _ = System::start(config);
300
301                let loc = current_location();
302                ntex_error::set_backtrace_start(loc.file(), loc.line() + 2);
303                fut.await
304            })
305            .await;
306
307        unsafe {
308            crate::remove_all_items();
309        }
310        result
311    }
312}
313
314#[track_caller]
315pub(crate) fn current_location() -> &'static panic::Location<'static> {
316    panic::Location::caller()
317}
318
319impl fmt::Debug for SystemRunner {
320    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
321        f.debug_struct("SystemRunner")
322            .field("config", &self.config)
323            .finish()
324    }
325}