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 stop_on_panic: bool,
17 stack_size: usize,
19 ping_interval: usize,
21 ping_threshold: usize,
23 signals: bool,
25 pool_limit: usize,
27 pool_recv_timeout: time::Duration,
28 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 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 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 pub fn signals(mut self, eanbled: bool) -> Self {
71 self.signals = eanbled;
72 self
73 }
74
75 #[doc(hidden)]
76 #[must_use]
77 pub fn disable_signals(mut self) -> Self {
81 self.signals = false;
82 self
83 }
84
85 #[doc(hidden)]
86 #[must_use]
87 pub fn enable_signals(mut self) -> Self {
91 self.signals = true;
92 self
93 }
94
95 #[must_use]
96 pub fn stack_size(mut self, size: usize) -> Self {
98 self.stack_size = size;
99 self
100 }
101
102 #[must_use]
103 pub fn ping_interval(mut self, interval: usize) -> Self {
108 self.ping_interval = interval;
109 self
110 }
111
112 #[must_use]
113 pub fn ping_threshold(mut self, interval: usize) -> Self {
120 self.ping_threshold = interval;
121 self
122 }
123
124 #[must_use]
125 pub fn thread_pool_limit(mut self, value: usize) -> Self {
128 self.pool_limit = value;
129 self
130 }
131
132 #[must_use]
133 pub fn testing(mut self) -> Self {
135 self.testing = true;
136 self.signals = false;
137 self
138 }
139
140 #[must_use]
141 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 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 pub fn build_with(self, config: SystemConfig) -> SystemRunner {
173 let runner = config.runner.clone();
174
175 SystemRunner {
177 config,
178 runner,
179 signals: self.signals,
180 _t: PhantomData,
181 }
182 }
183}
184
185#[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 pub fn run_until_stop(self) -> io::Result<()> {
198 self.run(|| Ok(()))
199 }
200
201 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 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 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 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 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}