1
- // Copyright 2012 The Rust Project Developers. See the COPYRIGHT
1
+ // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2
2
// file at the top-level directory of this distribution and at
3
3
// http://rust-lang.org/COPYRIGHT.
4
4
//
@@ -44,13 +44,13 @@ pub trait Program {
44
44
/// Returns the process id of the program
45
45
fn get_id ( & mut self ) -> pid_t ;
46
46
47
- /// Returns an io::writer that can be used to write to stdin
47
+ /// Returns an io::Writer that can be used to write to stdin
48
48
fn input ( & mut self ) -> @io:: Writer ;
49
49
50
- /// Returns an io::reader that can be used to read from stdout
50
+ /// Returns an io::Reader that can be used to read from stdout
51
51
fn output ( & mut self ) -> @io:: Reader ;
52
52
53
- /// Returns an io::reader that can be used to read from stderr
53
+ /// Returns an io::Reader that can be used to read from stderr
54
54
fn err ( & mut self ) -> @io:: Reader ;
55
55
56
56
/// Closes the handle to the child processes standard input
@@ -62,8 +62,23 @@ pub trait Program {
62
62
*/
63
63
fn finish ( & mut self ) -> int ;
64
64
65
- /// Closes open handles
65
+ /**
66
+ * Terminate the program, giving it a chance to clean itself up if
67
+ * this is supported by the operating system.
68
+ *
69
+ * On Posix OSs SIGTERM will be sent to the process. On Win32
70
+ * TerminateProcess(..) will be called.
71
+ */
66
72
fn destroy ( & mut self ) ;
73
+
74
+ /**
75
+ * Terminate the program as soon as possible without giving it a
76
+ * chance to clean itself up.
77
+ *
78
+ * On Posix OSs SIGKILL will be sent to the process. On Win32
79
+ * TerminateProcess(..) will be called.
80
+ */
81
+ fn force_destroy ( & mut self ) ;
67
82
}
68
83
69
84
@@ -172,6 +187,14 @@ fn with_dirp<T>(d: &Option<~str>,
172
187
}
173
188
}
174
189
190
+ /// helper function that closes non-NULL files and then makes them NULL
191
+ priv unsafe fn fclose_and_null ( f : & mut * libc:: FILE ) {
192
+ if * f != 0 as * libc:: FILE {
193
+ libc:: fclose ( * f) ;
194
+ * f = 0 as * libc:: FILE ;
195
+ }
196
+ }
197
+
175
198
/**
176
199
* Spawns a process and waits for it to terminate
177
200
*
@@ -192,9 +215,9 @@ pub fn run_program(prog: &str, args: &[~str]) -> int {
192
215
}
193
216
194
217
/**
195
- * Spawns a process and returns a program
218
+ * Spawns a process and returns a Program
196
219
*
197
- * The returned value is a boxed class containing a <program > object that can
220
+ * The returned value is a boxed class containing a <Program > object that can
198
221
* be used for sending and receiving data over the standard file descriptors.
199
222
* The class will ensure that file descriptors are closed properly.
200
223
*
@@ -240,28 +263,59 @@ pub fn start_program(prog: &str, args: &[~str]) -> @Program {
240
263
r. in_fd = invalid_fd;
241
264
}
242
265
}
266
+
267
+ fn close_repr_outputs ( r : & mut ProgRepr ) {
268
+ unsafe {
269
+ fclose_and_null ( & mut r. out_file ) ;
270
+ fclose_and_null ( & mut r. err_file ) ;
271
+ }
272
+ }
273
+
243
274
fn finish_repr ( r : & mut ProgRepr ) -> int {
244
275
if r. finished { return 0 ; }
245
276
r. finished = true ;
246
277
close_repr_input ( & mut * r) ;
247
278
return waitpid ( r. pid ) ;
248
279
}
249
- fn destroy_repr ( r : & mut ProgRepr ) {
250
- unsafe {
251
- finish_repr ( & mut * r) ;
252
- libc:: fclose ( r. out_file ) ;
253
- libc:: fclose ( r. err_file ) ;
280
+
281
+ fn destroy_repr ( r : & mut ProgRepr , force : bool ) {
282
+ killpid ( r. pid , force) ;
283
+ finish_repr ( & mut * r) ;
284
+ close_repr_outputs ( & mut * r) ;
285
+
286
+ #[ cfg( windows) ]
287
+ fn killpid ( pid : pid_t , _force : bool ) {
288
+ unsafe {
289
+ libc:: funcs:: extra:: kernel32:: TerminateProcess (
290
+ cast:: transmute ( pid) , 1 ) ;
291
+ }
292
+ }
293
+
294
+ #[ cfg( unix) ]
295
+ fn killpid ( pid : pid_t , force : bool ) {
296
+
297
+ let signal = if force {
298
+ libc:: consts:: os:: posix88:: SIGKILL
299
+ } else {
300
+ libc:: consts:: os:: posix88:: SIGTERM
301
+ } ;
302
+
303
+ unsafe {
304
+ libc:: funcs:: posix88:: signal:: kill ( pid, signal as c_int ) ;
305
+ }
254
306
}
255
307
}
308
+
256
309
struct ProgRes {
257
310
r : ProgRepr ,
258
311
}
259
312
260
313
impl Drop for ProgRes {
261
314
fn finalize ( & self ) {
262
315
unsafe {
263
- // FIXME #4943: This is bad.
264
- destroy_repr ( cast:: transmute ( & self . r ) ) ;
316
+ // FIXME #4943: transmute is bad.
317
+ finish_repr ( cast:: transmute ( & self . r ) ) ;
318
+ close_repr_outputs ( cast:: transmute ( & self . r ) ) ;
265
319
}
266
320
}
267
321
}
@@ -285,8 +339,10 @@ pub fn start_program(prog: &str, args: &[~str]) -> @Program {
285
339
}
286
340
fn close_input ( & mut self ) { close_repr_input ( & mut self . r ) ; }
287
341
fn finish ( & mut self ) -> int { finish_repr ( & mut self . r ) }
288
- fn destroy ( & mut self ) { destroy_repr ( & mut self . r ) ; }
342
+ fn destroy ( & mut self ) { destroy_repr ( & mut self . r , false ) ; }
343
+ fn force_destroy ( & mut self ) { destroy_repr ( & mut self . r , true ) ; }
289
344
}
345
+
290
346
let mut repr = ProgRepr {
291
347
pid : pid,
292
348
in_fd : pipe_input. out ,
@@ -458,8 +514,10 @@ pub fn waitpid(pid: pid_t) -> int {
458
514
459
515
#[ cfg( test) ]
460
516
mod tests {
517
+ use libc;
461
518
use option:: None ;
462
519
use os;
520
+ use path:: Path ;
463
521
use run:: { readclose, writeclose} ;
464
522
use run;
465
523
@@ -507,6 +565,50 @@ mod tests {
507
565
assert ! ( status == 1 ) ;
508
566
}
509
567
568
+ #[ test]
569
+ pub fn test_destroy_once ( ) {
570
+ let mut p = run:: start_program ( "echo" , [ ] ) ;
571
+ p. destroy ( ) ; // this shouldn't crash (and nor should the destructor)
572
+ }
573
+
574
+ #[ test]
575
+ pub fn test_destroy_twice ( ) {
576
+ let mut p = run:: start_program ( "echo" , [ ] ) ;
577
+ p. destroy ( ) ; // this shouldnt crash...
578
+ p. destroy ( ) ; // ...and nor should this (and nor should the destructor)
579
+ }
580
+
581
+ #[ cfg( unix) ] // there is no way to sleep on windows from inside libcore...
582
+ pub fn test_destroy_actually_kills ( force : bool ) {
583
+ let path = Path ( fmt ! ( "test/core-run-test-destroy-actually-kills-%?.tmp" , force) ) ;
584
+
585
+ os:: remove_file ( & path) ;
586
+
587
+ let cmd = fmt ! ( "sleep 5 && echo MurderDeathKill > %s" , path. to_str( ) ) ;
588
+ let mut p = run:: start_program ( "sh" , [ ~"-c" , cmd] ) ;
589
+
590
+ p. destroy ( ) ; // destroy the program before it has a chance to echo its message
591
+
592
+ unsafe {
593
+ // wait to ensure the program is really destroyed and not just waiting itself
594
+ libc:: sleep ( 10 ) ;
595
+ }
596
+
597
+ // the program should not have had chance to echo its message
598
+ assert ! ( !path. exists( ) ) ;
599
+ }
600
+
601
+ #[ test]
602
+ #[ cfg( unix) ]
603
+ pub fn test_unforced_destroy_actually_kills ( ) {
604
+ test_destroy_actually_kills ( false ) ;
605
+ }
606
+
607
+ #[ test]
608
+ #[ cfg( unix) ]
609
+ pub fn test_forced_destroy_actually_kills ( ) {
610
+ test_destroy_actually_kills ( true ) ;
611
+ }
510
612
}
511
613
512
614
// Local Variables:
0 commit comments