async_nursery 0.6.0

Primitive for structured concurrency
Documentation
//! We can use catch_unwind to keep a panicking task from crashing the application.
//! This is useful if resilience is needed for a long running application.
//!
//! You have to assert that your task is unwind safe before doing this!
//!
//! In this example we still bail out of sibling tasks if any spawned task panics,
//! but you can also keep them running by useing `next` instead of `try_next`. You
//! can then capture all the results in a collection with `collect` to inspect them.
//!
//! Expected output:
//!
//! $ cargo run --example return_catch_unwind
//!
//! INFO [return_catch_unwind] nursery created
//! INFO [return_catch_unwind] spawned slow.
//! INFO [return_catch_unwind] spawned wrong.
//! ERROR [return_catch_unwind] panicked at 'I don't like waiting.', examples/return_catch_unwind.rs:72:5
//! INFO [return_catch_unwind] nursery created
//! INFO [return_catch_unwind] spawned slow.
//! INFO [return_catch_unwind] spawned wrong.
//! ERROR [return_catch_unwind] panicked at 'I don't like waiting.', examples/return_catch_unwind.rs:72:5
//! INFO [return_catch_unwind] nursery created
//! INFO [return_catch_unwind] spawned slow.
//! INFO [return_catch_unwind] spawned wrong.
//! //! ... a 100 times.
//!
mod common;

use
{
	async_executors :: { AsyncStd                } ,
	async_nursery   :: { Nursery, NurseExt       } ,
	common          :: { setup_tracing           } ,
	futures         :: { FutureExt, TryStreamExt } ,
	futures_timer   :: { Delay                   } ,
	std             :: { time::Duration          } ,
	tracing_crate   :: { info, error             } ,
};


async fn return_catch_unwind() -> std::thread::Result<()>
{
	let (nursery, mut output) = Nursery::new( AsyncStd ); info!( "nursery created" );

	nursery.nurse( slow()                 )?;
	nursery.nurse( wrong().catch_unwind() )?;

	// This is necessary. Since we could keep spawning tasks even after starting to poll
	// the output, it can't know that we are done, unless we drop all senders or call
	// `close_nursery`.
	//
	// Of course if an error happens, it wouldn't matter, but if no error happens it would
	// make the while loop below hang indefinitely.
	//
	drop(nursery);

	// Can't do it with TryFuture because we would need specialization.
	// Use TryStreamExt. This will return as soon as an error happens in
	// any spawned task and thus drop all the others when the current function
	// returns.
	//
	while output.try_next().await?.is_some() {};

	unreachable!( "drop Nursery and NurseryStream" );
}



// Linger.
//
async fn slow() -> std::thread::Result<()>
{
	info!( "spawned slow." );

	Delay::new( Duration::from_secs(5) ).await;

	error!( "I managed to stall you all." );

	Ok(())
}



// This will return an error.
//
async fn wrong()
{
	info!( "spawned wrong." );

	panic!( "I don't like waiting." )
}



#[ async_std::main ]
//
async fn main()
{
	setup_tracing();

	// log errors instead of returning them on the stderr.
	//
	std::panic::set_hook( Box::new( |e| error!( "{}", e ) ) );

	for _ in 0..100
	{
		let err = return_catch_unwind().await;
		assert!( err.is_err() );
	}
}