blob: b93f060c7c7d95e60e2e7af02dbe1d530d569a08 [file] [log] [blame]
Stjepan Glavina1479e862019-08-12 20:18:51 +02001//! A single-threaded executor where join handles catch panics inside tasks.
2
Stjepan Glavina1479e862019-08-12 20:18:51 +02003use std::future::Future;
4use std::panic::AssertUnwindSafe;
5use std::thread;
6
7use crossbeam::channel::{unbounded, Sender};
8use futures::executor;
9use futures::future::FutureExt;
10use lazy_static::lazy_static;
11
Stjepan Glavinafcfa4ab2019-11-25 18:39:17 +010012type Task = async_task::Task<()>;
13type JoinHandle<T> = async_task::JoinHandle<T, ()>;
14
Stjepan Glavina1479e862019-08-12 20:18:51 +020015/// Spawns a future on the executor.
Stjepan Glavinafcfa4ab2019-11-25 18:39:17 +010016fn spawn<F, R>(future: F) -> JoinHandle<thread::Result<R>>
Stjepan Glavina1479e862019-08-12 20:18:51 +020017where
18 F: Future<Output = R> + Send + 'static,
19 R: Send + 'static,
20{
21 lazy_static! {
22 // A channel that holds scheduled tasks.
Stjepan Glavinafcfa4ab2019-11-25 18:39:17 +010023 static ref QUEUE: Sender<Task> = {
24 let (sender, receiver) = unbounded::<Task>();
Stjepan Glavina1479e862019-08-12 20:18:51 +020025
26 // Start the executor thread.
27 thread::spawn(|| {
28 for task in receiver {
29 // No need for `catch_unwind()` here because panics are already caught.
30 task.run();
31 }
32 });
33
34 sender
35 };
36 }
37
38 // Create a future that catches panics within itself.
39 let future = AssertUnwindSafe(future).catch_unwind();
40
41 // Create a task that is scheduled by sending itself into the channel.
42 let schedule = |t| QUEUE.send(t).unwrap();
43 let (task, handle) = async_task::spawn(future, schedule, ());
44
45 // Schedule the task by sending it into the channel.
46 task.schedule();
47
48 handle
49}
50
51fn main() {
52 // Spawn a future that completes succesfully.
53 let handle = spawn(async {
54 println!("Hello, world!");
55 });
56
57 // Block on the future and report its result.
58 match executor::block_on(handle) {
Stjepan Glavinad7b17fb2020-04-14 14:38:57 +020059 None => println!("The task was canceled."),
Stjepan Glavina1479e862019-08-12 20:18:51 +020060 Some(Ok(val)) => println!("The task completed with {:?}", val),
61 Some(Err(_)) => println!("The task has panicked"),
62 }
63
64 // Spawn a future that panics.
65 let handle = spawn(async {
66 panic!("Ooops!");
67 });
68
69 // Block on the future and report its result.
70 match executor::block_on(handle) {
Stjepan Glavinad7b17fb2020-04-14 14:38:57 +020071 None => println!("The task was canceled."),
Stjepan Glavina1479e862019-08-12 20:18:51 +020072 Some(Ok(val)) => println!("The task completed with {:?}", val),
73 Some(Err(_)) => println!("The task has panicked"),
74 }
75}