char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
f4a0f5b
main
1use std:: io; 2use std:: sync:: Mutex ; 3use std:: time::{ Duration , Instant }; 4 5use crate ::{ Child , Group , Launch }; 6 7pub ( crate ) struct Escalation { 8steps : Vec <( String , Duration )>, 9next : usize , 10deadline : Option < Instant >, 11pub forced : bool , 12} 13impl Escalation { 14pub fn new ( mut steps : Vec <( String , Duration )>) ->Self { 15 steps. push (( "SIGKILL" . into (), Duration :: from_secs ( 5 ))); 16Self { 17 steps, 18next : 0 , 19deadline : None , 20forced : false , 21} 22} 23pub fn cancelled ( & self ) ->bool { 24self . deadline . is_some () 25} 26pub fn request ( & mut self , force : bool ) ->bool { 27let changed = !self . cancelled () ||( force && !self . forced ); 28self . deadline . get_or_insert_with ( Instant :: now); 29self . forced |= force; 30 changed 31} 32pub fn advance ( & mut self , mut signal : impl FnMut ( & str ) -> io:: Result < bool >) -> io:: Result <()> { 33let Some ( mut deadline) =self . deadline else { 34return Ok (()); 35}; 36let mut error =None ; 37if self . forced { 38self . next =self . next . max ( self . steps . len () -1 ); 39} 40while self . next <self . steps . len () &&( self . forced ||Instant :: now () >= deadline) { 41let ( name, grace) =& self . steps [ self . next ]; 42if let Err ( e) =signal ( name) { 43 error. get_or_insert ( e); 44} 45self . next +=1 ; 46 deadline =Instant :: now () +* grace; 47} 48self . deadline =Some ( deadline); 49 error. map_or ( Ok (()), Err ) 50} 51fn expired ( & self ) ->bool { 52self . next ==self . steps . len () &&self . deadline . is_some_and ( |at|Instant :: now () >= at) 53} 54} 55 56/// An explicitly retained scope: command exits do not end descendant ownership. 57/// Cancellation serializes with both launching and `if_running` publication. 58pub struct Scope { 59group : Group , 60stop : Mutex < Escalation >, 61} 62impl Scope { 63pub fn new ( host : & std:: path:: Path , steps : Vec <( String , Duration )>) -> io:: Result < Self > { 64Ok ( Self { 65group : Group :: new ( host) ?, 66stop : Mutex :: new ( Escalation :: new ( steps)), 67}) 68} 69pub fn is_cancelled ( & self ) ->bool { 70self . stop . lock (). unwrap (). cancelled () 71} 72pub fn if_running < T >( & self , action : impl FnOnce () ->T ) ->Option < T > { 73let stop =self . stop . lock (). unwrap (); 74if stop. cancelled () { 75None 76} else { 77Some ( action ()) 78} 79} 80pub ( crate ) fn spawn ( & self , launch : Launch < ' _ >) -> io:: Result < Child > { 81let stop =self . stop . lock (). unwrap (); 82if stop. cancelled () { 83return Err ( io:: Error :: new ( 84 io:: ErrorKind :: Interrupted , 85"Task run was cancelled" , 86)); 87} 88crate :: spawn ( Launch { 89join : Some ( & self . group ), 90group : false , 91 ..launch 92}) 93} 94pub fn cancel ( & self ) { 95self . stop . lock (). unwrap (). request ( false ); 96} 97/// Bounded, resumable shutdown. The caller can include its own work in settling. 98pub fn terminate ( & self , settled : impl Fn () ->bool ) ->bool { 99self . cancel (); 100loop { 101{ 102let mut stop =self . stop . lock (). unwrap (); 103if !self . group . alive () &&settled () { 104self . group . close (); 105return true ; 106} 107let _ = stop. advance ( |signal|self . group . signal ( signal)); 108if stop. expired () { 109return false ; 110} 111} 112 std:: thread:: sleep ( Duration :: from_millis ( 5 )); 113} 114} 115pub fn force_close ( & self ) { 116self . stop . lock (). unwrap (). request ( true ); 117self . group . close (); 118} 119}