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