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