yz_server_executor/
lib.rs

1#![forbid(unsafe_code)]
2
3pub use async_executor::Executor;
4use event_listener::Event;
5use futures_lite::future::block_on as fblon;
6use std::sync::Arc;
7
8pub struct ServerExecutor {
9    // the user shouldn't be able to clone the current object.
10    ex: Arc<Executor<'static>>,
11
12    shutdown: Event,
13}
14
15#[cfg(feature = "num_cpus")]
16impl Default for ServerExecutor {
17    #[inline]
18    fn default() -> Self {
19        Self::new()
20    }
21}
22
23impl ServerExecutor {
24    #[cfg(feature = "num_cpus")]
25    #[inline]
26    pub fn new() -> Self {
27        Self::with_threads(num_cpus::get())
28    }
29
30    pub fn with_threads(n: usize) -> Self {
31        let ret = Self {
32            ex: Arc::new(Executor::new()),
33            shutdown: Event::new(),
34        };
35
36        for _ in 0..n {
37            let ex = ret.ex.clone();
38            let listener = ret.shutdown.listen();
39            std::thread::Builder::new()
40                .name("yxd-srvexec-worker".to_string())
41                .spawn(move || fblon(ex.run(listener)))
42                .expect("unable to spawn worker threads");
43        }
44
45        ret
46    }
47
48    /// Multithreaded `block_on` function
49    #[inline]
50    pub fn block_on<'x, F, I, R>(&'x self, f: F) -> R
51    where
52        F: FnOnce(&'x Arc<Executor<'static>>) -> I,
53        I: std::future::Future<Output = R> + 'x,
54        R: 'x,
55    {
56        fblon(f(&self.ex))
57    }
58}
59
60impl Drop for ServerExecutor {
61    fn drop(&mut self) {
62        // we don't rely on the fact that this destructor runs,
63        // as it only cleans up leftover resources
64        // if this doesn't run, we thus just waste some resources
65        self.shutdown.notify(usize::MAX);
66    }
67}