11use std:: cell:: RefCell ;
2- use std:: collections:: { BTreeMap , BTreeSet , VecDeque } ;
2+ use std:: collections:: { BTreeMap , VecDeque } ;
33use std:: io;
4- use std:: ops:: Bound ;
54use std:: time:: Duration ;
65
76use rustc_abi:: FieldIdx ;
@@ -23,7 +22,9 @@ pub struct Epoll {
2322 interest_list : RefCell < BTreeMap < EpollEventKey , EpollEventInterest > > ,
2423 /// The subset of interests that is currently considered "ready". Stored separately so we
2524 /// can access it more efficiently.
26- ready_set : RefCell < BTreeSet < EpollEventKey > > ,
25+ /// This is implemented as a queue so that for level-triggered epoll, all events eventually
26+ /// get returned from `epoll_wait`. The queue does not contain any duplicates.
27+ ready_events : RefCell < VecDeque < EpollEventKey > > ,
2728 /// The queue of threads blocked on this epoll instance.
2829 queue : RefCell < VecDeque < ThreadId > > ,
2930}
@@ -46,6 +47,9 @@ pub struct EpollEventInterest {
4647 relevant_events : u32 ,
4748 /// The currently active events for this file descriptor.
4849 active_events : u32 ,
50+ /// Boolean whether this is an edge-triggered interest.
51+ /// When [`false`] it's a level-triggered interest instead.
52+ is_edge_triggered : bool ,
4953 /// The vector clock for wakeups.
5054 clock : VClock ,
5155 /// User-defined data associated with this interest.
@@ -203,12 +207,8 @@ impl EpollInterestTable {
203207 . extract_if ( range_for_id ( id) , |_, _| true )
204208 // Consume the iterator.
205209 . for_each ( drop) ;
206- epoll
207- . ready_set
208- . borrow_mut ( )
209- . extract_if ( range_for_id ( id) , |_| true )
210- // Consume the iterator.
211- . for_each ( drop) ;
210+ // Remove the ready events for this file description.
211+ epoll. ready_events . borrow_mut ( ) . retain ( |( fd_id, _) | fd_id != & id) ;
212212 }
213213 }
214214 }
@@ -303,6 +303,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
303303 this. read_scalar ( & this. project_field ( & event, FieldIdx :: ZERO ) ?) ?. to_u32 ( ) ?;
304304 let data = this. read_scalar ( & this. project_field ( & event, FieldIdx :: ONE ) ?) ?. to_u64 ( ) ?;
305305
306+ let is_edge_triggered = if events & epollet == epollet {
307+ events &= !epollet;
308+ true
309+ } else {
310+ false
311+ } ;
312+
306313 // Unset the flag we support to discover if any unsupported flags are used.
307314 let mut flags = events;
308315 // epoll_wait(2) will always wait for epollhup and epollerr; it is not
@@ -311,12 +318,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
311318 events |= epollhup;
312319 events |= epollerr;
313320
314- if events & epollet != epollet {
315- // We only support edge-triggered notification for now.
316- throw_unsup_format ! ( "epoll_ctl: epollet flag must be included." ) ;
317- } else {
318- flags &= !epollet;
319- }
320321 if flags & epollin == epollin {
321322 flags &= !epollin;
322323 }
@@ -350,6 +351,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
350351 }
351352 let new_interest = EpollEventInterest {
352353 relevant_events : events,
354+ is_edge_triggered,
353355 data,
354356 active_events : 0 ,
355357 clock : VClock :: default ( ) ,
@@ -364,6 +366,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
364366 return this. set_errno_and_return_neg1_i32 ( LibcError ( "ENOENT" ) ) ;
365367 } ;
366368 interest. relevant_events = events;
369+ interest. is_edge_triggered = is_edge_triggered;
367370 interest. data = data;
368371 }
369372
@@ -391,7 +394,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
391394 // We did not have interest in this.
392395 return this. set_errno_and_return_neg1_i32 ( LibcError ( "ENOENT" ) ) ;
393396 } ;
394- epfd. ready_set . borrow_mut ( ) . remove ( & epoll_key) ;
397+ // Remove the ready event for this key, should one exist.
398+ let mut ready_events = epfd. ready_events . borrow_mut ( ) ;
399+ if let Some ( idx) = ready_events. iter ( ) . position ( |k| k == & epoll_key) {
400+ ready_events. remove ( idx) ;
401+ }
395402 // If this was the last interest in this FD, remove us from the global list
396403 // of who is interested in this FD.
397404 if interest_list. range ( range_for_id ( id) ) . next ( ) . is_none ( ) {
@@ -469,7 +476,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
469476 return this. set_errno_and_return_neg1 ( LibcError ( "EBADF" ) , dest) ;
470477 } ;
471478
472- if timeout == 0 || !epfd. ready_set . borrow ( ) . is_empty ( ) {
479+ if timeout == 0 || !epfd. ready_events . borrow ( ) . is_empty ( ) {
473480 // If the timeout is 0 or there is a ready event, we can return immediately.
474481 return_ready_list ( & epfd, dest, & event, this) ?;
475482 } else {
@@ -590,31 +597,42 @@ fn update_readiness<'tcx>(
590597 & mut dyn FnMut ( EpollEventKey , & mut EpollEventInterest ) -> InterpResult < ' tcx > ,
591598 ) -> InterpResult < ' tcx > ,
592599) -> InterpResult < ' tcx > {
593- let mut ready_set = epoll. ready_set . borrow_mut ( ) ;
600+ let mut ready_events = epoll. ready_events . borrow_mut ( ) ;
594601 for_each_interest ( & mut |key, interest| {
595602 // Update the ready events tracked in this interest.
596603 let new_readiness = interest. relevant_events & active_events;
597604 let prev_readiness = std:: mem:: replace ( & mut interest. active_events , new_readiness) ;
598605 if new_readiness == 0 {
599606 // Un-trigger this, there's nothing left to report here.
600- ready_set. remove ( & key) ;
607+ if let Some ( idx) = ready_events. iter ( ) . position ( |k| k == & key) {
608+ ready_events. remove ( idx) ;
609+ }
601610 } else if force_edge || new_readiness != prev_readiness & new_readiness {
602- // Either we force an "edge" to be detected, or there's a bit set in `new`
603- // that was not set in `prev`. In both cases, this is ready now.
604- ready_set. insert ( key) ;
611+ // Either we force an "edge" to be detected or there's a bit set in `new_readiness`
612+ // that was not set in `prev_readiness`. In both cases, this is ready now.
613+
614+ // We need to ensure that this event is not already part of the
615+ // `ready_events` queue before enqueueing:
616+ // <https://github.com/torvalds/linux/blob/HEAD/fs/eventpoll.c#L1292-L1296>
617+ if !ready_events. contains ( & key) {
618+ ready_events. push_back ( key) ;
619+ }
620+
621+ // No matter whether this is newly ready or just re-triggered,
622+ // the `epoll_wait` fetching this event should sync with the current thread.
605623 ecx. release_clock ( |clock| {
606624 interest. clock . join ( clock) ;
607625 } ) ?;
608626 }
609627 interp_ok ( ( ) )
610628 } ) ?;
611629 // While there are events ready to be delivered, wake up a thread to receive them.
612- while !ready_set . is_empty ( )
630+ while !ready_events . is_empty ( )
613631 && let Some ( thread_id) = epoll. queue . borrow_mut ( ) . pop_front ( )
614632 {
615- drop ( ready_set ) ; // release the "lock" so the unblocked thread can have it
633+ drop ( ready_events ) ; // release the "lock" so the unblocked thread can have it
616634 ecx. unblock_thread ( thread_id, BlockReason :: Epoll { epfd : epoll. clone ( ) } ) ?;
617- ready_set = epoll. ready_set . borrow_mut ( ) ;
635+ ready_events = epoll. ready_events . borrow_mut ( ) ;
618636 }
619637
620638 interp_ok ( ( ) )
@@ -629,7 +647,7 @@ fn return_ready_list<'tcx>(
629647 ecx : & mut MiriInterpCx < ' tcx > ,
630648) -> InterpResult < ' tcx , i32 > {
631649 let mut interest_list = epfd. interest_list . borrow_mut ( ) ;
632- let mut ready_set = epfd. ready_set . borrow_mut ( ) ;
650+ let mut ready_events = epfd. ready_events . borrow_mut ( ) ;
633651 let mut num_of_events: i32 = 0 ;
634652 let mut array_iter = ecx. project_array_fields ( events) ?;
635653
@@ -644,27 +662,29 @@ fn return_ready_list<'tcx>(
644662 }
645663 }
646664
647- // While there is a slot to store another event, and an event to store, deliver that event .
648- // We can't use an iterator over `ready_set` as we want to remove elements as we go,
649- // so we track the most recently delivered event to find the next one. We track it as a lower
650- // bound that we can pass to `BTreeSet::range`.
651- let mut event_lower_bound = Bound :: Unbounded ;
652- while let Some ( slot ) = array_iter . next ( ecx ) ?
653- && let Some ( & key) = ready_set . range ( ( event_lower_bound , Bound :: Unbounded ) ) . next ( )
665+ // We will fill at most the first `ready_events_len` slots of the array .
666+ // Bounding the iterator this way ensures that we can re-add events
667+ // to the end of the queue during the loop without having them show up in the array.
668+ let ready_events_len = u64 :: try_from ( ready_events . len ( ) ) . unwrap ( ) ;
669+ while let Some ( ( idx , slot ) ) = array_iter . next ( ecx ) ?
670+ && idx < ready_events_len
671+ && let Some ( key) = ready_events . pop_front ( )
654672 {
655673 let interest = interest_list. get_mut ( & key) . expect ( "non-existent event in ready set" ) ;
656674 // Deliver event to caller.
657675 ecx. write_int_fields_named (
658676 & [ ( "events" , interest. active_events . into ( ) ) , ( "u64" , interest. data . into ( ) ) ] ,
659- & slot. 1 ,
677+ & slot,
660678 ) ?;
661679 num_of_events = num_of_events. strict_add ( 1 ) ;
662680 // Synchronize receiving thread with the event of interest.
663681 ecx. acquire_clock ( & interest. clock ) ?;
664- // This was an edge-triggered event, so remove it from the ready set.
665- ready_set. remove ( & key) ;
666- // Go find the next event.
667- event_lower_bound = Bound :: Excluded ( key) ;
682+ if !interest. is_edge_triggered {
683+ // This is a level-triggered interest, so we need to re-add the event
684+ // at the end of the ready queue:
685+ // <https://github.com/torvalds/linux/blob/HEAD/fs/eventpoll.c#L1835-L1847>
686+ ready_events. push_back ( key) ;
687+ }
668688 }
669689 ecx. write_int ( num_of_events, dest) ?;
670690 interp_ok ( num_of_events)
0 commit comments