@@ -1013,34 +1013,117 @@ pub fn process_run_stdout(command: &str, args: &[String]) -> Result<String, Stri
10131013 let output = child
10141014 . output ( )
10151015 . map_err ( |error| format ! ( "failed to run `{command}`: {error}" ) ) ?;
1016+ process_output_result ( command, output)
1017+ }
1018+
1019+ pub fn process_run_stdout_timeout (
1020+ command : & str ,
1021+ args : & [ String ] ,
1022+ timeout_ms : i64 ,
1023+ ) -> Result < String , String > {
1024+ if timeout_ms <= 0 {
1025+ return process_run_stdout ( command, args) ;
1026+ }
1027+
1028+ let timeout_ms = u64:: try_from ( timeout_ms) . unwrap_or ( 0 ) ;
1029+ let deadline = Instant :: now ( ) + Duration :: from_millis ( timeout_ms) ;
1030+ let mut child = std:: process:: Command :: new ( command) ;
1031+ child
1032+ . args ( args)
1033+ . stdout ( std:: process:: Stdio :: piped ( ) )
1034+ . stderr ( std:: process:: Stdio :: piped ( ) ) ;
1035+ if std:: env:: var_os ( "RSSCRIPT_RAMDISK_PATH" ) . is_none ( )
1036+ && let Some ( path) = default_ramdisk_root_dir ( )
1037+ {
1038+ child. env ( "RSSCRIPT_RAMDISK_PATH" , path) ;
1039+ }
1040+ let mut child = child
1041+ . spawn ( )
1042+ . map_err ( |error| format ! ( "failed to run `{command}`: {error}" ) ) ?;
1043+ loop {
1044+ if child
1045+ . try_wait ( )
1046+ . map_err ( |error| format ! ( "failed to poll `{command}`: {error}" ) ) ?
1047+ . is_some ( )
1048+ {
1049+ let output = child
1050+ . wait_with_output ( )
1051+ . map_err ( |error| format ! ( "failed to collect `{command}` output: {error}" ) ) ?;
1052+ return process_output_result ( command, output) ;
1053+ }
1054+ let now = Instant :: now ( ) ;
1055+ if now >= deadline {
1056+ let _ = child. kill ( ) ;
1057+ let output = child
1058+ . wait_with_output ( )
1059+ . map_err ( |error| format ! ( "failed to collect `{command}` output: {error}" ) ) ?;
1060+ return Err ( format ! (
1061+ "`{command}` timed out after {timeout_ms}ms: {}" ,
1062+ process_output_details( & output)
1063+ ) ) ;
1064+ }
1065+ std:: thread:: sleep ( ( deadline - now) . min ( Duration :: from_millis ( 10 ) ) ) ;
1066+ }
1067+ }
1068+
1069+ fn process_output_result ( command : & str , output : std:: process:: Output ) -> Result < String , String > {
10161070 let stdout = String :: from_utf8_lossy ( & output. stdout ) . to_string ( ) ;
10171071 if output. status . success ( ) {
10181072 return Ok ( stdout) ;
10191073 }
10201074
1075+ let code = output
1076+ . status
1077+ . code ( )
1078+ . map ( |code| code. to_string ( ) )
1079+ . unwrap_or_else ( || "signal" . to_string ( ) ) ;
1080+ Err ( format ! (
1081+ "`{command}` exited with {code}: {}" ,
1082+ process_output_details( & output)
1083+ ) )
1084+ }
1085+
1086+ fn process_output_details ( output : & std:: process:: Output ) -> String {
1087+ let stdout = String :: from_utf8_lossy ( & output. stdout ) ;
10211088 let stderr = String :: from_utf8_lossy ( & output. stderr ) ;
10221089 let stdout = stdout. trim ( ) ;
10231090 let stderr = stderr. trim ( ) ;
1024- let details = if stdout. is_empty ( ) {
1091+ if stdout. is_empty ( ) {
10251092 stderr. to_string ( )
10261093 } else if stderr. is_empty ( ) {
10271094 stdout. to_string ( )
10281095 } else {
10291096 format ! ( "{stdout}\n {stderr}" )
1030- } ;
1031- let code = output
1032- . status
1033- . code ( )
1034- . map ( |code| code. to_string ( ) )
1035- . unwrap_or_else ( || "signal" . to_string ( ) ) ;
1036- Err ( format ! ( "`{command}` exited with {code}: {details}" ) )
1097+ }
10371098}
10381099
10391100pub fn process_run_many_stdout (
10401101 command : & str ,
10411102 args : & [ String ] ,
10421103 appended_args : & [ String ] ,
10431104 jobs : i64 ,
1105+ ) -> Result < Vec < String > , String > {
1106+ process_run_many_stdout_with_runner ( command, args, appended_args, jobs, process_run_stdout)
1107+ }
1108+
1109+ pub fn process_run_many_stdout_timeout (
1110+ command : & str ,
1111+ args : & [ String ] ,
1112+ appended_args : & [ String ] ,
1113+ jobs : i64 ,
1114+ timeout_ms : i64 ,
1115+ ) -> Result < Vec < String > , String > {
1116+ process_run_many_stdout_with_runner ( command, args, appended_args, jobs, |command, args| {
1117+ process_run_stdout_timeout ( command, args, timeout_ms)
1118+ } )
1119+ }
1120+
1121+ fn process_run_many_stdout_with_runner (
1122+ command : & str ,
1123+ args : & [ String ] ,
1124+ appended_args : & [ String ] ,
1125+ jobs : i64 ,
1126+ runner : impl Fn ( & str , & [ String ] ) -> Result < String , String > + Send + Sync ,
10441127) -> Result < Vec < String > , String > {
10451128 if appended_args. is_empty ( ) {
10461129 return Ok ( Vec :: new ( ) ) ;
@@ -1056,6 +1139,7 @@ pub fn process_run_many_stdout(
10561139 let errors = std:: sync:: Arc :: new ( std:: sync:: Mutex :: new ( Vec :: < String > :: new ( ) ) ) ;
10571140 let args = std:: sync:: Arc :: new ( args. to_vec ( ) ) ;
10581141 let appended_args = std:: sync:: Arc :: new ( appended_args. to_vec ( ) ) ;
1142+ let runner = & runner;
10591143
10601144 std:: thread:: scope ( |scope| {
10611145 for _ in 0 ..worker_count {
@@ -1072,7 +1156,7 @@ pub fn process_run_many_stdout(
10721156 } ;
10731157 let mut command_args = ( * args) . clone ( ) ;
10741158 command_args. push ( appended_arg. clone ( ) ) ;
1075- match process_run_stdout ( command, & command_args) {
1159+ match runner ( command, & command_args) {
10761160 Ok ( stdout) => {
10771161 if let Ok ( mut result) = results[ index] . lock ( ) {
10781162 * result = Some ( stdout) ;
0 commit comments