@ -108,12 +108,28 @@ async fn do_work(process: impl AsRef<Process>, file: impl AsRef<OsStr>, mut prog
}
}
}
}
pub async fn work < I , T , U > ( flags : & arg ::Flags , process : U , files : I , children : Option < NonZeroUsize > ) -> Result < ( ) , Box < dyn std ::error ::Error > >
pub async fn work < I , T , U > ( flags : & arg ::Flags , process : U , files : I , children : Option < NonZeroUsize > , cancel : impl Future + Send + Unpin + ' static ) -> Result < ( ) , Box < dyn std ::error ::Error > >
where I : IntoIterator < Item = T > ,
where I : IntoIterator < Item = T > ,
< I as IntoIterator > ::IntoIter : ExactSizeIterator ,
< I as IntoIterator > ::IntoIter : ExactSizeIterator ,
T : AsRef < OsStr > + Send + Sync + ' static + Clone ,
T : AsRef < OsStr > + Send + Sync + ' static + Clone ,
U : Into < PathBuf >
U : Into < PathBuf >
{
{
let ( cancel , cancel_register ) = {
use futures ::future ::AbortHandle ;
let ( handle , reg ) = AbortHandle ::new_pair ( ) ;
let rh = handle . clone ( ) ;
tokio ::spawn ( async move {
let _ = cancel . await ;
handle . abort ( ) ;
} ) ;
( rh , reg )
} ;
/// Make a future cancellable by the passed `cancel` token.
macro_rules! pass_cancel {
( $a :expr ) = > ( ::futures ::future ::Abortable ::new ( $a , cancel_register ) ) ;
}
let ( tx , mut rx ) = mpsc ::channel ::< ( T , fixed_stack ::IntoIter < ( bool , String ) > , usize ) > ( children . as_ref ( ) . map ( | & x | usize ::from ( x ) ) . unwrap_or ( 16 ) ) ;
let ( tx , mut rx ) = mpsc ::channel ::< ( T , fixed_stack ::IntoIter < ( bool , String ) > , usize ) > ( children . as_ref ( ) . map ( | & x | usize ::from ( x ) ) . unwrap_or ( 16 ) ) ;
let semaphore = children . map ( | children | Arc ::new ( Semaphore ::new ( children . into ( ) ) ) ) ;
let semaphore = children . map ( | children | Arc ::new ( Semaphore ::new ( children . into ( ) ) ) ) ;
let process = Arc ::new ( Process ::new ( process , flags . leanify_flags . clone ( ) ) ) ;
let process = Arc ::new ( Process ::new ( process , flags . leanify_flags . clone ( ) ) ) ;
@ -161,16 +177,19 @@ where I: IntoIterator<Item=T>,
} ;
} ;
let mut i = 0 usize ;
let mut i = 0 usize ;
let results =
let results =
join_all(
pass_cancel! ( join_all(
files
files
. map ( | filename | {
. map ( | filename | {
let semaphore = semaphore . clone ( ) ;
let semaphore = semaphore . clone ( ) ;
let process = Arc ::clone ( & process ) ;
let process = Arc ::clone ( & process ) ;
let mut tx = tx . clone ( ) ;
let mut tx = tx . clone ( ) ;
let cancel = cancel . clone ( ) ;
#[ cfg(feature= " progress " ) ] let mut progress = progress . clone ( ) ;
#[ cfg(feature= " progress " ) ] let mut progress = progress . clone ( ) ;
( tokio ::spawn ( async move {
//TODO: Where to put the cancellation check in here?? (XXX: Current
//`AbortHandle` (old tokio version) does not have `is_aborted()`... :/)
( tokio ::spawn ( async move {
#[ cfg(feature= " progress " ) ] type Opt < T > = OptionFuture < T > ;
#[ cfg(feature= " progress " ) ] type Opt < T > = OptionFuture < T > ;
#[ cfg(not(feature= " progress " )) ] type Opt < T > = std ::marker ::PhantomData < T > ;
#[ cfg(not(feature= " progress " )) ] type Opt < T > = std ::marker ::PhantomData < T > ;
let _task_id : Opt < _ > = {
let _task_id : Opt < _ > = {
@ -179,7 +198,7 @@ where I: IntoIterator<Item=T>,
// (task_id, worker_result)
// (task_id, worker_result)
let worker = {
let worker = {
cfg_if ! {
cfg_if ! {
if #[ cfg(feature= " progress " ) ] {
if #[ cfg(feature= " progress " ) ] {
let worker = do_work ( & process , & filename , progress . clone ( ) ) ;
let worker = do_work ( & process , & filename , progress . clone ( ) ) ;
let task = progress . add_task ( format! ( "{:?}" , filename . as_ref ( ) ) ) ;
let task = progress . add_task ( format! ( "{:?}" , filename . as_ref ( ) ) ) ;
future ::join ( task , worker ) . await
future ::join ( task , worker ) . await
@ -232,9 +251,23 @@ where I: IntoIterator<Item=T>,
let _ = progress . bump_min ( 1 ) . await ;
let _ = progress . bump_min ( 1 ) . await ;
}
}
} ) , i + = 1 ) . 0
} ) , i + = 1 ) . 0
} ) ) . await
} ) ) ) . await
. map ( | x | x
. into_iter ( )
. into_iter ( )
. filter_map ( | x | x . err ( ) ) ;
. filter_map ( | x | x . err ( ) ) ) ;
let results = match results {
Err ( _ ) = > {
#[ cfg(feature= " progress " ) ] {
progress . eprintln ( "[!] Child aborting..." ) . await ? . await ? ;
progress . clear_tasks ( true , Some ( "Waiting for existing..." . to_string ( ) ) ) . await ? ;
}
#[ cfg(not(feature= " progress " )) ] eprintln! ( "[!] Child aborting..." ) ;
todo! ( "XXX: How to actually implement this? Add global mutexed counter in `Process` itself to track them and optionally await on them? We have prevented any more from spawning, but how do we wait for the ones that already are (which is the whole point of this.)" ) ;
} ,
Ok ( v ) = > v ,
} ;
#[ cfg(feature= " progress " ) ] progress . shutdown ( ) . await ? ;
#[ cfg(feature= " progress " ) ] progress . shutdown ( ) . await ? ;
for failed in results
for failed in results