1use std::collections::{HashMap, HashSet};
10use std::future::Future;
11use std::io;
12use std::pin::Pin;
13use std::sync::Arc;
14use std::sync::atomic::{AtomicBool, Ordering};
15use std::time::{Duration, Instant};
16
17use urushi::Key;
18
19use super::delivery::{DeliveryQueue, EffectCancellation};
20use super::effect::{Effect, EffectKind};
21use super::subscription::{Source, Subscription};
22
23pub type Task = Pin<Box<dyn Future<Output = ()> + Send + 'static>>;
25
26pub type BlockingTask = Box<dyn FnOnce() + Send + 'static>;
28
29pub trait Execution: Send {}
31
32pub trait Executor: Send + Sync + 'static {
34 fn spawn(&self, task: Task) -> Box<dyn Execution>;
36
37 fn spawn_blocking(&self, task: BlockingTask) -> Box<dyn Execution>;
39}
40
41impl<E> Executor for Arc<E>
42where
43 E: Executor + ?Sized,
44{
45 fn spawn(&self, task: Task) -> Box<dyn Execution> {
46 E::spawn(self, task)
47 }
48
49 fn spawn_blocking(&self, task: BlockingTask) -> Box<dyn Execution> {
50 E::spawn_blocking(self, task)
51 }
52}
53
54pub trait Clock: Send + Sync + 'static {
56 fn now(&self) -> Instant;
58
59 fn sleep(&self, duration: Duration) -> Pin<Box<dyn Future<Output = Instant> + Send + 'static>>;
61}
62
63impl<C> Clock for Arc<C>
64where
65 C: Clock + ?Sized,
66{
67 fn now(&self) -> Instant {
68 C::now(self)
69 }
70
71 fn sleep(&self, duration: Duration) -> Pin<Box<dyn Future<Output = Instant> + Send + 'static>> {
72 C::sleep(self, duration)
73 }
74}
75
76#[cfg_attr(test, allow(dead_code, reason = "wired by the runtime core"))]
78pub struct TokioExecutor {
79 handle: tokio::runtime::Handle,
80}
81
82impl TokioExecutor {
83 #[cfg_attr(test, allow(dead_code, reason = "wired by the runtime core"))]
84 pub fn new(handle: tokio::runtime::Handle) -> Self {
86 Self { handle }
87 }
88}
89
90#[cfg_attr(test, allow(dead_code, reason = "wired by the runtime core"))]
91pub struct TokioClock;
93
94impl Clock for TokioClock {
95 fn now(&self) -> Instant {
96 Instant::now()
97 }
98
99 fn sleep(&self, duration: Duration) -> Pin<Box<dyn Future<Output = Instant> + Send + 'static>> {
100 let deadline = Instant::now() + duration;
101 Box::pin(async move {
102 tokio::time::sleep_until(tokio::time::Instant::from_std(deadline)).await;
103 deadline
104 })
105 }
106}
107
108impl Executor for TokioExecutor {
109 fn spawn(&self, task: Task) -> Box<dyn Execution> {
110 Box::new(TokioExecution(self.handle.spawn(task)))
111 }
112
113 fn spawn_blocking(&self, task: BlockingTask) -> Box<dyn Execution> {
114 Box::new(TokioExecution(self.handle.spawn_blocking(task)))
115 }
116}
117
118impl Executor for tokio::runtime::Handle {
119 fn spawn(&self, task: Task) -> Box<dyn Execution> {
120 Box::new(TokioExecution(self.spawn(task)))
121 }
122
123 fn spawn_blocking(&self, task: BlockingTask) -> Box<dyn Execution> {
124 Box::new(TokioExecution(self.spawn_blocking(task)))
125 }
126}
127
128#[cfg_attr(test, allow(dead_code, reason = "constructed by TokioExecutor"))]
129struct TokioExecution(tokio::task::JoinHandle<()>);
130
131impl Execution for TokioExecution {}
132
133impl Drop for TokioExecution {
134 fn drop(&mut self) {
135 self.0.abort();
136 }
137}
138
139#[derive(Clone, Copy, Debug, PartialEq, Eq)]
141pub(crate) enum EffectControl {
142 Continue,
143 Shutdown,
144}
145
146pub(crate) struct EffectExecutor<Message> {
148 executor: Arc<dyn Executor>,
149 clock: Arc<dyn Clock>,
150 deliveries: DeliveryQueue<Message>,
151 ordinary: Vec<Running>,
152 latest: HashMap<Key, Running>,
153}
154
155impl<Message: Send + 'static> EffectExecutor<Message> {
156 pub(crate) fn new(
157 executor: Arc<dyn Executor>,
158 clock: Arc<dyn Clock>,
159 deliveries: DeliveryQueue<Message>,
160 ) -> Self {
161 Self {
162 executor,
163 clock,
164 deliveries,
165 ordinary: Vec::new(),
166 latest: HashMap::new(),
167 }
168 }
169
170 pub(crate) fn start(&mut self, effect: Effect<Message>) -> EffectControl {
175 self.reap_finished();
176 if effect.requests_shutdown() {
177 self.stop();
178 return EffectControl::Shutdown;
179 }
180 let kind = effect.into_kind();
181 self.start_kind(kind);
182 self.reap_finished();
183 EffectControl::Continue
184 }
185
186 pub(crate) fn stop(&mut self) {
188 self.latest.clear();
189 self.ordinary.clear();
190 }
191
192 fn start_kind(&mut self, kind: EffectKind<Message>) {
193 match kind {
194 EffectKind::None => {}
195 EffectKind::Shutdown => unreachable!("shutdown is handled before effects start"),
196 EffectKind::Perform { key, work } => {
197 let executor = Arc::clone(&self.executor);
198 self.start_one(key, move |completion, finished| {
199 executor.spawn_blocking(Box::new(move || {
200 let _finished = FinishOnDrop(finished);
201 completion.complete(work());
202 }))
203 });
204 }
205 EffectKind::Future { key, future } => {
206 let executor = Arc::clone(&self.executor);
207 self.start_one(key, move |completion, finished| {
208 executor.spawn(Box::pin(async move {
209 let _finished = FinishOnDrop(finished);
210 completion.complete(future.await);
211 }))
212 });
213 }
214 EffectKind::After { key, delay, fire } => {
215 let executor = Arc::clone(&self.executor);
216 let clock = Arc::clone(&self.clock);
217 self.start_one(key, move |completion, finished| {
218 executor.spawn(Box::pin(async move {
219 let _finished = FinishOnDrop(finished);
220 let at = clock.sleep(delay).await;
221 completion.complete(fire(at));
222 }))
223 });
224 }
225 EffectKind::Batch(effects) => {
226 for effect in effects {
227 self.start_kind(effect.into_kind());
228 }
229 }
230 }
231 }
232
233 fn start_one(
234 &mut self,
235 key: Option<Key>,
236 spawn: impl FnOnce(
237 super::delivery::EffectCompletion<Message>,
238 Arc<AtomicBool>,
239 ) -> Box<dyn Execution>,
240 ) {
241 if let Some(key) = key {
242 drop(self.latest.remove(&key));
244 let running = Running::start(&self.deliveries, spawn);
245 self.latest.insert(key, running);
246 } else {
247 self.ordinary.push(Running::start(&self.deliveries, spawn));
248 }
249 }
250
251 fn reap_finished(&mut self) {
252 self.ordinary.retain(|running| !running.is_finished());
253 self.latest.retain(|_, running| !running.is_finished());
254 }
255}
256
257struct Running {
258 _cancellation: EffectCancellation,
261 _execution: Box<dyn Execution>,
262 finished: Arc<AtomicBool>,
263}
264
265struct FinishOnDrop(Arc<AtomicBool>);
267
268impl Drop for FinishOnDrop {
269 fn drop(&mut self) {
270 self.0.store(true, Ordering::Release);
271 }
272}
273
274impl Running {
275 fn start<Message>(
276 deliveries: &DeliveryQueue<Message>,
277 spawn: impl FnOnce(
278 super::delivery::EffectCompletion<Message>,
279 Arc<AtomicBool>,
280 ) -> Box<dyn Execution>,
281 ) -> Self {
282 let (completion, cancellation) = deliveries.latest_completion();
283 let finished = Arc::new(AtomicBool::new(false));
284 let execution = spawn(completion, Arc::clone(&finished));
285 Self {
286 _cancellation: cancellation,
287 _execution: execution,
288 finished,
289 }
290 }
291
292 fn is_finished(&self) -> bool {
293 self.finished.load(Ordering::Acquire)
294 }
295}
296
297pub(crate) trait SourceSpawner<Message>: Send + Sync + 'static {
299 fn start(
300 &self,
301 source: Source<Message>,
302 deliveries: DeliveryQueue<Message>,
303 ) -> io::Result<Box<dyn RunningSource<Message>>>;
304
305 fn failure(&self) -> Pin<Box<dyn Future<Output = io::Error> + Send + '_>> {
306 Box::pin(std::future::pending())
307 }
308
309 fn attach_runtime(&self, _deliveries: DeliveryQueue<Message>) {}
310}
311
312pub(crate) trait RunningSource<Message>: Send {
314 fn refresh(&mut self, source: Source<Message>);
315}
316
317pub(crate) struct SubscriptionExecutor<Message> {
319 spawner: Arc<dyn SourceSpawner<Message>>,
320 deliveries: DeliveryQueue<Message>,
321 running: HashMap<Key, Box<dyn RunningSource<Message>>>,
322}
323
324impl<Message: Send + 'static> SubscriptionExecutor<Message> {
325 pub(crate) fn new(
326 spawner: Arc<dyn SourceSpawner<Message>>,
327 deliveries: DeliveryQueue<Message>,
328 ) -> Self {
329 Self {
330 spawner,
331 deliveries,
332 running: HashMap::new(),
333 }
334 }
335
336 pub(crate) fn reconcile(&mut self, subscription: Subscription<Message>) -> io::Result<()> {
337 let desired = latest_declarations(subscription.into_sources());
338 let desired_keys: HashSet<Key> = desired.iter().map(|source| source.key).collect();
339 self.running.retain(|key, _| desired_keys.contains(key));
340
341 for source in desired {
342 if let Some(running) = self.running.get_mut(&source.key) {
343 running.refresh(source);
344 } else {
345 let key = source.key;
346 let running = self.spawner.start(source, self.deliveries.clone())?;
347 self.running.insert(key, running);
348 }
349 }
350 Ok(())
351 }
352
353 pub(crate) fn stop(&mut self) {
354 self.running.clear();
355 }
356}
357
358impl<Message> Drop for SubscriptionExecutor<Message> {
359 fn drop(&mut self) {
360 self.running.clear();
363 }
364}
365
366fn latest_declarations<Message>(sources: Vec<Source<Message>>) -> Vec<Source<Message>> {
370 let mut seen = HashSet::new();
371 let mut declarations: Vec<_> = sources
372 .into_iter()
373 .rev()
374 .filter(|source| seen.insert(source.key))
375 .collect();
376 declarations.reverse();
377 declarations.sort_by_key(|source| source.kind.is_surface());
383 declarations
384}
385
386#[cfg(test)]
387#[path = "executor_tests.rs"]
388mod tests;