1
- use std:: process:: Stdio ;
1
+ use std:: {
2
+ process:: Stdio ,
3
+ sync:: { Arc , Mutex } ,
4
+ } ;
2
5
3
6
use process_wrap:: tokio:: { TokioChildWrapper , TokioCommandWrap } ;
4
7
use tokio:: {
@@ -9,6 +12,7 @@ use tokio::{
9
12
use super :: { IntoTransport , Transport } ;
10
13
use crate :: service:: ServiceRole ;
11
14
15
+ const MAX_WAIT_ON_DROP_SECS : u64 = 3 ;
12
16
/// The parts of a child process.
13
17
type ChildProcessParts = (
14
18
Box < dyn TokioChildWrapper > ,
@@ -41,13 +45,35 @@ pub struct TokioChildProcess {
41
45
}
42
46
43
47
pub struct ChildWithCleanup {
44
- inner : Box < dyn TokioChildWrapper > ,
48
+ inner : Arc < Mutex < Option < Box < dyn TokioChildWrapper > > > > ,
45
49
}
46
50
47
51
impl Drop for ChildWithCleanup {
48
52
fn drop ( & mut self ) {
49
- if let Err ( e) = self . inner . start_kill ( ) {
50
- tracing:: warn!( "Failed to kill child process: {e}" ) ;
53
+ // Close child more graceful
54
+ if let Some ( mut child) = self . inner . lock ( ) . unwrap ( ) . take ( ) {
55
+ // Spawn a background task to clean up
56
+ tokio:: spawn ( async move {
57
+ // Wait will drop the stdin
58
+ let wait_fut = Box :: into_pin ( child. wait ( ) ) ;
59
+ tokio:: select! {
60
+ _ = tokio:: time:: sleep( std:: time:: Duration :: from_secs( MAX_WAIT_ON_DROP_SECS ) ) => {
61
+ if let Err ( e) = Box :: into_pin( child. kill( ) ) . await {
62
+ tracing:: warn!( "Error killing child: {e}" ) ;
63
+ }
64
+ } ,
65
+ res = wait_fut => {
66
+ match res {
67
+ Ok ( status) => {
68
+ tracing:: info!( "Child exited gracefully {}" , status) ;
69
+ }
70
+ Err ( e) => {
71
+ tracing:: warn!( "Error waiting for child: {e}" ) ;
72
+ }
73
+ }
74
+ }
75
+ }
76
+ } ) ;
51
77
}
52
78
}
53
79
}
@@ -64,7 +90,7 @@ pin_project_lite::pin_project! {
64
90
impl TokioChildProcessOut {
65
91
/// Get the process ID of the child process.
66
92
pub fn id ( & self ) -> Option < u32 > {
67
- self . child . inner . id ( )
93
+ self . child . inner . lock ( ) . unwrap ( ) . as_ref ( ) ? . inner ( ) . id ( )
68
94
}
69
95
}
70
96
@@ -92,7 +118,7 @@ impl TokioChildProcess {
92
118
93
119
/// Get the process ID of the child process.
94
120
pub fn id ( & self ) -> Option < u32 > {
95
- self . child . inner . id ( )
121
+ self . child . inner . lock ( ) . unwrap ( ) . as_ref ( ) ? . inner ( ) . id ( )
96
122
}
97
123
98
124
/// Split this helper into a reader (stdout) and writer (stdin).
@@ -157,7 +183,9 @@ impl TokioChildProcessBuilder {
157
183
let ( child, stdout, stdin, stderr_opt) = child_process ( self . cmd . spawn ( ) ?) ?;
158
184
159
185
let proc = TokioChildProcess {
160
- child : ChildWithCleanup { inner : child } ,
186
+ child : ChildWithCleanup {
187
+ inner : Arc :: new ( Mutex :: new ( Some ( child) ) ) ,
188
+ } ,
161
189
child_stdin : stdin,
162
190
child_stdout : stdout,
163
191
} ;
@@ -183,3 +211,44 @@ impl ConfigureCommandExt for tokio::process::Command {
183
211
self
184
212
}
185
213
}
214
+
215
+ #[ cfg( unix) ]
216
+ #[ cfg( test) ]
217
+ mod tests {
218
+ use super :: * ;
219
+ use tokio:: process:: Command ;
220
+
221
+ #[ tokio:: test]
222
+ async fn test_tokio_child_process_drop ( ) {
223
+ let r = TokioChildProcess :: new ( Command :: new ( "sleep" ) . configure ( |cmd| {
224
+ cmd. arg ( "30" ) ;
225
+ } ) ) ;
226
+ assert ! ( r. is_ok( ) ) ;
227
+ let child_process = r. unwrap ( ) ;
228
+ let id = child_process. id ( ) ;
229
+ assert ! ( id. is_some( ) ) ;
230
+ let id = id. unwrap ( ) ;
231
+ // Drop the child process
232
+ drop ( child_process) ;
233
+ // Wait a moment to allow the cleanup task to run
234
+ tokio:: time:: sleep ( std:: time:: Duration :: from_secs ( MAX_WAIT_ON_DROP_SECS +1 ) ) . await ;
235
+ // Check if the process is still running
236
+ let status = Command :: new ( "ps" )
237
+ . arg ( "-p" )
238
+ . arg ( id. to_string ( ) )
239
+ . status ( )
240
+ . await ;
241
+ match status {
242
+ Ok ( status) => {
243
+ assert ! (
244
+ !status. success( ) ,
245
+ "Process with PID {} is still running" ,
246
+ id
247
+ ) ;
248
+ }
249
+ Err ( e) => {
250
+ panic ! ( "Failed to check process status: {}" , e) ;
251
+ }
252
+ }
253
+ }
254
+ }
0 commit comments