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