af_core/
run.rs

1// Copyright © 2021 Alexandra Frydl
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7use crate::prelude::*;
8use crate::task;
9use crate::thread;
10use signal_hook::consts::TERM_SIGNALS;
11use signal_hook::iterator::Signals;
12use std::process::exit;
13
14/// Starts a task from the given future, waits for it to stop, then exits the
15/// process.
16///
17/// If the task fails, this function logs the error and exits the process with
18/// a non-zero exit code.
19pub fn run<T, E>(future: impl Future<Output = Result<T, E>> + Send + 'static) -> !
20where
21  T: Send + 'static,
22  E: Display + Send + 'static,
23{
24  let task = task::start(future);
25
26  match thread::block_on(task.join()) {
27    Ok(Err(err)) => {
28      error!("The main task failed. {}", err);
29
30      thread::sleep(Duration::hz(60));
31      exit(1)
32    }
33
34    Err(err) => {
35      if let Some(value) = err.value_str() {
36        error!("The main task panicked with `{}`.", value);
37      } else {
38        error!("The main task panicked.")
39      }
40
41      thread::sleep(Duration::hz(60));
42      exit(-1)
43    }
44
45    _ => exit(0),
46  }
47}
48
49/// Starts a task from the given function, waits for it to stop, then exits the
50/// process.
51///
52/// If the task fails, this function logs the error and exits the process with
53/// a non-zero exit code.
54///
55/// The provided function is passed a [`task::CancelSignal`] that is triggered
56/// when the process receives a termination signal (SIGINT, SIGTERM, or
57/// SIGQUIT).
58pub fn run_with<T, E, F>(func: impl FnOnce(task::CancelSignal) -> F + Send + 'static) -> !
59where
60  T: Send + 'static,
61  E: Display + Send + 'static,
62  F: Future<Output = Result<T, E>> + Send + 'static,
63{
64  let canceler = task::Canceler::new();
65  let cancel = canceler.signal();
66
67  let mut signals = Signals::new(TERM_SIGNALS).expect("Failed to register signal handler");
68
69  thread::start("af_core::run canceler", move || {
70    let mut iter = signals.into_iter();
71
72    iter.next();
73
74    warn!("The process received a termination signal. Canceling the main task…");
75
76    canceler.cancel();
77  });
78
79  run(async { func(cancel).await })
80}