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