11use std:: cell:: RefCell ;
2- use std:: collections:: BTreeMap ;
2+ use std:: collections:: { BTreeMap , VecDeque } ;
33use std:: io;
44use std:: time:: Duration ;
55
@@ -22,8 +22,8 @@ struct Epoll {
2222 /// "ready" list; instead, a boolean flag in this list tracks which subset is ready. This makes
2323 /// `epoll_wait` less efficient, but also requires less bookkeeping.
2424 interest_list : RefCell < BTreeMap < EpollEventKey , EpollEventInterest > > ,
25- /// A list of thread ids blocked on this epoll instance.
26- blocked : RefCell < Vec < ThreadId > > ,
25+ /// The queue of threads blocked on this epoll instance, and how many events they'd like to get .
26+ queue : RefCell < VecDeque < ( ThreadId , u32 ) > > ,
2727}
2828
2929impl VisitProvenance for Epoll {
@@ -459,7 +459,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
459459 }
460460 } ;
461461 // Record this thread as blocked.
462- epfd. blocked . borrow_mut ( ) . push ( this. active_thread ( ) ) ;
462+ epfd. queue
463+ . borrow_mut ( )
464+ . push_back ( ( this. active_thread ( ) , maxevents. try_into ( ) . unwrap ( ) ) ) ;
463465 // And block it.
464466 let dest = dest. clone ( ) ;
465467 // We keep a strong ref to the underlying `Epoll` to make sure it sticks around.
@@ -477,14 +479,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
477479 |this, unblock: UnblockKind | {
478480 match unblock {
479481 UnblockKind :: Ready => {
480- return_ready_list( & epfd, & dest, & event, this) ?;
482+ let events = return_ready_list( & epfd, & dest, & event, this) ?;
483+ assert!( events > 0 , "we got woken up with no events to deliver" ) ;
481484 interp_ok( ( ) )
482485 } ,
483486 UnblockKind :: TimedOut => {
484487 // Remove the current active thread_id from the blocked thread_id list.
485488 epfd
486- . blocked . borrow_mut( )
487- . retain( |& id | id != this. active_thread( ) ) ;
489+ . queue . borrow_mut( )
490+ . retain( |& ( id , _events ) | id != this. active_thread( ) ) ;
488491 this. write_int( 0 , & dest) ?;
489492 interp_ok( ( ) )
490493 } ,
@@ -550,7 +553,7 @@ fn update_readiness<'tcx>(
550553 & mut dyn FnMut ( & mut EpollEventInterest ) -> InterpResult < ' tcx > ,
551554 ) -> InterpResult < ' tcx > ,
552555) -> InterpResult < ' tcx > {
553- let mut wakeup = false ;
556+ let mut num_ready = 0u32 ; // how many events we have ready to deliver
554557 for_each_interest ( & mut |interest| {
555558 // Update the ready events tracked in this interest.
556559 let new_readiness = interest. relevant_events & active_events;
@@ -565,19 +568,29 @@ fn update_readiness<'tcx>(
565568 ecx. release_clock ( |clock| {
566569 interest. clock . join ( clock) ;
567570 } ) ?;
568- wakeup = true ;
571+ num_ready = num_ready . saturating_add ( 1 ) ;
569572 }
570573 interp_ok ( ( ) )
571574 } ) ?;
572- if wakeup {
573- // Wake up threads that may have been waiting for events on this epoll.
574- // Do this only once for all the interests.
575- // Edge-triggered notification only notify one thread even if there are
576- // multiple threads blocked on the same epoll.
577- if let Some ( thread_id) = epoll. blocked . borrow_mut ( ) . pop ( ) {
578- ecx. unblock_thread ( thread_id, BlockReason :: Epoll ) ?;
575+ // Edge-triggered notifications only wake up as many threads as are needed to deliver
576+ // all the events.
577+ while num_ready > 0
578+ && let Some ( ( thread_id, events) ) = epoll. queue . borrow_mut ( ) . pop_front ( )
579+ {
580+ ecx. unblock_thread ( thread_id, BlockReason :: Epoll ) ?;
581+ // Keep track of how many events we have left to deliver (except if we saturated;
582+ // in that case we just wake up everybody).
583+ if num_ready != u32:: MAX {
584+ num_ready = num_ready. saturating_sub ( events) ;
579585 }
580586 }
587+ // Sanity-check: if there are threads left to wake up, then there are no more ready events.
588+ if !epoll. queue . borrow ( ) . is_empty ( ) {
589+ assert ! (
590+ epoll. interest_list. borrow( ) . values( ) . all( |i| !i. ready) ,
591+ "there are unconsumed ready events and threads ready to take them"
592+ ) ;
593+ }
581594
582595 interp_ok ( ( ) )
583596}
@@ -589,7 +602,7 @@ fn return_ready_list<'tcx>(
589602 dest : & MPlaceTy < ' tcx > ,
590603 events : & MPlaceTy < ' tcx > ,
591604 ecx : & mut MiriInterpCx < ' tcx > ,
592- ) -> InterpResult < ' tcx > {
605+ ) -> InterpResult < ' tcx , i32 > {
593606 let mut interest_list = epfd. interest_list . borrow_mut ( ) ;
594607 let mut num_of_events: i32 = 0 ;
595608 let mut array_iter = ecx. project_array_fields ( events) ?;
@@ -629,5 +642,5 @@ fn return_ready_list<'tcx>(
629642 }
630643 }
631644 ecx. write_int ( num_of_events, dest) ?;
632- interp_ok ( ( ) )
645+ interp_ok ( num_of_events )
633646}
0 commit comments