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)]
7pub struct Builder {
13 name: String,
15 stack_size: usize,
17 ping_interval: usize,
19 ping_threshold: usize,
21 signals: bool,
23 pool_limit: usize,
25 pool_recv_timeout: time::Duration,
26 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 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 pub fn stop_on_panic(self, _: bool) -> Self {
61 self
62 }
63
64 #[must_use]
65 pub fn signals(mut self, eanbled: bool) -> Self {
69 self.signals = eanbled;
70 self
71 }
72
73 #[doc(hidden)]
74 #[must_use]
75 pub fn disable_signals(mut self) -> Self {
79 self.signals = false;
80 self
81 }
82
83 #[doc(hidden)]
84 #[must_use]
85 pub fn enable_signals(mut self) -> Self {
89 self.signals = true;
90 self
91 }
92
93 #[must_use]
94 pub fn stack_size(mut self, size: usize) -> Self {
96 self.stack_size = size;
97 self
98 }
99
100 #[must_use]
101 pub fn ping_interval(mut self, interval: usize) -> Self {
106 self.ping_interval = interval;
107 self
108 }
109
110 #[must_use]
111 pub fn ping_threshold(mut self, interval: usize) -> Self {
118 self.ping_threshold = interval;
119 self
120 }
121
122 #[must_use]
123 pub fn thread_pool_limit(mut self, value: usize) -> Self {
126 self.pool_limit = value;
127 self
128 }
129
130 #[must_use]
131 pub fn testing(mut self) -> Self {
133 self.testing = true;
134 self.signals = false;
135 self
136 }
137
138 #[must_use]
139 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 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 pub fn build_with(self, config: SystemConfig) -> SystemRunner {
170 let runner = config.runner.clone();
171
172 SystemRunner {
174 config,
175 runner,
176 signals: self.signals,
177 _t: PhantomData,
178 }
179 }
180}
181
182#[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 pub fn run_until_stop(self) -> io::Result<()> {
195 self.run(|| Ok(()))
196 }
197
198 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 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 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 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 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}