@@ -227,7 +227,19 @@ pub trait FileDescription: std::fmt::Debug + FileDescriptionExt {
227227 }
228228}
229229
230- impl FileDescription for io:: Stdin {
230+ #[ derive( Debug ) ]
231+ struct Stdin {
232+ stdin : io:: Stdin ,
233+ watched : ReadinessWatched ,
234+ }
235+
236+ impl Stdin {
237+ fn new ( ) -> Self {
238+ Self { stdin : io:: stdin ( ) , watched : ReadinessWatched :: default ( ) }
239+ }
240+ }
241+
242+ impl FileDescription for Stdin {
231243 fn name ( & self ) -> & ' static str {
232244 "stdin"
233245 }
@@ -245,17 +257,42 @@ impl FileDescription for io::Stdin {
245257 helpers:: isolation_abort_error ( "`read` from stdin" ) ?;
246258 }
247259
248- let mut stdin = & * self ;
249- let result = ecx. read_from_host ( |buf| stdin. read ( buf) , len, ptr) ?;
260+ // FIXME: this can block on the host, halting the entire interpreter.
261+ let result = ecx. read_from_host ( |buf| ( & mut & self . stdin ) . read ( buf) , len, ptr) ?;
250262 finish. call ( ecx, result)
251263 }
252264
253265 fn is_tty ( & self , communicate_allowed : bool ) -> bool {
254- communicate_allowed && self . is_terminal ( )
266+ communicate_allowed && self . stdin . is_terminal ( )
267+ }
268+
269+ fn readiness_watched ( & self ) -> Option < & ReadinessWatched > {
270+ Some ( & self . watched )
255271 }
272+
273+ fn readiness ( & self ) -> Readiness {
274+ // Stdin is readable (we never return EWOULDBLOCK above) and also writable (since that never
275+ // blocks either). This matches what we see on Linux.
276+ let mut readiness = Readiness :: EMPTY ;
277+ readiness. readable = true ;
278+ readiness. writable = true ;
279+ readiness
280+ }
281+ }
282+
283+ #[ derive( Debug ) ]
284+ struct Stdout {
285+ stdout : io:: Stdout ,
286+ watched : ReadinessWatched ,
256287}
257288
258- impl FileDescription for io:: Stdout {
289+ impl Stdout {
290+ fn new ( ) -> Self {
291+ Self { stdout : io:: stdout ( ) , watched : ReadinessWatched :: default ( ) }
292+ }
293+ }
294+
295+ impl FileDescription for Stdout {
259296 fn name ( & self ) -> & ' static str {
260297 "stdout"
261298 }
@@ -269,7 +306,7 @@ impl FileDescription for io::Stdout {
269306 finish : DynMachineCallback < ' tcx , Result < usize , IoError > > ,
270307 ) -> InterpResult < ' tcx > {
271308 // We allow writing to stdout even with isolation enabled.
272- let result = ecx. write_to_host ( & * self , len, ptr) ?;
309+ let result = ecx. write_to_host ( & self . stdout , len, ptr) ?;
273310 // Stdout is buffered, flush to make sure it appears on the
274311 // screen. This is the write() syscall of the interpreted
275312 // program, we want it to correspond to a write() syscall on
@@ -281,11 +318,34 @@ impl FileDescription for io::Stdout {
281318 }
282319
283320 fn is_tty ( & self , communicate_allowed : bool ) -> bool {
284- communicate_allowed && self . is_terminal ( )
321+ communicate_allowed && self . stdout . is_terminal ( )
322+ }
323+
324+ fn readiness_watched ( & self ) -> Option < & ReadinessWatched > {
325+ Some ( & self . watched )
285326 }
327+
328+ fn readiness ( & self ) -> Readiness {
329+ // stdout can always be written (we never return EWOULDBLOCK there) and never be read.
330+ let mut readiness = Readiness :: EMPTY ;
331+ readiness. writable = true ;
332+ readiness
333+ }
334+ }
335+
336+ #[ derive( Debug ) ]
337+ struct Stderr {
338+ stderr : io:: Stderr ,
339+ watched : ReadinessWatched ,
286340}
287341
288- impl FileDescription for io:: Stderr {
342+ impl Stderr {
343+ fn new ( ) -> Self {
344+ Self { stderr : io:: stderr ( ) , watched : ReadinessWatched :: default ( ) }
345+ }
346+ }
347+
348+ impl FileDescription for Stderr {
289349 fn name ( & self ) -> & ' static str {
290350 "stderr"
291351 }
@@ -299,13 +359,65 @@ impl FileDescription for io::Stderr {
299359 finish : DynMachineCallback < ' tcx , Result < usize , IoError > > ,
300360 ) -> InterpResult < ' tcx > {
301361 // We allow writing to stderr even with isolation enabled.
302- let result = ecx. write_to_host ( & * self , len, ptr) ?;
362+ let result = ecx. write_to_host ( & self . stderr , len, ptr) ?;
303363 // No need to flush, stderr is not buffered.
304364 finish. call ( ecx, result)
305365 }
306366
307367 fn is_tty ( & self , communicate_allowed : bool ) -> bool {
308- communicate_allowed && self . is_terminal ( )
368+ communicate_allowed && self . stderr . is_terminal ( )
369+ }
370+
371+ fn readiness_watched ( & self ) -> Option < & ReadinessWatched > {
372+ Some ( & self . watched )
373+ }
374+
375+ fn readiness ( & self ) -> Readiness {
376+ // stderr can always be written (we never return EWOULDBLOCK there) and never be read.
377+ let mut readiness = Readiness :: EMPTY ;
378+ readiness. writable = true ;
379+ readiness
380+ }
381+ }
382+
383+ /// Like /dev/null
384+ #[ derive( Debug ) ]
385+ pub struct NullOutput {
386+ watched : ReadinessWatched ,
387+ }
388+
389+ impl NullOutput {
390+ fn new ( ) -> Self {
391+ Self { watched : ReadinessWatched :: default ( ) }
392+ }
393+ }
394+
395+ impl FileDescription for NullOutput {
396+ fn name ( & self ) -> & ' static str {
397+ "null output"
398+ }
399+
400+ fn write < ' tcx > (
401+ self : FileDescriptionRef < Self > ,
402+ _communicate_allowed : bool ,
403+ _ptr : Pointer ,
404+ len : usize ,
405+ ecx : & mut MiriInterpCx < ' tcx > ,
406+ finish : DynMachineCallback < ' tcx , Result < usize , IoError > > ,
407+ ) -> InterpResult < ' tcx > {
408+ // We just don't write anything, but report to the user that we did.
409+ finish. call ( ecx, Ok ( len) )
410+ }
411+
412+ fn readiness_watched ( & self ) -> Option < & ReadinessWatched > {
413+ Some ( & self . watched )
414+ }
415+
416+ fn readiness ( & self ) -> Readiness {
417+ // null output can always be written (we never return EWOULDBLOCK there) and never be read.
418+ let mut readiness = Readiness :: EMPTY ;
419+ readiness. writable = true ;
420+ readiness
309421 }
310422}
311423
@@ -416,28 +528,6 @@ impl FileDescription for DirHandle {
416528 }
417529}
418530
419- /// Like /dev/null
420- #[ derive( Debug ) ]
421- pub struct NullOutput ;
422-
423- impl FileDescription for NullOutput {
424- fn name ( & self ) -> & ' static str {
425- "stderr and stdout"
426- }
427-
428- fn write < ' tcx > (
429- self : FileDescriptionRef < Self > ,
430- _communicate_allowed : bool ,
431- _ptr : Pointer ,
432- len : usize ,
433- ecx : & mut MiriInterpCx < ' tcx > ,
434- finish : DynMachineCallback < ' tcx , Result < usize , IoError > > ,
435- ) -> InterpResult < ' tcx > {
436- // We just don't write anything, but report to the user that we did.
437- finish. call ( ecx, Ok ( len) )
438- }
439- }
440-
441531/// Internal type of a file-descriptor - this is what [`FdTable`] expects
442532pub type FdNum = i32 ;
443533
@@ -461,13 +551,13 @@ impl FdTable {
461551 }
462552 pub ( crate ) fn init ( mute_stdout_stderr : bool ) -> FdTable {
463553 let mut fds = FdTable :: new ( ) ;
464- fds. insert_new ( io :: stdin ( ) ) ;
554+ fds. insert_new ( Stdin :: new ( ) ) ;
465555 if mute_stdout_stderr {
466- assert_eq ! ( fds. insert_new( NullOutput ) , 1 ) ;
467- assert_eq ! ( fds. insert_new( NullOutput ) , 2 ) ;
556+ assert_eq ! ( fds. insert_new( NullOutput :: new ( ) ) , 1 ) ;
557+ assert_eq ! ( fds. insert_new( NullOutput :: new ( ) ) , 2 ) ;
468558 } else {
469- assert_eq ! ( fds. insert_new( io :: stdout ( ) ) , 1 ) ;
470- assert_eq ! ( fds. insert_new( io :: stderr ( ) ) , 2 ) ;
559+ assert_eq ! ( fds. insert_new( Stdout :: new ( ) ) , 1 ) ;
560+ assert_eq ! ( fds. insert_new( Stderr :: new ( ) ) , 2 ) ;
471561 }
472562 fds
473563 }
0 commit comments