1
use std::time::Duration;
2

            
3
use futures::future::Future;
4
use kludgine_core::flume;
5
use lazy_static::lazy_static;
6
use parking_lot::RwLock;
7
use smol_timeout::TimeoutExt;
8

            
9
lazy_static! {
10
    pub static ref GLOBAL_THREAD_POOL: RwLock<Option<smol::Executor<'static>>> = RwLock::new(None);
11
}
12

            
13
pub fn initialize() {
14
    {
15
        let mut pool_guard = GLOBAL_THREAD_POOL.write();
16
        if pool_guard.is_some() {
17
            return;
18
        }
19

            
20
        let executor = smol::Executor::new();
21
        *pool_guard = Some(executor);
22
    }
23

            
24
    // Launch a thread pool
25
    std::thread::spawn(|| {
26
        let (signal, shutdown) = flume::unbounded::<()>();
27

            
28
        easy_parallel::Parallel::new()
29
            // Run four executor threads.
30
            .each(0..4, |_| {
31
                #[allow(clippy::await_holding_lock)] // this is an rwlock, not a mutex.
32
                futures::executor::block_on(async {
33
                    let guard = GLOBAL_THREAD_POOL.read();
34
                    let executor = guard.as_ref().unwrap();
35
                    executor.run(shutdown.recv_async()).await
36
                })
37
            })
38
            // Run the main future on the current thread.
39
            .finish(|| {});
40

            
41
        drop(signal);
42
    });
43
}
44

            
45
impl super::Runtime {
46
    /// Spawns an async task.
47
    pub fn spawn<Fut: Future<Output = T> + Send + 'static, T: Send + 'static>(future: Fut) {
48
        let guard = GLOBAL_THREAD_POOL.read();
49
        let executor = guard.as_ref().unwrap();
50
        executor.spawn(future).detach();
51
    }
52

            
53
    /// Executes a future in a blocking-safe manner.
54
    pub fn block_on<'a, Fut: Future<Output = R> + Send + 'a, R: Send + Sync + 'a>(
55
        future: Fut,
56
    ) -> R {
57
        futures::executor::block_on(future)
58
    }
59

            
60
    /// Executes `future` for up to `duration`. If a timeout occurs, `None` is
61
    /// returned.
62
    pub async fn timeout<F: Future<Output = T> + Send, T: Send>(
63
        future: F,
64
        duration: Duration,
65
    ) -> Option<T> {
66
        future.timeout(duration).await
67
    }
68
}