@@ -2,6 +2,7 @@ use std::cell::RefCell;
22use std:: collections:: BTreeMap ;
33use std:: io;
44use std:: rc:: { Rc , Weak } ;
5+ use std:: time:: Duration ;
56
67use crate :: shims:: unix:: fd:: FdId ;
78use crate :: shims:: unix:: * ;
@@ -378,30 +379,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
378379 events_op : & OpTy < ' tcx > ,
379380 maxevents : & OpTy < ' tcx > ,
380381 timeout : & OpTy < ' tcx > ,
381- place : & MPlaceTy < ' tcx > ,
382+ place : & ' tcx MPlaceTy < ' tcx > ,
382383 ) -> InterpResult < ' tcx > {
383384 let this = self . eval_context_mut ( ) ;
384385
385- let epfd = this. read_scalar ( epfd) ?. to_i32 ( ) ?;
386+ let epfd_value = this. read_scalar ( epfd) ?. to_i32 ( ) ?;
386387 let maxevents = this. read_scalar ( maxevents) ?. to_i32 ( ) ?;
387388 let event = this. deref_pointer_as (
388389 events_op,
389390 this. libc_array_ty_layout ( "epoll_event" , maxevents. try_into ( ) . unwrap ( ) ) ,
390391 ) ?;
391- let timeout = this. read_scalar ( timeout) ?. to_i32 ( ) ?;
392+ let timeout = this. read_scalar ( timeout) ?. to_u32 ( ) ?;
392393
393- if epfd <= 0 {
394+ if epfd_value <= 0 {
394395 let einval = this. eval_libc ( "EINVAL" ) ;
395396 this. set_last_error ( einval) ?;
396397 this. write_scalar ( Scalar :: from_i32 ( -1 ) , place) ?;
397398 return Ok ( ( ) ) ;
398399 }
399- // FIXME: Implement blocking support
400- if timeout != 0 {
401- throw_unsup_format ! ( "epoll_wait: timeout value can only be 0" ) ;
402- }
403-
404- let Some ( epfd) = this. machine . fds . get_ref ( epfd) else {
400+ let Some ( epfd) = this. machine . fds . get_ref ( epfd_value) else {
405401 let result_value = this. fd_not_found ( ) ?;
406402 this. write_scalar ( Scalar :: from_i32 ( result_value) , place) ?;
407403 return Ok ( ( ) ) ;
@@ -412,30 +408,42 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
412408 . ok_or_else ( || err_unsup_format ! ( "non-epoll FD passed to `epoll_wait`" ) ) ?;
413409
414410 let binding = epoll_file_description. get_ready_list ( ) ;
415- let mut ready_list = binding. borrow_mut ( ) ;
416- let mut num_of_events: i32 = 0 ;
417- let mut array_iter = this. project_array_fields ( & event) ?;
411+ let ready_list = binding. borrow_mut ( ) ;
418412
419- while let Some ( ( epoll_key, epoll_return) ) = ready_list. pop_first ( ) {
420- // If the file description is fully close, the entry for corresponding FdID in the
421- // global epoll event interest table would be empty.
422- if this. machine . epoll_interests . get_epoll_interest ( epoll_key. 0 ) . is_some ( ) {
423- // Return notification to the caller if the file description is not fully closed.
424- if let Some ( des) = array_iter. next ( this) ? {
425- this. write_int_fields_named (
426- & [
427- ( "events" , epoll_return. events . into ( ) ) ,
428- ( "u64" , epoll_return. data . into ( ) ) ,
429- ] ,
430- & des. 1 ,
431- ) ?;
432- num_of_events = num_of_events. checked_add ( 1 ) . unwrap ( ) ;
433- } else {
434- break ;
435- }
413+ if ready_list. is_empty ( ) {
414+ if timeout == 0 {
415+ // Non-blocking with no notification returned.
416+ this. write_scalar ( Scalar :: from_i32 ( 0 ) , place) ?;
417+ return Ok ( ( ) ) ;
418+ } else {
419+ // Blocking
420+ let duration = Duration :: from_secs ( timeout. into ( ) ) ;
421+ this. block_thread (
422+ BlockReason :: Epoll ,
423+ Some ( ( TimeoutClock :: Monotonic , TimeoutAnchor :: Relative , duration) ) ,
424+ callback ! (
425+ @capture<' tcx> {
426+ epfd_value: i32 ,
427+ place: & ' tcx MPlaceTy <' tcx>,
428+ event: MPlaceTy <' tcx>,
429+ }
430+ @unblock = |this| {
431+ this. blocking_epoll_callback( epfd_value, place, & event) ;
432+ Ok ( ( ) )
433+ }
434+ @timeout = |this| {
435+ this. write_scalar( Scalar :: from_i32( 0 ) , place) ?;
436+ Ok ( ( ) )
437+ }
438+ ) ,
439+ ) ;
436440 }
441+ } else {
442+ // Non-blocking with notification returned.
443+ this. blocking_epoll_callback ( epfd_value, place, & event) ;
444+ return Ok ( ( ) ) ;
437445 }
438- this . write_scalar ( Scalar :: from_i32 ( num_of_events ) , place ) ? ;
446+
439447 Ok ( ( ) )
440448 }
441449 /// For a specific unique file descriptor id, get its ready events and update
@@ -477,4 +485,51 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
477485 }
478486 Ok ( ( ) )
479487 }
488+
489+ /// Callback function after epoll_wait unblocks
490+ fn blocking_epoll_callback (
491+ & mut self ,
492+ epfd : i32 ,
493+ place : & MPlaceTy < ' tcx > ,
494+ event : & MPlaceTy < ' tcx > ,
495+ ) -> InterpResult < ' tcx > {
496+ let this = self . eval_context_mut ( ) ;
497+
498+ let Some ( epfd) = this. machine . fds . get_ref ( epfd) else {
499+ let result_value = this. fd_not_found ( ) ?;
500+ this. write_scalar ( Scalar :: from_i32 ( result_value) , place) ?;
501+ return Ok ( ( ) ) ;
502+ } ;
503+ let mut binding = epfd. borrow_mut ( ) ;
504+ let epoll_file_description = & mut binding
505+ . downcast_mut :: < Epoll > ( )
506+ . ok_or_else ( || err_unsup_format ! ( "non-epoll FD passed to `epoll_wait`" ) ) ?;
507+
508+ let binding = epoll_file_description. get_ready_list ( ) ;
509+ let mut ready_list = binding. borrow_mut ( ) ;
510+ let mut num_of_events: i32 = 0 ;
511+ let mut array_iter = this. project_array_fields ( event) ?;
512+
513+ while let Some ( ( epoll_key, epoll_return) ) = ready_list. pop_first ( ) {
514+ // If the file description is fully close, the entry for corresponding FdID in the
515+ // global epoll event interest table would be empty.
516+ if this. machine . epoll_interests . get_epoll_interest ( epoll_key. 0 ) . is_some ( ) {
517+ // Return notification to the caller if the file description is not fully closed.
518+ if let Some ( des) = array_iter. next ( this) ? {
519+ this. write_int_fields_named (
520+ & [
521+ ( "events" , epoll_return. events . into ( ) ) ,
522+ ( "u64" , epoll_return. data . into ( ) ) ,
523+ ] ,
524+ & des. 1 ,
525+ ) ?;
526+ num_of_events = num_of_events. checked_add ( 1 ) . unwrap ( ) ;
527+ } else {
528+ break ;
529+ }
530+ }
531+ }
532+ this. write_scalar ( Scalar :: from_i32 ( num_of_events) , place) ?;
533+ Ok ( ( ) )
534+ }
480535}
0 commit comments