miri/shims/unix/linux_like/epoll.rs
1use std::cell::RefCell;
2use std::collections::BTreeMap;
3use std::io;
4use std::rc::{Rc, Weak};
5use std::time::Duration;
6
7use rustc_abi::FieldIdx;
8
9use crate::concurrency::VClock;
10use crate::shims::files::{
11 DynFileDescriptionRef, FdId, FileDescription, FileDescriptionRef, WeakFileDescriptionRef,
12};
13use crate::shims::unix::UnixFileDescription;
14use crate::*;
15
16/// An `Epoll` file descriptor connects file handles and epoll events
17#[derive(Debug, Default)]
18struct Epoll {
19 /// A map of EpollEventInterests registered under this epoll instance.
20 /// Each entry is differentiated using FdId and file descriptor value.
21 interest_list: RefCell<BTreeMap<(FdId, i32), Rc<RefCell<EpollEventInterest>>>>,
22 /// A map of EpollEventInstance that will be returned when `epoll_wait` is called.
23 /// Similar to interest_list, the entry is also differentiated using FdId
24 /// and file descriptor value.
25 ready_list: ReadyList,
26 /// A list of thread ids blocked on this epoll instance.
27 blocked_tid: RefCell<Vec<ThreadId>>,
28}
29
30impl VisitProvenance for Epoll {
31 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
32 // No provenance anywhere in this type.
33 }
34}
35
36/// EpollEventInstance contains information that will be returned by epoll_wait.
37#[derive(Debug)]
38pub struct EpollEventInstance {
39 /// Xor-ed event types that happened to the file description.
40 events: u32,
41 /// Original data retrieved from `epoll_event` during `epoll_ctl`.
42 data: u64,
43 /// The release clock associated with this event.
44 clock: VClock,
45}
46
47impl EpollEventInstance {
48 pub fn new(events: u32, data: u64) -> EpollEventInstance {
49 EpollEventInstance { events, data, clock: Default::default() }
50 }
51}
52
53/// EpollEventInterest registers the file description information to an epoll
54/// instance during a successful `epoll_ctl` call. It also stores additional
55/// information needed to check and update readiness state for `epoll_wait`.
56///
57/// `events` and `data` field matches the `epoll_event` struct defined
58/// by the epoll_ctl man page. For more information
59/// see the man page:
60///
61/// <https://man7.org/linux/man-pages/man2/epoll_ctl.2.html>
62#[derive(Debug)]
63pub struct EpollEventInterest {
64 /// The file descriptor value of the file description registered.
65 /// This is only used for ready_list, to inform userspace which FD triggered an event.
66 /// For that, it is crucial to preserve the original FD number.
67 /// This FD number must never be "dereferenced" to a file description inside Miri.
68 fd_num: i32,
69 /// The events bitmask retrieved from `epoll_event`.
70 events: u32,
71 /// The data retrieved from `epoll_event`.
72 /// libc's data field in epoll_event can store integer or pointer,
73 /// but only u64 is supported for now.
74 /// <https://man7.org/linux/man-pages/man3/epoll_event.3type.html>
75 data: u64,
76 /// The epoll file description that this EpollEventInterest is registered under.
77 /// This is weak to avoid cycles, but an upgrade is always guaranteed to succeed
78 /// because only the `Epoll` holds a strong ref to a `EpollEventInterest`.
79 weak_epfd: WeakFileDescriptionRef<Epoll>,
80}
81
82/// EpollReadyEvents reflects the readiness of a file description.
83pub struct EpollReadyEvents {
84 /// The associated file is available for read(2) operations, in the sense that a read will not block.
85 /// (I.e., returning EOF is considered "ready".)
86 pub epollin: bool,
87 /// The associated file is available for write(2) operations, in the sense that a write will not block.
88 pub epollout: bool,
89 /// Stream socket peer closed connection, or shut down writing
90 /// half of connection.
91 pub epollrdhup: bool,
92 /// For stream socket, this event merely indicates that the peer
93 /// closed its end of the channel.
94 /// Unlike epollrdhup, this should only be set when the stream is fully closed.
95 /// epollrdhup also gets set when only the write half is closed, which is possible
96 /// via `shutdown(_, SHUT_WR)`.
97 pub epollhup: bool,
98 /// Error condition happened on the associated file descriptor.
99 pub epollerr: bool,
100}
101
102#[derive(Debug, Default)]
103struct ReadyList {
104 mapping: RefCell<BTreeMap<(FdId, i32), EpollEventInstance>>,
105}
106
107impl EpollReadyEvents {
108 pub fn new() -> Self {
109 EpollReadyEvents {
110 epollin: false,
111 epollout: false,
112 epollrdhup: false,
113 epollhup: false,
114 epollerr: false,
115 }
116 }
117
118 pub fn get_event_bitmask<'tcx>(&self, ecx: &MiriInterpCx<'tcx>) -> u32 {
119 let epollin = ecx.eval_libc_u32("EPOLLIN");
120 let epollout = ecx.eval_libc_u32("EPOLLOUT");
121 let epollrdhup = ecx.eval_libc_u32("EPOLLRDHUP");
122 let epollhup = ecx.eval_libc_u32("EPOLLHUP");
123 let epollerr = ecx.eval_libc_u32("EPOLLERR");
124
125 let mut bitmask = 0;
126 if self.epollin {
127 bitmask |= epollin;
128 }
129 if self.epollout {
130 bitmask |= epollout;
131 }
132 if self.epollrdhup {
133 bitmask |= epollrdhup;
134 }
135 if self.epollhup {
136 bitmask |= epollhup;
137 }
138 if self.epollerr {
139 bitmask |= epollerr;
140 }
141 bitmask
142 }
143}
144
145impl FileDescription for Epoll {
146 fn name(&self) -> &'static str {
147 "epoll"
148 }
149
150 fn close<'tcx>(
151 self,
152 _communicate_allowed: bool,
153 _ecx: &mut MiriInterpCx<'tcx>,
154 ) -> InterpResult<'tcx, io::Result<()>> {
155 interp_ok(Ok(()))
156 }
157
158 fn as_unix<'tcx>(&self, _ecx: &MiriInterpCx<'tcx>) -> &dyn UnixFileDescription {
159 self
160 }
161}
162
163impl UnixFileDescription for Epoll {}
164
165/// The table of all EpollEventInterest.
166/// The BTreeMap key is the FdId of an active file description registered with
167/// any epoll instance. The value is a list of EpollEventInterest associated
168/// with that file description.
169pub struct EpollInterestTable(BTreeMap<FdId, Vec<Weak<RefCell<EpollEventInterest>>>>);
170
171impl EpollInterestTable {
172 pub(crate) fn new() -> Self {
173 EpollInterestTable(BTreeMap::new())
174 }
175
176 pub fn insert_epoll_interest(&mut self, id: FdId, fd: Weak<RefCell<EpollEventInterest>>) {
177 match self.0.get_mut(&id) {
178 Some(fds) => {
179 fds.push(fd);
180 }
181 None => {
182 let vec = vec![fd];
183 self.0.insert(id, vec);
184 }
185 }
186 }
187
188 pub fn get_epoll_interest(&self, id: FdId) -> Option<&Vec<Weak<RefCell<EpollEventInterest>>>> {
189 self.0.get(&id)
190 }
191
192 pub fn get_epoll_interest_mut(
193 &mut self,
194 id: FdId,
195 ) -> Option<&mut Vec<Weak<RefCell<EpollEventInterest>>>> {
196 self.0.get_mut(&id)
197 }
198
199 pub fn remove(&mut self, id: FdId) {
200 self.0.remove(&id);
201 }
202}
203
204impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
205pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
206 /// This function returns a file descriptor referring to the new `Epoll` instance. This file
207 /// descriptor is used for all subsequent calls to the epoll interface. If the `flags` argument
208 /// is 0, then this function is the same as `epoll_create()`.
209 ///
210 /// <https://linux.die.net/man/2/epoll_create1>
211 fn epoll_create1(&mut self, flags: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
212 let this = self.eval_context_mut();
213
214 let flags = this.read_scalar(flags)?.to_i32()?;
215
216 let epoll_cloexec = this.eval_libc_i32("EPOLL_CLOEXEC");
217
218 // Miri does not support exec, so EPOLL_CLOEXEC flag has no effect.
219 if flags != epoll_cloexec && flags != 0 {
220 throw_unsup_format!(
221 "epoll_create1: flag {:#x} is unsupported, only 0 or EPOLL_CLOEXEC are allowed",
222 flags
223 );
224 }
225
226 let fd = this.machine.fds.insert_new(Epoll::default());
227 interp_ok(Scalar::from_i32(fd))
228 }
229
230 /// This function performs control operations on the `Epoll` instance referred to by the file
231 /// descriptor `epfd`. It requests that the operation `op` be performed for the target file
232 /// descriptor, `fd`.
233 ///
234 /// Valid values for the op argument are:
235 /// `EPOLL_CTL_ADD` - Register the target file descriptor `fd` on the `Epoll` instance referred
236 /// to by the file descriptor `epfd` and associate the event `event` with the internal file
237 /// linked to `fd`.
238 /// `EPOLL_CTL_MOD` - Change the event `event` associated with the target file descriptor `fd`.
239 /// `EPOLL_CTL_DEL` - Deregister the target file descriptor `fd` from the `Epoll` instance
240 /// referred to by `epfd`. The `event` is ignored and can be null.
241 ///
242 /// <https://linux.die.net/man/2/epoll_ctl>
243 fn epoll_ctl(
244 &mut self,
245 epfd: &OpTy<'tcx>,
246 op: &OpTy<'tcx>,
247 fd: &OpTy<'tcx>,
248 event: &OpTy<'tcx>,
249 ) -> InterpResult<'tcx, Scalar> {
250 let this = self.eval_context_mut();
251
252 let epfd_value = this.read_scalar(epfd)?.to_i32()?;
253 let op = this.read_scalar(op)?.to_i32()?;
254 let fd = this.read_scalar(fd)?.to_i32()?;
255 let event = this.deref_pointer_as(event, this.libc_ty_layout("epoll_event"))?;
256
257 let epoll_ctl_add = this.eval_libc_i32("EPOLL_CTL_ADD");
258 let epoll_ctl_mod = this.eval_libc_i32("EPOLL_CTL_MOD");
259 let epoll_ctl_del = this.eval_libc_i32("EPOLL_CTL_DEL");
260 let epollin = this.eval_libc_u32("EPOLLIN");
261 let epollout = this.eval_libc_u32("EPOLLOUT");
262 let epollrdhup = this.eval_libc_u32("EPOLLRDHUP");
263 let epollet = this.eval_libc_u32("EPOLLET");
264 let epollhup = this.eval_libc_u32("EPOLLHUP");
265 let epollerr = this.eval_libc_u32("EPOLLERR");
266
267 // Throw EINVAL if epfd and fd have the same value.
268 if epfd_value == fd {
269 return this.set_last_error_and_return_i32(LibcError("EINVAL"));
270 }
271
272 // Check if epfd is a valid epoll file descriptor.
273 let Some(epfd) = this.machine.fds.get(epfd_value) else {
274 return this.set_last_error_and_return_i32(LibcError("EBADF"));
275 };
276 let epfd = epfd
277 .downcast::<Epoll>()
278 .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_ctl`"))?;
279
280 let mut interest_list = epfd.interest_list.borrow_mut();
281
282 let Some(fd_ref) = this.machine.fds.get(fd) else {
283 return this.set_last_error_and_return_i32(LibcError("EBADF"));
284 };
285 let id = fd_ref.id();
286
287 if op == epoll_ctl_add || op == epoll_ctl_mod {
288 // Read event bitmask and data from epoll_event passed by caller.
289 let mut events =
290 this.read_scalar(&this.project_field(&event, FieldIdx::ZERO)?)?.to_u32()?;
291 let data = this.read_scalar(&this.project_field(&event, FieldIdx::ONE)?)?.to_u64()?;
292
293 // Unset the flag we support to discover if any unsupported flags are used.
294 let mut flags = events;
295 // epoll_wait(2) will always wait for epollhup and epollerr; it is not
296 // necessary to set it in events when calling epoll_ctl().
297 // So we will always set these two event types.
298 events |= epollhup;
299 events |= epollerr;
300
301 if events & epollet != epollet {
302 // We only support edge-triggered notification for now.
303 throw_unsup_format!("epoll_ctl: epollet flag must be included.");
304 } else {
305 flags &= !epollet;
306 }
307 if flags & epollin == epollin {
308 flags &= !epollin;
309 }
310 if flags & epollout == epollout {
311 flags &= !epollout;
312 }
313 if flags & epollrdhup == epollrdhup {
314 flags &= !epollrdhup;
315 }
316 if flags & epollhup == epollhup {
317 flags &= !epollhup;
318 }
319 if flags & epollerr == epollerr {
320 flags &= !epollerr;
321 }
322 if flags != 0 {
323 throw_unsup_format!(
324 "epoll_ctl: encountered unknown unsupported flags {:#x}",
325 flags
326 );
327 }
328
329 let epoll_key = (id, fd);
330
331 // Check the existence of fd in the interest list.
332 if op == epoll_ctl_add {
333 if interest_list.contains_key(&epoll_key) {
334 return this.set_last_error_and_return_i32(LibcError("EEXIST"));
335 }
336 } else {
337 if !interest_list.contains_key(&epoll_key) {
338 return this.set_last_error_and_return_i32(LibcError("ENOENT"));
339 }
340 }
341
342 if op == epoll_ctl_add {
343 // Create an epoll_interest.
344 let interest = Rc::new(RefCell::new(EpollEventInterest {
345 fd_num: fd,
346 events,
347 data,
348 weak_epfd: FileDescriptionRef::downgrade(&epfd),
349 }));
350 // Notification will be returned for current epfd if there is event in the file
351 // descriptor we registered.
352 check_and_update_one_event_interest(&fd_ref, &interest, id, this)?;
353
354 // Insert an epoll_interest to global epoll_interest list.
355 this.machine.epoll_interests.insert_epoll_interest(id, Rc::downgrade(&interest));
356 interest_list.insert(epoll_key, interest);
357 } else {
358 // Modify the existing interest.
359 let epoll_interest = interest_list.get_mut(&epoll_key).unwrap();
360 {
361 let mut epoll_interest = epoll_interest.borrow_mut();
362 epoll_interest.events = events;
363 epoll_interest.data = data;
364 }
365 // Updating an FD interest triggers events.
366 check_and_update_one_event_interest(&fd_ref, epoll_interest, id, this)?;
367 }
368
369 interp_ok(Scalar::from_i32(0))
370 } else if op == epoll_ctl_del {
371 let epoll_key = (id, fd);
372
373 // Remove epoll_event_interest from interest_list.
374 let Some(epoll_interest) = interest_list.remove(&epoll_key) else {
375 return this.set_last_error_and_return_i32(LibcError("ENOENT"));
376 };
377 // All related Weak<EpollEventInterest> will fail to upgrade after the drop.
378 drop(epoll_interest);
379
380 // Remove related epoll_interest from ready list.
381 epfd.ready_list.mapping.borrow_mut().remove(&epoll_key);
382
383 // Remove dangling EpollEventInterest from its global table.
384 // .unwrap() below should succeed because the file description id must have registered
385 // at least one epoll_interest, if not, it will fail when removing epoll_interest from
386 // interest list.
387 this.machine
388 .epoll_interests
389 .get_epoll_interest_mut(id)
390 .unwrap()
391 .retain(|event| event.upgrade().is_some());
392
393 interp_ok(Scalar::from_i32(0))
394 } else {
395 throw_unsup_format!("unsupported epoll_ctl operation: {op}");
396 }
397 }
398
399 /// The `epoll_wait()` system call waits for events on the `Epoll`
400 /// instance referred to by the file descriptor `epfd`. The buffer
401 /// pointed to by `events` is used to return information from the ready
402 /// list about file descriptors in the interest list that have some
403 /// events available. Up to `maxevents` are returned by `epoll_wait()`.
404 /// The `maxevents` argument must be greater than zero.
405 ///
406 /// The `timeout` argument specifies the number of milliseconds that
407 /// `epoll_wait()` will block. Time is measured against the
408 /// CLOCK_MONOTONIC clock. If the timeout is zero, the function will not block,
409 /// while if the timeout is -1, the function will block
410 /// until at least one event has been retrieved (or an error
411 /// occurred).
412 ///
413 /// A call to `epoll_wait()` will block until either:
414 /// • a file descriptor delivers an event;
415 /// • the call is interrupted by a signal handler; or
416 /// • the timeout expires.
417 ///
418 /// Note that the timeout interval will be rounded up to the system
419 /// clock granularity, and kernel scheduling delays mean that the
420 /// blocking interval may overrun by a small amount. Specifying a
421 /// timeout of -1 causes `epoll_wait()` to block indefinitely, while
422 /// specifying a timeout equal to zero cause `epoll_wait()` to return
423 /// immediately, even if no events are available.
424 ///
425 /// On success, `epoll_wait()` returns the number of file descriptors
426 /// ready for the requested I/O, or zero if no file descriptor became
427 /// ready during the requested timeout milliseconds. On failure,
428 /// `epoll_wait()` returns -1 and errno is set to indicate the error.
429 ///
430 /// <https://man7.org/linux/man-pages/man2/epoll_wait.2.html>
431 fn epoll_wait(
432 &mut self,
433 epfd: &OpTy<'tcx>,
434 events_op: &OpTy<'tcx>,
435 maxevents: &OpTy<'tcx>,
436 timeout: &OpTy<'tcx>,
437 dest: &MPlaceTy<'tcx>,
438 ) -> InterpResult<'tcx> {
439 let this = self.eval_context_mut();
440
441 let epfd_value = this.read_scalar(epfd)?.to_i32()?;
442 let events = this.read_immediate(events_op)?;
443 let maxevents = this.read_scalar(maxevents)?.to_i32()?;
444 let timeout = this.read_scalar(timeout)?.to_i32()?;
445
446 if epfd_value <= 0 || maxevents <= 0 {
447 return this.set_last_error_and_return(LibcError("EINVAL"), dest);
448 }
449
450 // This needs to come after the maxevents value check, or else maxevents.try_into().unwrap()
451 // will fail.
452 let event = this.deref_pointer_as(
453 &events,
454 this.libc_array_ty_layout("epoll_event", maxevents.try_into().unwrap()),
455 )?;
456
457 let Some(epfd) = this.machine.fds.get(epfd_value) else {
458 return this.set_last_error_and_return(LibcError("EBADF"), dest);
459 };
460 let Some(epfd) = epfd.downcast::<Epoll>() else {
461 return this.set_last_error_and_return(LibcError("EBADF"), dest);
462 };
463
464 // We just need to know if the ready list is empty and borrow the thread_ids out.
465 let ready_list_empty = epfd.ready_list.mapping.borrow().is_empty();
466 if timeout == 0 || !ready_list_empty {
467 // If the ready list is not empty, or the timeout is 0, we can return immediately.
468 return_ready_list(&epfd, dest, &event, this)?;
469 } else {
470 // Blocking
471 let timeout = match timeout {
472 0.. => {
473 let duration = Duration::from_millis(timeout.try_into().unwrap());
474 Some((TimeoutClock::Monotonic, TimeoutAnchor::Relative, duration))
475 }
476 -1 => None,
477 ..-1 => {
478 throw_unsup_format!(
479 "epoll_wait: Only timeout values greater than or equal to -1 are supported."
480 );
481 }
482 };
483 // Record this thread as blocked.
484 epfd.blocked_tid.borrow_mut().push(this.active_thread());
485 // And block it.
486 let dest = dest.clone();
487 // We keep a strong ref to the underlying `Epoll` to make sure it sticks around.
488 // This means there'll be a leak if we never wake up, but that anyway would imply
489 // a thread is permanently blocked so this is fine.
490 this.block_thread(
491 BlockReason::Epoll,
492 timeout,
493 callback!(
494 @capture<'tcx> {
495 epfd: FileDescriptionRef<Epoll>,
496 dest: MPlaceTy<'tcx>,
497 event: MPlaceTy<'tcx>,
498 }
499 |this, unblock: UnblockKind| {
500 match unblock {
501 UnblockKind::Ready => {
502 return_ready_list(&epfd, &dest, &event, this)?;
503 interp_ok(())
504 },
505 UnblockKind::TimedOut => {
506 // Remove the current active thread_id from the blocked thread_id list.
507 epfd
508 .blocked_tid.borrow_mut()
509 .retain(|&id| id != this.active_thread());
510 this.write_int(0, &dest)?;
511 interp_ok(())
512 },
513 }
514 }
515 ),
516 );
517 }
518 interp_ok(())
519 }
520
521 /// For a specific file description, get its ready events and update the corresponding ready
522 /// list. This function should be called whenever an event causes more bytes or an EOF to become
523 /// newly readable from an FD, and whenever more bytes can be written to an FD or no more future
524 /// writes are possible.
525 ///
526 /// This *will* report an event if anyone is subscribed to it, without any further filtering, so
527 /// do not call this function when an FD didn't have anything happen to it!
528 fn check_and_update_readiness(
529 &mut self,
530 fd_ref: DynFileDescriptionRef,
531 ) -> InterpResult<'tcx, ()> {
532 let this = self.eval_context_mut();
533 let id = fd_ref.id();
534 let mut waiter = Vec::new();
535 // Get a list of EpollEventInterest that is associated to a specific file description.
536 if let Some(epoll_interests) = this.machine.epoll_interests.get_epoll_interest(id) {
537 for weak_epoll_interest in epoll_interests {
538 if let Some(epoll_interest) = weak_epoll_interest.upgrade() {
539 let is_updated =
540 check_and_update_one_event_interest(&fd_ref, &epoll_interest, id, this)?;
541 if is_updated {
542 // Edge-triggered notification only notify one thread even if there are
543 // multiple threads blocked on the same epfd.
544
545 // This unwrap can never fail because if the current epoll instance were
546 // closed, the upgrade of weak_epoll_interest
547 // above would fail. This guarantee holds because only the epoll instance
548 // holds a strong ref to epoll_interest.
549 let epfd = epoll_interest.borrow().weak_epfd.upgrade().unwrap();
550 // FIXME: We can randomly pick a thread to unblock.
551 if let Some(thread_id) = epfd.blocked_tid.borrow_mut().pop() {
552 waiter.push(thread_id);
553 };
554 }
555 }
556 }
557 }
558 waiter.sort();
559 waiter.dedup();
560 for thread_id in waiter {
561 this.unblock_thread(thread_id, BlockReason::Epoll)?;
562 }
563 interp_ok(())
564 }
565}
566
567/// This function takes in ready list and returns EpollEventInstance with file description
568/// that is not closed.
569fn ready_list_next(
570 ecx: &MiriInterpCx<'_>,
571 ready_list: &mut BTreeMap<(FdId, i32), EpollEventInstance>,
572) -> Option<EpollEventInstance> {
573 while let Some((epoll_key, epoll_event_instance)) = ready_list.pop_first() {
574 // This ensures that we only return events that we are interested. The FD might have been closed since
575 // the event was generated, in which case we are not interested anymore.
576 // When a file description is fully closed, it gets removed from `machine.epoll_interests`,
577 // so we skip events whose FD is not in that map anymore.
578 if ecx.machine.epoll_interests.get_epoll_interest(epoll_key.0).is_some() {
579 return Some(epoll_event_instance);
580 }
581 }
582 None
583}
584
585/// This helper function checks whether an epoll notification should be triggered for a specific
586/// epoll_interest and, if necessary, triggers the notification, and returns whether the
587/// notification was added/updated. Unlike check_and_update_readiness, this function sends a
588/// notification to only one epoll instance.
589fn check_and_update_one_event_interest<'tcx>(
590 fd_ref: &DynFileDescriptionRef,
591 interest: &RefCell<EpollEventInterest>,
592 id: FdId,
593 ecx: &MiriInterpCx<'tcx>,
594) -> InterpResult<'tcx, bool> {
595 // Get the bitmask of ready events for a file description.
596 let ready_events_bitmask = fd_ref.as_unix(ecx).get_epoll_ready_events()?.get_event_bitmask(ecx);
597 let epoll_event_interest = interest.borrow();
598 let epfd = epoll_event_interest.weak_epfd.upgrade().unwrap();
599 // This checks if any of the events specified in epoll_event_interest.events
600 // match those in ready_events.
601 let flags = epoll_event_interest.events & ready_events_bitmask;
602 // If there is any event that we are interested in being specified as ready,
603 // insert an epoll_return to the ready list.
604 if flags != 0 {
605 let epoll_key = (id, epoll_event_interest.fd_num);
606 let mut ready_list = epfd.ready_list.mapping.borrow_mut();
607 let mut event_instance = EpollEventInstance::new(flags, epoll_event_interest.data);
608 // If we are tracking data races, remember the current clock so we can sync with it later.
609 ecx.release_clock(|clock| {
610 event_instance.clock.clone_from(clock);
611 });
612 // Triggers the notification by inserting it to the ready list.
613 ready_list.insert(epoll_key, event_instance);
614 interp_ok(true)
615 } else {
616 interp_ok(false)
617 }
618}
619
620/// Stores the ready list of the `epfd` epoll instance into `events` (which must be an array),
621/// and the number of returned events into `dest`.
622fn return_ready_list<'tcx>(
623 epfd: &FileDescriptionRef<Epoll>,
624 dest: &MPlaceTy<'tcx>,
625 events: &MPlaceTy<'tcx>,
626 ecx: &mut MiriInterpCx<'tcx>,
627) -> InterpResult<'tcx> {
628 let mut ready_list = epfd.ready_list.mapping.borrow_mut();
629 let mut num_of_events: i32 = 0;
630 let mut array_iter = ecx.project_array_fields(events)?;
631
632 while let Some(des) = array_iter.next(ecx)? {
633 if let Some(epoll_event_instance) = ready_list_next(ecx, &mut ready_list) {
634 ecx.write_int_fields_named(
635 &[
636 ("events", epoll_event_instance.events.into()),
637 ("u64", epoll_event_instance.data.into()),
638 ],
639 &des.1,
640 )?;
641 // Synchronize waking thread with the event of interest.
642 ecx.acquire_clock(&epoll_event_instance.clock);
643
644 num_of_events = num_of_events.strict_add(1);
645 } else {
646 break;
647 }
648 }
649 ecx.write_int(num_of_events, dest)?;
650 interp_ok(())
651}