1use async_io::Timer;
2use async_task::Task;
3use futures_lite::{FutureExt, Stream, StreamExt};
4use std::{
5 future::Future,
6 pin::Pin,
7 sync::Arc,
8 task::{Context, Poll},
9 time::Duration,
10};
11use trillium_server_common::{DroppableFuture, Runtime, RuntimeTrait};
12
13#[derive(Debug, Clone, Copy, Default)]
15pub struct SmolRuntime(());
16
17struct DetachOnDrop<Output>(Option<Task<Output>>);
18impl<Output> Future for DetachOnDrop<Output> {
19 type Output = Output;
20
21 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
22 Pin::new(self.0.as_mut().unwrap()).poll(cx)
23 }
24}
25
26impl<Output> Drop for DetachOnDrop<Output> {
27 fn drop(&mut self) {
28 if let Some(task) = self.0.take() {
29 task.detach();
30 }
31 }
32}
33
34impl RuntimeTrait for SmolRuntime {
35 fn spawn<Fut>(
36 &self,
37 fut: Fut,
38 ) -> DroppableFuture<impl Future<Output = Option<Fut::Output>> + Send + 'static>
39 where
40 Fut: Future + Send + 'static,
41 Fut::Output: Send + 'static,
42 {
43 let join_handle = DetachOnDrop(Some(async_global_executor::spawn(fut)));
44 DroppableFuture::new(async move { join_handle.catch_unwind().await.ok() })
45 }
46
47 async fn delay(&self, duration: Duration) {
48 Timer::after(duration).await;
49 }
50
51 fn interval(&self, period: Duration) -> impl Stream<Item = ()> + Send + 'static {
52 Timer::interval(period).map(|_| ())
53 }
54
55 fn block_on<Fut: Future>(&self, fut: Fut) -> Fut::Output {
56 async_global_executor::block_on(fut)
57 }
58
59 #[cfg(unix)]
60 fn hook_signals(
61 &self,
62 signals: impl IntoIterator<Item = i32>,
63 ) -> impl Stream<Item = i32> + Send + 'static {
64 use async_signal::{Signal, Signals};
65 use signal_hook::consts::signal::*;
66 let signals: Vec<Signal> = signals
67 .into_iter()
68 .filter_map(|n| match n {
69 SIGHUP => Some(Signal::Hup),
70 SIGINT => Some(Signal::Int),
71 SIGQUIT => Some(Signal::Quit),
72 SIGILL => Some(Signal::Ill),
73 SIGTRAP => Some(Signal::Trap),
74 SIGABRT => Some(Signal::Abort),
75 SIGBUS => Some(Signal::Bus),
76 SIGFPE => Some(Signal::Fpe),
77 SIGKILL => Some(Signal::Kill),
78 SIGUSR1 => Some(Signal::Usr1),
79 SIGSEGV => Some(Signal::Segv),
80 SIGUSR2 => Some(Signal::Usr2),
81 SIGPIPE => Some(Signal::Pipe),
82 SIGALRM => Some(Signal::Alarm),
83 SIGTERM => Some(Signal::Term),
84 SIGCHLD => Some(Signal::Child),
85 SIGCONT => Some(Signal::Cont),
86 SIGSTOP => Some(Signal::Stop),
87 SIGTSTP => Some(Signal::Tstp),
88 SIGTTIN => Some(Signal::Ttin),
89 SIGTTOU => Some(Signal::Ttou),
90 SIGURG => Some(Signal::Urg),
91 SIGXCPU => Some(Signal::Xcpu),
92 SIGXFSZ => Some(Signal::Xfsz),
93 SIGVTALRM => Some(Signal::Vtalarm),
94 SIGPROF => Some(Signal::Prof),
95 SIGWINCH => Some(Signal::Winch),
96 SIGIO => Some(Signal::Io),
97 SIGSYS => Some(Signal::Sys),
98 _ => None,
99 })
100 .collect();
101 Signals::new(signals)
102 .unwrap()
103 .filter_map(|r| r.ok().map(|s| s as i32))
104 }
105}
106
107impl SmolRuntime {
108 pub fn spawn<Fut>(
118 &self,
119 fut: Fut,
120 ) -> DroppableFuture<impl Future<Output = Option<Fut::Output>> + Send + 'static + use<Fut>>
121 where
122 Fut: Future + Send + 'static,
123 Fut::Output: Send + 'static,
124 {
125 let join_handle = DetachOnDrop(Some(async_global_executor::spawn(fut)));
126 DroppableFuture::new(async move { join_handle.catch_unwind().await.ok() })
127 }
128
129 pub async fn delay(&self, duration: Duration) {
131 Timer::after(duration).await;
132 }
133
134 pub fn interval(&self, period: Duration) -> impl Stream<Item = ()> + Send + 'static + use<> {
136 Timer::interval(period).map(|_| ())
137 }
138
139 pub fn block_on<Fut: Future>(&self, fut: Fut) -> Fut::Output {
141 RuntimeTrait::block_on(self, fut)
142 }
143
144 pub async fn timeout<Fut>(&self, duration: Duration, fut: Fut) -> Option<Fut::Output>
146 where
147 Fut: Future + Send,
148 Fut::Output: Send + 'static,
149 {
150 RuntimeTrait::timeout(self, duration, fut).await
151 }
152}
153
154impl From<SmolRuntime> for Runtime {
155 fn from(value: SmolRuntime) -> Self {
156 Arc::new(value).into()
157 }
158}