1use std::future::Future;
5use std::sync::Arc;
6
7use futures::Stream;
8use futures::StreamExt;
9use futures::stream::BoxStream;
10use parking_lot::Mutex;
11use smol::block_on;
12
13use crate::runtime::BlockingRuntime;
14use crate::runtime::Executor;
15use crate::runtime::Handle;
16pub use crate::runtime::pool::CurrentThreadWorkerPool;
17
18#[derive(Clone, Default)]
32pub struct CurrentThreadRuntime {
33 executor: Arc<smol::Executor<'static>>,
34}
35
36impl CurrentThreadRuntime {
37 pub fn new() -> Self {
39 Self::default()
40 }
41
42 pub fn new_pool(&self) -> CurrentThreadWorkerPool {
50 CurrentThreadWorkerPool::new(Arc::clone(&self.executor))
51 }
52
53 pub fn block_on_stream_thread_safe<F, S, R>(&self, f: F) -> ThreadSafeIterator<R>
61 where
62 F: FnOnce(Handle) -> S,
63 S: Stream<Item = R> + Send + 'static,
64 R: Send + 'static,
65 {
66 let stream = f(self.handle());
67
68 let (result_tx, result_rx) = kanal::bounded_async(1);
72 let driver = self.executor.spawn(async move {
73 futures::pin_mut!(stream);
74 while let Some(item) = stream.next().await {
75 if let Err(e) = result_tx.send(item).await {
77 tracing::trace!("all receivers dropped, stopping stream: {}", e);
78 break;
79 }
80 }
81 });
82
83 ThreadSafeIterator {
84 executor: Arc::clone(&self.executor),
85 results: result_rx,
86 driver: Arc::new(Mutex::new(Some(driver))),
87 }
88 }
89}
90
91impl BlockingRuntime for CurrentThreadRuntime {
92 type BlockingIterator<'a, R: 'a> = CurrentThreadIterator<'a, R>;
93
94 fn handle(&self) -> Handle {
95 let executor: Arc<dyn Executor> = Arc::clone(&self.executor) as Arc<dyn Executor>;
96 Handle::new(Arc::downgrade(&executor))
97 }
98
99 fn block_on<Fut, R>(&self, fut: Fut) -> R
100 where
101 Fut: Future<Output = R>,
102 {
103 block_on(self.executor.run(fut))
104 }
105
106 fn block_on_stream<'a, S, R>(&self, stream: S) -> Self::BlockingIterator<'a, R>
107 where
108 S: Stream<Item = R> + Send + 'a,
109 R: Send + 'a,
110 {
111 CurrentThreadIterator {
112 executor: Arc::clone(&self.executor),
113 stream: stream.boxed(),
114 }
115 }
116}
117
118pub struct CurrentThreadIterator<'a, T> {
120 executor: Arc<smol::Executor<'static>>,
121 stream: BoxStream<'a, T>,
122}
123
124impl<T> Iterator for CurrentThreadIterator<'_, T> {
125 type Item = T;
126
127 fn next(&mut self) -> Option<Self::Item> {
128 block_on(self.executor.run(self.stream.next()))
129 }
130}
131
132pub struct ThreadSafeIterator<T> {
134 executor: Arc<smol::Executor<'static>>,
135 results: kanal::AsyncReceiver<T>,
136 driver: Arc<Mutex<Option<smol::Task<()>>>>,
140}
141
142impl<T> Clone for ThreadSafeIterator<T> {
144 fn clone(&self) -> Self {
145 Self {
146 executor: Arc::clone(&self.executor),
147 results: self.results.clone(),
148 driver: Arc::clone(&self.driver),
149 }
150 }
151}
152
153impl<T> Iterator for ThreadSafeIterator<T> {
154 type Item = T;
155
156 fn next(&mut self) -> Option<Self::Item> {
157 match block_on(self.executor.run(self.results.recv())) {
158 Ok(item) => Some(item),
159 Err(_) => {
163 let task = self.driver.lock().take();
164 if let Some(task) = task {
165 block_on(self.executor.run(task));
166 }
167 None
168 }
169 }
170 }
171}
172
173#[expect(clippy::if_then_some_else_none)] #[cfg(test)]
175mod tests {
176 use std::any::Any;
177 use std::panic::AssertUnwindSafe;
178 use std::sync::Arc;
179 use std::sync::Barrier;
180 use std::sync::atomic::AtomicUsize;
181 use std::sync::atomic::Ordering;
182 use std::task::Poll;
183 use std::thread;
184 use std::time::Duration;
185
186 use futures::StreamExt;
187 use futures::stream;
188 use parking_lot::Mutex;
189
190 use super::*;
191
192 #[test]
193 fn test_worker_thread() {
194 let runtime = CurrentThreadRuntime::new();
195
196 let value = Arc::new(AtomicUsize::new(0));
198 let value2 = Arc::clone(&value);
199 runtime
200 .handle()
201 .spawn(async move {
202 value2.store(42, Ordering::SeqCst);
203 })
204 .detach();
205
206 assert_eq!(value.load(Ordering::SeqCst), 0);
208
209 let pool = runtime.new_pool();
211 assert_eq!(value.load(Ordering::SeqCst), 0);
212
213 pool.set_workers(1);
215 for _ in 0..10 {
216 if value.load(Ordering::SeqCst) == 42 {
217 break;
218 }
219 thread::sleep(Duration::from_millis(10));
220 }
221 assert_eq!(value.load(Ordering::SeqCst), 42);
222 }
223
224 #[test]
225 fn test_block_on_stream_single_thread() {
226 let mut iter =
227 CurrentThreadRuntime::new().block_on_stream(stream::iter(vec![1, 2, 3, 4, 5]).boxed());
228
229 assert_eq!(iter.next(), Some(1));
230 assert_eq!(iter.next(), Some(2));
231 assert_eq!(iter.next(), Some(3));
232 assert_eq!(iter.next(), Some(4));
233 assert_eq!(iter.next(), Some(5));
234 assert_eq!(iter.next(), None);
235 }
236
237 #[test]
238 fn test_block_on_stream_multiple_threads() {
239 let counter = Arc::new(AtomicUsize::new(0));
240 let num_threads = 4;
241 let items_per_thread = 25;
242 let total_items = 100;
243
244 let iter = CurrentThreadRuntime::new()
245 .block_on_stream_thread_safe(|_h| stream::iter(0..total_items).boxed());
246
247 let barrier = Arc::new(Barrier::new(num_threads));
248 let results = Arc::new(Mutex::new(Vec::new()));
249
250 let threads: Vec<_> = (0..num_threads)
251 .map(|_| {
252 let mut iter = iter.clone();
253 let counter = Arc::clone(&counter);
254 let barrier = Arc::clone(&barrier);
255 let results = Arc::clone(&results);
256
257 thread::spawn(move || {
258 barrier.wait();
259 let mut local_results = Vec::new();
260
261 for _ in 0..items_per_thread {
262 if let Some(item) = iter.next() {
263 counter.fetch_add(1, Ordering::SeqCst);
264 local_results.push(item);
265 }
266 }
267
268 results.lock().push(local_results);
269 })
270 })
271 .collect();
272
273 for thread in threads {
274 thread.join().unwrap();
275 }
276
277 assert_eq!(counter.load(Ordering::SeqCst), total_items);
278
279 let all_results = results.lock();
280 let mut collected: Vec<_> = all_results.iter().flatten().copied().collect();
281 collected.sort();
282 assert_eq!(collected, (0..total_items).collect::<Vec<_>>());
283 }
284
285 #[test]
286 fn test_block_on_stream_thread_safe_propagates_driver_panic() {
287 let runtime = CurrentThreadRuntime::new();
288 let mut iter = runtime.block_on_stream_thread_safe(|_h| {
289 stream::poll_fn(|_| -> Poll<Option<usize>> {
290 panic!("stream driver panic");
291 })
292 .boxed()
293 });
294
295 let panic = std::panic::catch_unwind(AssertUnwindSafe(|| iter.next()))
296 .expect_err("stream panic must propagate through iterator");
297 let message = panic
298 .downcast_ref::<&'static str>()
299 .copied()
300 .or_else(|| panic.downcast_ref::<String>().map(String::as_str))
301 .unwrap_or("<unknown panic>");
302 assert!(message.contains("stream driver panic"));
303 }
304
305 fn panic_message(panic: &(dyn Any + Send)) -> &str {
306 panic
307 .downcast_ref::<&'static str>()
308 .copied()
309 .or_else(|| panic.downcast_ref::<String>().map(String::as_str))
310 .unwrap_or("<unknown panic>")
311 }
312
313 #[test]
316 fn test_block_on_stream_thread_safe_panic_propagation_is_deterministic() {
317 for i in 0..2000 {
318 let mut iter = CurrentThreadRuntime::new().block_on_stream_thread_safe(|_h| {
319 stream::poll_fn(|_| -> Poll<Option<usize>> {
320 panic!("deterministic driver panic");
321 })
322 .boxed()
323 });
324
325 let outcome = std::panic::catch_unwind(AssertUnwindSafe(|| iter.next()));
326 assert!(
327 outcome.is_err(),
328 "driver panic was swallowed on iteration {i}: next() returned {:?}",
329 outcome.ok().flatten(),
330 );
331 }
332 }
333
334 #[test]
337 fn test_block_on_stream_thread_safe_panic_after_items() {
338 let mut emitted = 0usize;
339 let iter = CurrentThreadRuntime::new().block_on_stream_thread_safe(move |_h| {
340 stream::poll_fn(move |_| -> Poll<Option<usize>> {
341 if emitted < 3 {
342 emitted += 1;
343 Poll::Ready(Some(emitted))
344 } else {
345 panic!("driver panic after items");
346 }
347 })
348 .boxed()
349 });
350
351 let outcome = std::panic::catch_unwind(AssertUnwindSafe(move || iter.collect::<Vec<_>>()));
354 match outcome {
355 Ok(items) => panic!("driver panic was swallowed; stream ended cleanly with {items:?}"),
356 Err(panic) => assert!(panic_message(&*panic).contains("driver panic after items")),
357 }
358 }
359
360 #[test]
364 fn test_block_on_stream_thread_safe_multi_consumer_panic_surfaced() {
365 let iter = CurrentThreadRuntime::new().block_on_stream_thread_safe(|_h| {
366 stream::poll_fn(|_| -> Poll<Option<usize>> {
367 panic!("multi consumer driver panic");
368 })
369 .boxed()
370 });
371
372 let num_threads = 4;
373 let barrier = Arc::new(Barrier::new(num_threads));
374 let panics = Arc::new(AtomicUsize::new(0));
375
376 let handles: Vec<_> = (0..num_threads)
377 .map(|_| {
378 let mut iter = iter.clone();
379 let barrier = Arc::clone(&barrier);
380 let panics = Arc::clone(&panics);
381 thread::spawn(move || {
382 barrier.wait();
383 match std::panic::catch_unwind(AssertUnwindSafe(|| iter.next())) {
384 Ok(None) => {}
387 Ok(Some(_)) => panic!("no item was produced before the driver panicked"),
388 Err(panic) => {
389 assert!(panic_message(&*panic).contains("multi consumer driver panic"));
390 panics.fetch_add(1, Ordering::SeqCst);
391 }
392 }
393 })
394 })
395 .collect();
396
397 for handle in handles {
398 handle.join().expect("consumer thread panicked uncaught");
399 }
400
401 assert_eq!(panics.load(Ordering::SeqCst), 1);
403 }
404
405 #[test]
407 fn test_block_on_stream_thread_safe_clean_completion_returns_none() {
408 let mut iter = CurrentThreadRuntime::new()
409 .block_on_stream_thread_safe(|_h| stream::iter(vec![1usize, 2, 3]).boxed());
410
411 assert_eq!(iter.next(), Some(1));
412 assert_eq!(iter.next(), Some(2));
413 assert_eq!(iter.next(), Some(3));
414 assert_eq!(iter.next(), None);
415 assert_eq!(iter.next(), None);
416 }
417
418 #[test]
419 fn test_block_on_stream_concurrent_clone_and_drive() {
420 let num_items = 50;
421 let num_threads = 3;
422
423 let iter = CurrentThreadRuntime::new().block_on_stream_thread_safe(|h| {
424 stream::unfold(0, move |state| {
425 let h = h.clone();
426 async move {
427 if state < num_items {
428 h.spawn_cpu(move || {
429 thread::sleep(Duration::from_micros(10));
430 state
431 })
432 .await;
433 Some((state, state + 1))
434 } else {
435 None
436 }
437 }
438 })
439 });
440
441 let collected = Arc::new(Mutex::new(Vec::new()));
442 let barrier = Arc::new(Barrier::new(num_threads));
443
444 let threads: Vec<_> = (0..num_threads)
445 .map(|thread_id| {
446 let iter = iter.clone();
447 let collected = Arc::clone(&collected);
448 let barrier = Arc::clone(&barrier);
449
450 thread::spawn(move || {
451 barrier.wait();
452 let mut local_items = Vec::new();
453
454 for item in iter {
455 local_items.push((thread_id, item));
456 if local_items.len() >= 5 {
457 break;
458 }
459 }
460
461 collected.lock().extend(local_items);
462 })
463 })
464 .collect();
465
466 for thread in threads {
467 thread.join().unwrap();
468 }
469
470 let results = collected.lock();
471 let mut values: Vec<_> = results.iter().map(|(_, v)| *v).collect();
472 values.sort();
473 values.dedup();
474
475 assert!(values.len() >= 5);
476 assert!(values.iter().all(|&v| v < num_items));
477 }
478
479 #[test]
480 fn test_block_on_stream_async_work() {
481 let runtime = CurrentThreadRuntime::new();
482 let handle = runtime.handle();
483 let iter = runtime.block_on_stream({
484 stream::unfold((handle, 0), |(h, state)| async move {
485 if state < 10 {
486 let value = h
487 .spawn(async move { futures::future::ready(state * 2).await })
488 .await;
489 Some((value, (h, state + 1)))
490 } else {
491 None
492 }
493 })
494 });
495
496 let results: Vec<_> = iter.collect();
497 assert_eq!(results, vec![0, 2, 4, 6, 8, 10, 12, 14, 16, 18]);
498 }
499
500 #[test]
501 fn test_block_on_stream_drop_receivers_early() {
502 let counter = Arc::new(AtomicUsize::new(0));
503 let c = Arc::clone(&counter);
504
505 let mut iter = CurrentThreadRuntime::new().block_on_stream({
506 stream::unfold(0, move |state| {
507 let c = Arc::clone(&c);
508 async move {
509 (state < 100).then(|| {
510 c.fetch_add(1, Ordering::SeqCst);
511 (state, state + 1)
512 })
513 }
514 })
515 .boxed()
516 });
517
518 assert_eq!(iter.next(), Some(0));
519 assert_eq!(iter.next(), Some(1));
520 assert_eq!(iter.next(), Some(2));
521
522 drop(iter);
523
524 let final_count = counter.load(Ordering::SeqCst);
525 assert!(
526 final_count < 100,
527 "Stream should stop when all receivers are dropped"
528 );
529 }
530
531 #[test]
532 fn test_block_on_stream_interleaved_access() {
533 let barrier = Arc::new(Barrier::new(2));
534 let iter = CurrentThreadRuntime::new()
535 .block_on_stream_thread_safe(|_h| stream::iter(0..20).boxed());
536
537 let iter1 = iter.clone();
538 let iter2 = iter;
539 let barrier1 = Arc::clone(&barrier);
540 let barrier2 = barrier;
541
542 let thread1 = thread::spawn(move || {
543 let mut iter = iter1;
544 let mut results = Vec::new();
545 barrier1.wait();
546
547 for _ in 0..5 {
548 if let Some(val) = iter.next() {
549 results.push(val);
550 thread::sleep(Duration::from_micros(50));
551 }
552 }
553 results
554 });
555
556 let thread2 = thread::spawn(move || {
557 let mut iter = iter2;
558 let mut results = Vec::new();
559 barrier2.wait();
560
561 for _ in 0..5 {
562 if let Some(val) = iter.next() {
563 results.push(val);
564 thread::sleep(Duration::from_micros(50));
565 }
566 }
567 results
568 });
569
570 let results1 = thread1.join().unwrap();
571 let results2 = thread2.join().unwrap();
572
573 let mut all_results = results1;
574 all_results.extend(results2);
575 all_results.sort();
576
577 assert_eq!(all_results, (0..10).collect::<Vec<_>>());
578
579 for i in 0..10 {
580 assert_eq!(all_results.iter().filter(|&&x| x == i).count(), 1);
581 }
582 }
583
584 #[test]
585 fn test_block_on_stream_stress_test() {
586 let num_threads = 10;
587 let num_items = 1000;
588
589 let iter = CurrentThreadRuntime::new()
590 .block_on_stream_thread_safe(|_h| stream::iter(0..num_items).boxed());
591
592 let received = Arc::new(Mutex::new(Vec::new()));
593 let barrier = Arc::new(Barrier::new(num_threads));
594
595 let threads: Vec<_> = (0..num_threads)
596 .map(|_| {
597 let iter = iter.clone();
598 let received = Arc::clone(&received);
599 let barrier = Arc::clone(&barrier);
600
601 thread::spawn(move || {
602 barrier.wait();
603 for val in iter {
604 received.lock().push(val);
605 }
606 })
607 })
608 .collect();
609
610 for thread in threads {
611 thread.join().unwrap();
612 }
613
614 let mut results = received.lock().clone();
615 results.sort();
616
617 assert_eq!(results.len(), num_items);
618 assert_eq!(results, (0..num_items).collect::<Vec<_>>());
619 }
620}