veilid_tools/
future_race_completion.rs1use super::*;
10
11use futures_util::future::{select, Either};
12use std::pin::Pin;
13
14#[derive(Debug)]
16pub enum FutureRaceCompletion<T, R> {
17 Original(T),
19 Race(R),
21}
22
23#[derive(Debug, Clone)]
25pub struct FutureRaceCompletionPool {
26 inner: Arc<AsyncRwLock<()>>,
28 name: Arc<String>,
30}
31
32impl FutureRaceCompletionPool {
33 pub fn new(name: impl Into<String>) -> Self {
35 Self {
36 inner: Arc::new(AsyncRwLock::new(())),
37 name: Arc::new(name.into()),
38 }
39 }
40
41 pub fn spawn<F>(&self, future: F)
43 where
44 F: Future<Output = ()> + Send + 'static,
45 {
46 let inner = self.inner.clone();
47 let name = self.name.clone();
48 spawn_detached(name.as_str(), async move {
49 let read_guard = inner.read_arc().await;
50 future.await;
51 drop(read_guard);
52 });
53 }
54
55 pub fn channel_spawn<F, T>(&self, future: F) -> flume::Receiver<T>
59 where
60 F: Future<Output = T> + Send + 'static,
61 T: Send + 'static,
62 {
63 let (tx, rx) = flume::bounded(1);
64 let inner = self.inner.clone();
65 let name = self.name.clone();
66 spawn_detached(name.as_str(), async move {
67 let read_guard = inner.read_arc().await;
68 let result = future.await;
69 let _ = tx.send(result);
70 drop(read_guard);
71 });
72 rx
73 }
74
75 pub async fn shutdown(&self) {
78 let _w = self.inner.write_arc().await;
79 }
80}
81
82pub trait FutureRaceCompletionPoolExt: Future + Sized {
84 fn race_complete_on<R, F>(
88 self,
89 pool: FutureRaceCompletionPool,
90 race_fut: F,
91 ) -> impl Future<Output = FutureRaceCompletion<Self::Output, R>> + Send
92 where
93 Self: Send + 'static,
94 Self::Output: Send + 'static,
95 F: Future<Output = R> + Send,
96 R: Send;
97}
98
99impl<Fut> FutureRaceCompletionPoolExt for Fut
100where
101 Fut: Future,
102{
103 #[expect(clippy::manual_async_fn)]
105 fn race_complete_on<R, F>(
106 self,
107 pool: FutureRaceCompletionPool,
108 race_fut: F,
109 ) -> impl Future<Output = FutureRaceCompletion<Fut::Output, R>> + Send
110 where
111 Self: Send + 'static,
112 Self::Output: Send + 'static,
113 F: Future<Output = R> + Send,
114 R: Send,
115 {
116 async move {
117 let pinned_self: Pin<Box<Fut>> = Box::pin(self);
118 let pinned_race = Box::pin(race_fut);
119 match select(pinned_self, pinned_race).await {
120 Either::Left((output, _race)) => FutureRaceCompletion::Original(output),
121 Either::Right((race_output, original)) => {
122 pool.spawn(async move {
123 let _ = original.await;
124 });
125 FutureRaceCompletion::Race(race_output)
126 }
127 }
128 }
129 }
130}