about summary refs log tree commit diff
path: root/src/tools
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-08-16 16:14:42 +0000
committerbors <bors@rust-lang.org>2024-08-16 16:14:42 +0000
commit83f1b380825d50e3cffe84ef4c39a09bf841a3c1 (patch)
treec70b68a567305becd89e4a26a0c37d07b7a2a370 /src/tools
parent1a51dd9247c5ecf9efa99ebf644bb12e33dcf3b5 (diff)
parent883e4773b30f652e83326df99dc0ec7d652e1f94 (diff)
downloadrust-83f1b380825d50e3cffe84ef4c39a09bf841a3c1.tar.gz
rust-83f1b380825d50e3cffe84ef4c39a09bf841a3c1.zip
Auto merge of #3809 - RalfJung:fd-refcell, r=oli-obk
FD: remove big surrounding RefCell, simplify socketpair

A while ago, I added the big implicit RefCell for all file descriptions since it avoided interior mutability in `eventfd`. However, this requires us to hold the RefCell "lock" around the entire invocation of the `read`/`write` methods on an FD, which is not great. For instance, if an FD wants to update epoll notifications from inside its `read`/`write`, it is very crucial that the notification check does not end up accessing the FD itself. Such cycles, however, occur naturally:
- eventfd wants to update notifications for itself
- socketfd wants to update notifications on its "peer", which will in turn check *its* peer to see whether that buffer is empty -- and my peer's peer is myself.

This then also lets us simplify socketpair, which currently holds a weak reference to its peer *and* a weak reference to the peer's buffer -- that was previously needed precisely to avoid this issue.
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/miri/src/shims/unix/fd.rs121
-rw-r--r--src/tools/miri/src/shims/unix/fs.rs54
-rw-r--r--src/tools/miri/src/shims/unix/linux/epoll.rs50
-rw-r--r--src/tools/miri/src/shims/unix/linux/eventfd.rs64
-rw-r--r--src/tools/miri/src/shims/unix/socket.rs161
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-epoll.rs243
6 files changed, 294 insertions, 399 deletions
diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs
index 98a124b9a56..e3b9835e360 100644
--- a/src/tools/miri/src/shims/unix/fd.rs
+++ b/src/tools/miri/src/shims/unix/fd.rs
@@ -2,9 +2,9 @@
 //! standard file descriptors (stdin/stdout/stderr).
 
 use std::any::Any;
-use std::cell::{Ref, RefCell, RefMut};
 use std::collections::BTreeMap;
 use std::io::{self, ErrorKind, IsTerminal, Read, SeekFrom, Write};
+use std::ops::Deref;
 use std::rc::Rc;
 use std::rc::Weak;
 
@@ -27,9 +27,9 @@ pub trait FileDescription: std::fmt::Debug + Any {
 
     /// Reads as much as possible into the given buffer, and returns the number of bytes read.
     fn read<'tcx>(
-        &mut self,
+        &self,
+        _self_ref: &FileDescriptionRef,
         _communicate_allowed: bool,
-        _fd_id: FdId,
         _bytes: &mut [u8],
         _ecx: &mut MiriInterpCx<'tcx>,
     ) -> InterpResult<'tcx, io::Result<usize>> {
@@ -38,9 +38,9 @@ pub trait FileDescription: std::fmt::Debug + Any {
 
     /// Writes as much as possible from the given buffer, and returns the number of bytes written.
     fn write<'tcx>(
-        &mut self,
+        &self,
+        _self_ref: &FileDescriptionRef,
         _communicate_allowed: bool,
-        _fd_id: FdId,
         _bytes: &[u8],
         _ecx: &mut MiriInterpCx<'tcx>,
     ) -> InterpResult<'tcx, io::Result<usize>> {
@@ -50,7 +50,7 @@ pub trait FileDescription: std::fmt::Debug + Any {
     /// Reads as much as possible into the given buffer from a given offset,
     /// and returns the number of bytes read.
     fn pread<'tcx>(
-        &mut self,
+        &self,
         _communicate_allowed: bool,
         _bytes: &mut [u8],
         _offset: u64,
@@ -62,7 +62,7 @@ pub trait FileDescription: std::fmt::Debug + Any {
     /// Writes as much as possible from the given buffer starting at a given offset,
     /// and returns the number of bytes written.
     fn pwrite<'tcx>(
-        &mut self,
+        &self,
         _communicate_allowed: bool,
         _bytes: &[u8],
         _offset: u64,
@@ -74,7 +74,7 @@ pub trait FileDescription: std::fmt::Debug + Any {
     /// Seeks to the given offset (which can be relative to the beginning, end, or current position).
     /// Returns the new position from the start of the stream.
     fn seek<'tcx>(
-        &mut self,
+        &self,
         _communicate_allowed: bool,
         _offset: SeekFrom,
     ) -> InterpResult<'tcx, io::Result<u64>> {
@@ -111,14 +111,9 @@ pub trait FileDescription: std::fmt::Debug + Any {
 
 impl dyn FileDescription {
     #[inline(always)]
-    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
+    pub fn downcast<T: Any>(&self) -> Option<&T> {
         (self as &dyn Any).downcast_ref()
     }
-
-    #[inline(always)]
-    pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
-        (self as &mut dyn Any).downcast_mut()
-    }
 }
 
 impl FileDescription for io::Stdin {
@@ -127,9 +122,9 @@ impl FileDescription for io::Stdin {
     }
 
     fn read<'tcx>(
-        &mut self,
+        &self,
+        _self_ref: &FileDescriptionRef,
         communicate_allowed: bool,
-        _fd_id: FdId,
         bytes: &mut [u8],
         _ecx: &mut MiriInterpCx<'tcx>,
     ) -> InterpResult<'tcx, io::Result<usize>> {
@@ -137,7 +132,7 @@ impl FileDescription for io::Stdin {
             // We want isolation mode to be deterministic, so we have to disallow all reads, even stdin.
             helpers::isolation_abort_error("`read` from stdin")?;
         }
-        Ok(Read::read(self, bytes))
+        Ok(Read::read(&mut { self }, bytes))
     }
 
     fn is_tty(&self, communicate_allowed: bool) -> bool {
@@ -151,14 +146,14 @@ impl FileDescription for io::Stdout {
     }
 
     fn write<'tcx>(
-        &mut self,
+        &self,
+        _self_ref: &FileDescriptionRef,
         _communicate_allowed: bool,
-        _fd_id: FdId,
         bytes: &[u8],
         _ecx: &mut MiriInterpCx<'tcx>,
     ) -> InterpResult<'tcx, io::Result<usize>> {
         // We allow writing to stderr even with isolation enabled.
-        let result = Write::write(self, bytes);
+        let result = Write::write(&mut { self }, bytes);
         // Stdout is buffered, flush to make sure it appears on the
         // screen.  This is the write() syscall of the interpreted
         // program, we want it to correspond to a write() syscall on
@@ -180,9 +175,9 @@ impl FileDescription for io::Stderr {
     }
 
     fn write<'tcx>(
-        &mut self,
+        &self,
+        _self_ref: &FileDescriptionRef,
         _communicate_allowed: bool,
-        _fd_id: FdId,
         bytes: &[u8],
         _ecx: &mut MiriInterpCx<'tcx>,
     ) -> InterpResult<'tcx, io::Result<usize>> {
@@ -206,9 +201,9 @@ impl FileDescription for NullOutput {
     }
 
     fn write<'tcx>(
-        &mut self,
+        &self,
+        _self_ref: &FileDescriptionRef,
         _communicate_allowed: bool,
-        _fd_id: FdId,
         bytes: &[u8],
         _ecx: &mut MiriInterpCx<'tcx>,
     ) -> InterpResult<'tcx, io::Result<usize>> {
@@ -221,26 +216,23 @@ impl FileDescription for NullOutput {
 #[derive(Clone, Debug)]
 pub struct FileDescWithId<T: FileDescription + ?Sized> {
     id: FdId,
-    file_description: RefCell<Box<T>>,
+    file_description: Box<T>,
 }
 
 #[derive(Clone, Debug)]
 pub struct FileDescriptionRef(Rc<FileDescWithId<dyn FileDescription>>);
 
-impl FileDescriptionRef {
-    fn new(fd: impl FileDescription, id: FdId) -> Self {
-        FileDescriptionRef(Rc::new(FileDescWithId {
-            id,
-            file_description: RefCell::new(Box::new(fd)),
-        }))
-    }
+impl Deref for FileDescriptionRef {
+    type Target = dyn FileDescription;
 
-    pub fn borrow(&self) -> Ref<'_, dyn FileDescription> {
-        Ref::map(self.0.file_description.borrow(), |fd| fd.as_ref())
+    fn deref(&self) -> &Self::Target {
+        &*self.0.file_description
     }
+}
 
-    pub fn borrow_mut(&self) -> RefMut<'_, dyn FileDescription> {
-        RefMut::map(self.0.file_description.borrow_mut(), |fd| fd.as_mut())
+impl FileDescriptionRef {
+    fn new(fd: impl FileDescription, id: FdId) -> Self {
+        FileDescriptionRef(Rc::new(FileDescWithId { id, file_description: Box::new(fd) }))
     }
 
     pub fn close<'tcx>(
@@ -256,7 +248,7 @@ impl FileDescriptionRef {
                 // Remove entry from the global epoll_event_interest table.
                 ecx.machine.epoll_interests.remove(id);
 
-                RefCell::into_inner(fd.file_description).close(communicate_allowed, ecx)
+                fd.file_description.close(communicate_allowed, ecx)
             }
             None => Ok(Ok(())),
         }
@@ -269,16 +261,6 @@ impl FileDescriptionRef {
     pub fn get_id(&self) -> FdId {
         self.0.id
     }
-
-    /// Function used to retrieve the readiness events of a file description and insert
-    /// an `EpollEventInstance` into the ready list if the file description is ready.
-    pub(crate) fn check_and_update_readiness<'tcx>(
-        &self,
-        ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>,
-    ) -> InterpResult<'tcx, ()> {
-        use crate::shims::unix::linux::epoll::EvalContextExt;
-        ecx.check_and_update_readiness(self.get_id(), || self.borrow_mut().get_epoll_ready_events())
-    }
 }
 
 /// Holds a weak reference to the actual file description.
@@ -334,11 +316,20 @@ impl FdTable {
         fds
     }
 
-    /// Insert a new file description to the FdTable.
-    pub fn insert_new(&mut self, fd: impl FileDescription) -> i32 {
+    pub fn new_ref(&mut self, fd: impl FileDescription) -> FileDescriptionRef {
         let file_handle = FileDescriptionRef::new(fd, self.next_file_description_id);
         self.next_file_description_id = FdId(self.next_file_description_id.0.strict_add(1));
-        self.insert_ref_with_min_fd(file_handle, 0)
+        file_handle
+    }
+
+    /// Insert a new file description to the FdTable.
+    pub fn insert_new(&mut self, fd: impl FileDescription) -> i32 {
+        let fd_ref = self.new_ref(fd);
+        self.insert(fd_ref)
+    }
+
+    pub fn insert(&mut self, fd_ref: FileDescriptionRef) -> i32 {
+        self.insert_ref_with_min_fd(fd_ref, 0)
     }
 
     /// Insert a file description, giving it a file descriptor that is at least `min_fd`.
@@ -368,17 +359,7 @@ impl FdTable {
         new_fd
     }
 
-    pub fn get(&self, fd: i32) -> Option<Ref<'_, dyn FileDescription>> {
-        let fd = self.fds.get(&fd)?;
-        Some(fd.borrow())
-    }
-
-    pub fn get_mut(&self, fd: i32) -> Option<RefMut<'_, dyn FileDescription>> {
-        let fd = self.fds.get(&fd)?;
-        Some(fd.borrow_mut())
-    }
-
-    pub fn get_ref(&self, fd: i32) -> Option<FileDescriptionRef> {
+    pub fn get(&self, fd: i32) -> Option<FileDescriptionRef> {
         let fd = self.fds.get(&fd)?;
         Some(fd.clone())
     }
@@ -397,7 +378,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn dup(&mut self, old_fd: i32) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
 
-        let Some(dup_fd) = this.machine.fds.get_ref(old_fd) else {
+        let Some(dup_fd) = this.machine.fds.get(old_fd) else {
             return Ok(Scalar::from_i32(this.fd_not_found()?));
         };
         Ok(Scalar::from_i32(this.machine.fds.insert_ref_with_min_fd(dup_fd, 0)))
@@ -406,7 +387,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn dup2(&mut self, old_fd: i32, new_fd: i32) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
 
-        let Some(dup_fd) = this.machine.fds.get_ref(old_fd) else {
+        let Some(dup_fd) = this.machine.fds.get(old_fd) else {
             return Ok(Scalar::from_i32(this.fd_not_found()?));
         };
         if new_fd != old_fd {
@@ -492,7 +473,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             let start = this.read_scalar(&args[2])?.to_i32()?;
 
-            match this.machine.fds.get_ref(fd) {
+            match this.machine.fds.get(fd) {
                 Some(dup_fd) =>
                     Ok(Scalar::from_i32(this.machine.fds.insert_ref_with_min_fd(dup_fd, start))),
                 None => Ok(Scalar::from_i32(this.fd_not_found()?)),
@@ -565,7 +546,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let communicate = this.machine.communicate();
 
         // We temporarily dup the FD to be able to retain mutable access to `this`.
-        let Some(fd) = this.machine.fds.get_ref(fd) else {
+        let Some(fd) = this.machine.fds.get(fd) else {
             trace!("read: FD not found");
             return Ok(Scalar::from_target_isize(this.fd_not_found()?, this));
         };
@@ -576,14 +557,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // `usize::MAX` because it is bounded by the host's `isize`.
         let mut bytes = vec![0; usize::try_from(count).unwrap()];
         let result = match offset {
-            None => fd.borrow_mut().read(communicate, fd.get_id(), &mut bytes, this),
+            None => fd.read(&fd, communicate, &mut bytes, this),
             Some(offset) => {
                 let Ok(offset) = u64::try_from(offset) else {
                     let einval = this.eval_libc("EINVAL");
                     this.set_last_error(einval)?;
                     return Ok(Scalar::from_target_isize(-1, this));
                 };
-                fd.borrow_mut().pread(communicate, &mut bytes, offset, this)
+                fd.pread(communicate, &mut bytes, offset, this)
             }
         };
 
@@ -629,19 +610,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         let bytes = this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(count))?.to_owned();
         // We temporarily dup the FD to be able to retain mutable access to `this`.
-        let Some(fd) = this.machine.fds.get_ref(fd) else {
+        let Some(fd) = this.machine.fds.get(fd) else {
             return Ok(Scalar::from_target_isize(this.fd_not_found()?, this));
         };
 
         let result = match offset {
-            None => fd.borrow_mut().write(communicate, fd.get_id(), &bytes, this),
+            None => fd.write(&fd, communicate, &bytes, this),
             Some(offset) => {
                 let Ok(offset) = u64::try_from(offset) else {
                     let einval = this.eval_libc("EINVAL");
                     this.set_last_error(einval)?;
                     return Ok(Scalar::from_target_isize(-1, this));
                 };
-                fd.borrow_mut().pwrite(communicate, &bytes, offset, this)
+                fd.pwrite(communicate, &bytes, offset, this)
             }
         };
 
diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs
index 9da36e64a0f..80f4b89bf34 100644
--- a/src/tools/miri/src/shims/unix/fs.rs
+++ b/src/tools/miri/src/shims/unix/fs.rs
@@ -12,7 +12,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_target::abi::Size;
 
 use crate::shims::os_str::bytes_to_os_str;
-use crate::shims::unix::fd::FdId;
+use crate::shims::unix::fd::FileDescriptionRef;
 use crate::shims::unix::*;
 use crate::*;
 use shims::time::system_time_to_duration;
@@ -31,29 +31,29 @@ impl FileDescription for FileHandle {
     }
 
     fn read<'tcx>(
-        &mut self,
+        &self,
+        _self_ref: &FileDescriptionRef,
         communicate_allowed: bool,
-        _fd_id: FdId,
         bytes: &mut [u8],
         _ecx: &mut MiriInterpCx<'tcx>,
     ) -> InterpResult<'tcx, io::Result<usize>> {
         assert!(communicate_allowed, "isolation should have prevented even opening a file");
-        Ok(self.file.read(bytes))
+        Ok((&mut &self.file).read(bytes))
     }
 
     fn write<'tcx>(
-        &mut self,
+        &self,
+        _self_ref: &FileDescriptionRef,
         communicate_allowed: bool,
-        _fd_id: FdId,
         bytes: &[u8],
         _ecx: &mut MiriInterpCx<'tcx>,
     ) -> InterpResult<'tcx, io::Result<usize>> {
         assert!(communicate_allowed, "isolation should have prevented even opening a file");
-        Ok(self.file.write(bytes))
+        Ok((&mut &self.file).write(bytes))
     }
 
     fn pread<'tcx>(
-        &mut self,
+        &self,
         communicate_allowed: bool,
         bytes: &mut [u8],
         offset: u64,
@@ -63,13 +63,13 @@ impl FileDescription for FileHandle {
         // Emulates pread using seek + read + seek to restore cursor position.
         // Correctness of this emulation relies on sequential nature of Miri execution.
         // The closure is used to emulate `try` block, since we "bubble" `io::Error` using `?`.
+        let file = &mut &self.file;
         let mut f = || {
-            let cursor_pos = self.file.stream_position()?;
-            self.file.seek(SeekFrom::Start(offset))?;
-            let res = self.file.read(bytes);
+            let cursor_pos = file.stream_position()?;
+            file.seek(SeekFrom::Start(offset))?;
+            let res = file.read(bytes);
             // Attempt to restore cursor position even if the read has failed
-            self.file
-                .seek(SeekFrom::Start(cursor_pos))
+            file.seek(SeekFrom::Start(cursor_pos))
                 .expect("failed to restore file position, this shouldn't be possible");
             res
         };
@@ -77,7 +77,7 @@ impl FileDescription for FileHandle {
     }
 
     fn pwrite<'tcx>(
-        &mut self,
+        &self,
         communicate_allowed: bool,
         bytes: &[u8],
         offset: u64,
@@ -87,13 +87,13 @@ impl FileDescription for FileHandle {
         // Emulates pwrite using seek + write + seek to restore cursor position.
         // Correctness of this emulation relies on sequential nature of Miri execution.
         // The closure is used to emulate `try` block, since we "bubble" `io::Error` using `?`.
+        let file = &mut &self.file;
         let mut f = || {
-            let cursor_pos = self.file.stream_position()?;
-            self.file.seek(SeekFrom::Start(offset))?;
-            let res = self.file.write(bytes);
+            let cursor_pos = file.stream_position()?;
+            file.seek(SeekFrom::Start(offset))?;
+            let res = file.write(bytes);
             // Attempt to restore cursor position even if the write has failed
-            self.file
-                .seek(SeekFrom::Start(cursor_pos))
+            file.seek(SeekFrom::Start(cursor_pos))
                 .expect("failed to restore file position, this shouldn't be possible");
             res
         };
@@ -101,12 +101,12 @@ impl FileDescription for FileHandle {
     }
 
     fn seek<'tcx>(
-        &mut self,
+        &self,
         communicate_allowed: bool,
         offset: SeekFrom,
     ) -> InterpResult<'tcx, io::Result<u64>> {
         assert!(communicate_allowed, "isolation should have prevented even opening a file");
-        Ok(self.file.seek(offset))
+        Ok((&mut &self.file).seek(offset))
     }
 
     fn close<'tcx>(
@@ -580,7 +580,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         let communicate = this.machine.communicate();
 
-        let Some(mut file_description) = this.machine.fds.get_mut(fd) else {
+        let Some(file_description) = this.machine.fds.get(fd) else {
             return Ok(Scalar::from_i64(this.fd_not_found()?));
         };
         let result = file_description
@@ -1276,7 +1276,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         // FIXME: Support ftruncate64 for all FDs
         let FileHandle { file, writable } =
-            file_description.downcast_ref::<FileHandle>().ok_or_else(|| {
+            file_description.downcast::<FileHandle>().ok_or_else(|| {
                 err_unsup_format!("`ftruncate64` is only supported on file-backed file descriptors")
             })?;
 
@@ -1328,7 +1328,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         };
         // Only regular files support synchronization.
         let FileHandle { file, writable } =
-            file_description.downcast_ref::<FileHandle>().ok_or_else(|| {
+            file_description.downcast::<FileHandle>().ok_or_else(|| {
                 err_unsup_format!("`fsync` is only supported on file-backed file descriptors")
             })?;
         let io_result = maybe_sync_file(file, *writable, File::sync_all);
@@ -1353,7 +1353,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         };
         // Only regular files support synchronization.
         let FileHandle { file, writable } =
-            file_description.downcast_ref::<FileHandle>().ok_or_else(|| {
+            file_description.downcast::<FileHandle>().ok_or_else(|| {
                 err_unsup_format!("`fdatasync` is only supported on file-backed file descriptors")
             })?;
         let io_result = maybe_sync_file(file, *writable, File::sync_data);
@@ -1401,7 +1401,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         };
         // Only regular files support synchronization.
         let FileHandle { file, writable } =
-            file_description.downcast_ref::<FileHandle>().ok_or_else(|| {
+            file_description.downcast::<FileHandle>().ok_or_else(|| {
                 err_unsup_format!(
                     "`sync_data_range` is only supported on file-backed file descriptors"
                 )
@@ -1708,7 +1708,7 @@ impl FileMetadata {
         };
 
         let file = &file_description
-            .downcast_ref::<FileHandle>()
+            .downcast::<FileHandle>()
             .ok_or_else(|| {
                 err_unsup_format!(
                     "obtaining metadata is only supported on file-backed file descriptors"
diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs
index 89616bd0d07..d529a9b63e6 100644
--- a/src/tools/miri/src/shims/unix/linux/epoll.rs
+++ b/src/tools/miri/src/shims/unix/linux/epoll.rs
@@ -3,7 +3,7 @@ use std::collections::BTreeMap;
 use std::io;
 use std::rc::{Rc, Weak};
 
-use crate::shims::unix::fd::FdId;
+use crate::shims::unix::fd::{FdId, FileDescriptionRef};
 use crate::shims::unix::*;
 use crate::*;
 
@@ -12,7 +12,7 @@ use crate::*;
 struct Epoll {
     /// A map of EpollEventInterests registered under this epoll instance.
     /// Each entry is differentiated using FdId and file descriptor value.
-    interest_list: BTreeMap<(FdId, i32), Rc<RefCell<EpollEventInterest>>>,
+    interest_list: RefCell<BTreeMap<(FdId, i32), Rc<RefCell<EpollEventInterest>>>>,
     /// A map of EpollEventInstance that will be returned when `epoll_wait` is called.
     /// Similar to interest_list, the entry is also differentiated using FdId
     /// and file descriptor value.
@@ -35,6 +35,7 @@ impl EpollEventInstance {
         EpollEventInstance { events, data }
     }
 }
+
 /// EpollEventInterest registers the file description information to an epoll
 /// instance during a successful `epoll_ctl` call. It also stores additional
 /// information needed to check and update readiness state for `epoll_wait`.
@@ -226,18 +227,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         }
 
         // Check if epfd is a valid epoll file descriptor.
-        let Some(epfd) = this.machine.fds.get_ref(epfd_value) else {
+        let Some(epfd) = this.machine.fds.get(epfd_value) else {
             return Ok(Scalar::from_i32(this.fd_not_found()?));
         };
-        let mut binding = epfd.borrow_mut();
-        let epoll_file_description = &mut binding
-            .downcast_mut::<Epoll>()
+        let epoll_file_description = epfd
+            .downcast::<Epoll>()
             .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_ctl`"))?;
 
-        let interest_list = &mut epoll_file_description.interest_list;
+        let mut interest_list = epoll_file_description.interest_list.borrow_mut();
         let ready_list = &epoll_file_description.ready_list;
 
-        let Some(file_descriptor) = this.machine.fds.get_ref(fd) else {
+        let Some(file_descriptor) = this.machine.fds.get(fd) else {
             return Ok(Scalar::from_i32(this.fd_not_found()?));
         };
         let id = file_descriptor.get_id();
@@ -310,7 +310,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
 
             // Readiness will be updated immediately when the epoll_event_interest is added or modified.
-            file_descriptor.check_and_update_readiness(this)?;
+            this.check_and_update_readiness(&file_descriptor)?;
 
             return Ok(Scalar::from_i32(0));
         } else if op == epoll_ctl_del {
@@ -399,16 +399,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             throw_unsup_format!("epoll_wait: timeout value can only be 0");
         }
 
-        let Some(epfd) = this.machine.fds.get_ref(epfd) else {
+        let Some(epfd) = this.machine.fds.get(epfd) else {
             return Ok(Scalar::from_i32(this.fd_not_found()?));
         };
-        let mut binding = epfd.borrow_mut();
-        let epoll_file_description = &mut binding
-            .downcast_mut::<Epoll>()
+        let epoll_file_description = epfd
+            .downcast::<Epoll>()
             .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_wait`"))?;
 
-        let binding = epoll_file_description.get_ready_list();
-        let mut ready_list = binding.borrow_mut();
+        let ready_list = epoll_file_description.get_ready_list();
+        let mut ready_list = ready_list.borrow_mut();
         let mut num_of_events: i32 = 0;
         let mut array_iter = this.project_array_fields(&event)?;
 
@@ -434,22 +433,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         Ok(Scalar::from_i32(num_of_events))
     }
 
-    /// For a specific unique file descriptor id, get its ready events and update
-    /// the corresponding ready list. This function is called whenever a file description
-    /// is registered with epoll, or when read, write, or close operations are performed,
-    /// regardless of any changes in readiness.
+    /// For a specific file description, get its ready events and update the corresponding ready
+    /// list. This function should be called whenever an event causes more bytes or an EOF to become
+    /// newly readable from an FD, and whenever more bytes can be written to an FD or no more future
+    /// writes are possible.
     ///
-    /// This is an internal helper function and is typically not meant to be used directly.
-    /// In most cases, `FileDescriptionRef::check_and_update_readiness` should be preferred.
-    fn check_and_update_readiness(
-        &self,
-        id: FdId,
-        get_ready_events: impl FnOnce() -> InterpResult<'tcx, EpollReadyEvents>,
-    ) -> InterpResult<'tcx, ()> {
+    /// This *will* report an event if anyone is subscribed to it, without any further filtering, so
+    /// do not call this function when an FD didn't have anything happen to it!
+    fn check_and_update_readiness(&self, fd_ref: &FileDescriptionRef) -> InterpResult<'tcx, ()> {
         let this = self.eval_context_ref();
+        let id = fd_ref.get_id();
         // Get a list of EpollEventInterest that is associated to a specific file description.
         if let Some(epoll_interests) = this.machine.epoll_interests.get_epoll_interest(id) {
-            let epoll_ready_events = get_ready_events()?;
+            let epoll_ready_events = fd_ref.get_epoll_ready_events()?;
             // Get the bitmask of ready events.
             let ready_events = epoll_ready_events.get_event_bitmask(this);
 
diff --git a/src/tools/miri/src/shims/unix/linux/eventfd.rs b/src/tools/miri/src/shims/unix/linux/eventfd.rs
index 8a11f225b22..77d16a032aa 100644
--- a/src/tools/miri/src/shims/unix/linux/eventfd.rs
+++ b/src/tools/miri/src/shims/unix/linux/eventfd.rs
@@ -1,12 +1,13 @@
 //! Linux `eventfd` implementation.
+use std::cell::{Cell, RefCell};
 use std::io;
 use std::io::{Error, ErrorKind};
 use std::mem;
 
-use fd::FdId;
 use rustc_target::abi::Endian;
 
-use crate::shims::unix::linux::epoll::EpollReadyEvents;
+use crate::shims::unix::fd::FileDescriptionRef;
+use crate::shims::unix::linux::epoll::{EpollReadyEvents, EvalContextExt as _};
 use crate::shims::unix::*;
 use crate::{concurrency::VClock, *};
 
@@ -27,9 +28,9 @@ const MAX_COUNTER: u64 = u64::MAX - 1;
 struct Event {
     /// The object contains an unsigned 64-bit integer (uint64_t) counter that is maintained by the
     /// kernel. This counter is initialized with the value specified in the argument initval.
-    counter: u64,
+    counter: Cell<u64>,
     is_nonblock: bool,
-    clock: VClock,
+    clock: RefCell<VClock>,
 }
 
 impl FileDescription for Event {
@@ -42,8 +43,8 @@ impl FileDescription for Event {
         // need to be supported in the future, the check should be added here.
 
         Ok(EpollReadyEvents {
-            epollin: self.counter != 0,
-            epollout: self.counter != MAX_COUNTER,
+            epollin: self.counter.get() != 0,
+            epollout: self.counter.get() != MAX_COUNTER,
             ..EpollReadyEvents::new()
         })
     }
@@ -58,9 +59,9 @@ impl FileDescription for Event {
 
     /// Read the counter in the buffer and return the counter if succeeded.
     fn read<'tcx>(
-        &mut self,
+        &self,
+        self_ref: &FileDescriptionRef,
         _communicate_allowed: bool,
-        fd_id: FdId,
         bytes: &mut [u8],
         ecx: &mut MiriInterpCx<'tcx>,
     ) -> InterpResult<'tcx, io::Result<usize>> {
@@ -69,7 +70,8 @@ impl FileDescription for Event {
             return Ok(Err(Error::from(ErrorKind::InvalidInput)));
         };
         // Block when counter == 0.
-        if self.counter == 0 {
+        let counter = self.counter.get();
+        if counter == 0 {
             if self.is_nonblock {
                 return Ok(Err(Error::from(ErrorKind::WouldBlock)));
             } else {
@@ -78,25 +80,17 @@ impl FileDescription for Event {
             }
         } else {
             // Synchronize with all prior `write` calls to this FD.
-            ecx.acquire_clock(&self.clock);
+            ecx.acquire_clock(&self.clock.borrow());
             // Return the counter in the host endianness using the buffer provided by caller.
             *bytes = match ecx.tcx.sess.target.endian {
-                Endian::Little => self.counter.to_le_bytes(),
-                Endian::Big => self.counter.to_be_bytes(),
+                Endian::Little => counter.to_le_bytes(),
+                Endian::Big => counter.to_be_bytes(),
             };
-            self.counter = 0;
+            self.counter.set(0);
             // When any of the event happened, we check and update the status of all supported event
             // types for current file description.
+            ecx.check_and_update_readiness(self_ref)?;
 
-            // We have to use our own FdID in contrast to every other file descriptor out there, because
-            // we are updating ourselves when writing and reading. Technically `Event` is like socketpair, but
-            // it does not create two separate file descriptors. Thus we can't re-borrow ourselves via
-            // `FileDescriptionRef::check_and_update_readiness` while already being mutably borrowed for read/write.
-            crate::shims::unix::linux::epoll::EvalContextExt::check_and_update_readiness(
-                ecx,
-                fd_id,
-                || self.get_epoll_ready_events(),
-            )?;
             return Ok(Ok(U64_ARRAY_SIZE));
         }
     }
@@ -114,9 +108,9 @@ impl FileDescription for Event {
     /// supplied buffer is less than 8 bytes, or if an attempt is
     /// made to write the value 0xffffffffffffffff.
     fn write<'tcx>(
-        &mut self,
+        &self,
+        self_ref: &FileDescriptionRef,
         _communicate_allowed: bool,
-        fd_id: FdId,
         bytes: &[u8],
         ecx: &mut MiriInterpCx<'tcx>,
     ) -> InterpResult<'tcx, io::Result<usize>> {
@@ -135,13 +129,13 @@ impl FileDescription for Event {
         }
         // If the addition does not let the counter to exceed the maximum value, update the counter.
         // Else, block.
-        match self.counter.checked_add(num) {
+        match self.counter.get().checked_add(num) {
             Some(new_count @ 0..=MAX_COUNTER) => {
                 // Future `read` calls will synchronize with this write, so update the FD clock.
                 if let Some(clock) = &ecx.release_clock() {
-                    self.clock.join(clock);
+                    self.clock.borrow_mut().join(clock);
                 }
-                self.counter = new_count;
+                self.counter.set(new_count);
             }
             None | Some(u64::MAX) => {
                 if self.is_nonblock {
@@ -154,15 +148,8 @@ impl FileDescription for Event {
         };
         // When any of the event happened, we check and update the status of all supported event
         // types for current file description.
+        ecx.check_and_update_readiness(self_ref)?;
 
-        // Just like read() above, we use this internal method to not get the second borrow of the
-        // RefCell of this FileDescription. This is a special case, we should only use
-        // FileDescriptionRef::check_and_update_readiness in normal case.
-        crate::shims::unix::linux::epoll::EvalContextExt::check_and_update_readiness(
-            ecx,
-            fd_id,
-            || self.get_epoll_ready_events(),
-        )?;
         Ok(Ok(U64_ARRAY_SIZE))
     }
 }
@@ -219,8 +206,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         let fds = &mut this.machine.fds;
 
-        let fd_value =
-            fds.insert_new(Event { counter: val.into(), is_nonblock, clock: VClock::default() });
+        let fd_value = fds.insert_new(Event {
+            counter: Cell::new(val.into()),
+            is_nonblock,
+            clock: RefCell::new(VClock::default()),
+        });
 
         Ok(Scalar::from_i32(fd_value))
     }
diff --git a/src/tools/miri/src/shims/unix/socket.rs b/src/tools/miri/src/shims/unix/socket.rs
index 0f40d9776bb..75d4d0dbbe4 100644
--- a/src/tools/miri/src/shims/unix/socket.rs
+++ b/src/tools/miri/src/shims/unix/socket.rs
@@ -1,11 +1,10 @@
-use std::cell::RefCell;
+use std::cell::{OnceCell, RefCell};
 use std::collections::VecDeque;
 use std::io;
 use std::io::{Error, ErrorKind, Read};
-use std::rc::{Rc, Weak};
 
-use crate::shims::unix::fd::{FdId, WeakFileDescriptionRef};
-use crate::shims::unix::linux::epoll::EpollReadyEvents;
+use crate::shims::unix::fd::{FileDescriptionRef, WeakFileDescriptionRef};
+use crate::shims::unix::linux::epoll::{EpollReadyEvents, EvalContextExt as _};
 use crate::shims::unix::*;
 use crate::{concurrency::VClock, *};
 
@@ -17,15 +16,12 @@ const MAX_SOCKETPAIR_BUFFER_CAPACITY: usize = 212992;
 /// Pair of connected sockets.
 #[derive(Debug)]
 struct SocketPair {
-    // By making the write link weak, a `write` can detect when all readers are
-    // gone, and trigger EPIPE as appropriate.
-    writebuf: Weak<RefCell<Buffer>>,
-    readbuf: Rc<RefCell<Buffer>>,
-    /// When a socketpair instance is created, two socketpair file descriptions are generated.
-    /// The peer_fd field holds a weak reference to the file description of peer socketpair.
-    // TODO: It might be possible to retrieve writebuf from peer_fd and remove the writebuf
-    // field above.
-    peer_fd: WeakFileDescriptionRef,
+    /// The buffer we are reading from.
+    readbuf: RefCell<Buffer>,
+    /// The `SocketPair` file descriptor that is our "peer", and that holds the buffer we are
+    /// writing to. This is a weak reference because the other side may be closed before us; all
+    /// future writes will then trigger EPIPE.
+    peer_fd: OnceCell<WeakFileDescriptionRef>,
     is_nonblock: bool,
 }
 
@@ -33,10 +29,18 @@ struct SocketPair {
 struct Buffer {
     buf: VecDeque<u8>,
     clock: VClock,
-    /// Indicates if there is at least one active writer to this buffer.
-    /// If all writers of this buffer are dropped, buf_has_writer becomes false and we
-    /// indicate EOF instead of blocking.
-    buf_has_writer: bool,
+}
+
+impl Buffer {
+    fn new() -> Self {
+        Buffer { buf: VecDeque::new(), clock: VClock::default() }
+    }
+}
+
+impl SocketPair {
+    fn peer_fd(&self) -> &WeakFileDescriptionRef {
+        self.peer_fd.get().unwrap()
+    }
 }
 
 impl FileDescription for SocketPair {
@@ -49,29 +53,29 @@ impl FileDescription for SocketPair {
         // need to be supported in the future, the check should be added here.
 
         let mut epoll_ready_events = EpollReadyEvents::new();
-        let readbuf = self.readbuf.borrow();
 
         // Check if it is readable.
+        let readbuf = self.readbuf.borrow();
         if !readbuf.buf.is_empty() {
             epoll_ready_events.epollin = true;
         }
 
         // Check if is writable.
-        if let Some(writebuf) = self.writebuf.upgrade() {
-            let writebuf = writebuf.borrow();
+        if let Some(peer_fd) = self.peer_fd().upgrade() {
+            let writebuf = &peer_fd.downcast::<SocketPair>().unwrap().readbuf.borrow();
             let data_size = writebuf.buf.len();
             let available_space = MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(data_size);
             if available_space != 0 {
                 epoll_ready_events.epollout = true;
             }
-        }
-
-        // Check if the peer_fd closed
-        if self.peer_fd.upgrade().is_none() {
+        } else {
+            // Peer FD has been closed.
             epoll_ready_events.epollrdhup = true;
-            // This is an edge case. Whenever epollrdhup is triggered, epollin will be added
-            // even though there is no data in the buffer.
+            // Since the peer is closed, even if no data is available reads will return EOF and
+            // writes will return EPIPE. In other words, they won't block, so we mark this as ready
+            // for read and write.
             epoll_ready_events.epollin = true;
+            epoll_ready_events.epollout = true;
         }
         Ok(epoll_ready_events)
     }
@@ -81,39 +85,31 @@ impl FileDescription for SocketPair {
         _communicate_allowed: bool,
         ecx: &mut MiriInterpCx<'tcx>,
     ) -> InterpResult<'tcx, io::Result<()>> {
-        // This is used to signal socketfd of other side that there is no writer to its readbuf.
-        // If the upgrade fails, there is no need to update as all read ends have been dropped.
-        if let Some(writebuf) = self.writebuf.upgrade() {
-            writebuf.borrow_mut().buf_has_writer = false;
-        };
-
-        // Notify peer fd that closed has happened.
-        if let Some(peer_fd) = self.peer_fd.upgrade() {
-            // When any of the event happened, we check and update the status of all supported events
-            // types of peer fd.
-            peer_fd.check_and_update_readiness(ecx)?;
+        if let Some(peer_fd) = self.peer_fd().upgrade() {
+            // Notify peer fd that close has happened, since that can unblock reads and writes.
+            ecx.check_and_update_readiness(&peer_fd)?;
         }
         Ok(Ok(()))
     }
 
     fn read<'tcx>(
-        &mut self,
+        &self,
+        _self_ref: &FileDescriptionRef,
         _communicate_allowed: bool,
-        _fd_id: FdId,
         bytes: &mut [u8],
         ecx: &mut MiriInterpCx<'tcx>,
     ) -> InterpResult<'tcx, io::Result<usize>> {
         let request_byte_size = bytes.len();
-        let mut readbuf = self.readbuf.borrow_mut();
 
         // Always succeed on read size 0.
         if request_byte_size == 0 {
             return Ok(Ok(0));
         }
 
+        let mut readbuf = self.readbuf.borrow_mut();
         if readbuf.buf.is_empty() {
-            if !readbuf.buf_has_writer {
-                // Socketpair with no writer and empty buffer.
+            if self.peer_fd().upgrade().is_none() {
+                // Socketpair with no peer and empty buffer.
                 // 0 bytes successfully read indicates end-of-file.
                 return Ok(Ok(0));
             } else {
@@ -141,8 +137,7 @@ impl FileDescription for SocketPair {
         // Conveniently, `read` exists on `VecDeque` and has exactly the desired behavior.
         let actual_read_size = readbuf.buf.read(bytes).unwrap();
 
-        // The readbuf needs to be explicitly dropped because it will cause panic when
-        // check_and_update_readiness borrows it again.
+        // Need to drop before others can access the readbuf again.
         drop(readbuf);
 
         // A notification should be provided for the peer file description even when it can
@@ -152,17 +147,17 @@ impl FileDescription for SocketPair {
         // don't know what that *certain number* is, we will provide a notification every time
         // a read is successful. This might result in our epoll emulation providing more
         // notifications than the real system.
-        if let Some(peer_fd) = self.peer_fd.upgrade() {
-            peer_fd.check_and_update_readiness(ecx)?;
+        if let Some(peer_fd) = self.peer_fd().upgrade() {
+            ecx.check_and_update_readiness(&peer_fd)?;
         }
 
         return Ok(Ok(actual_read_size));
     }
 
     fn write<'tcx>(
-        &mut self,
+        &self,
+        _self_ref: &FileDescriptionRef,
         _communicate_allowed: bool,
-        _fd_id: FdId,
         bytes: &[u8],
         ecx: &mut MiriInterpCx<'tcx>,
     ) -> InterpResult<'tcx, io::Result<usize>> {
@@ -173,12 +168,13 @@ impl FileDescription for SocketPair {
             return Ok(Ok(0));
         }
 
-        let Some(writebuf) = self.writebuf.upgrade() else {
+        // We are writing to our peer's readbuf.
+        let Some(peer_fd) = self.peer_fd().upgrade() else {
             // If the upgrade from Weak to Rc fails, it indicates that all read ends have been
             // closed.
             return Ok(Err(Error::from(ErrorKind::BrokenPipe)));
         };
-        let mut writebuf = writebuf.borrow_mut();
+        let mut writebuf = peer_fd.downcast::<SocketPair>().unwrap().readbuf.borrow_mut();
         let data_size = writebuf.buf.len();
         let available_space = MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(data_size);
         if available_space == 0 {
@@ -198,13 +194,13 @@ impl FileDescription for SocketPair {
         let actual_write_size = write_size.min(available_space);
         writebuf.buf.extend(&bytes[..actual_write_size]);
 
-        // The writebuf needs to be explicitly dropped because it will cause panic when
-        // check_and_update_readiness borrows it again.
+        // Need to stop accessing peer_fd so that it can be notified.
         drop(writebuf);
+
         // Notification should be provided for peer fd as it became readable.
-        if let Some(peer_fd) = self.peer_fd.upgrade() {
-            peer_fd.check_and_update_readiness(ecx)?;
-        }
+        // The kernel does this even if the fd was already readable before, so we follow suit.
+        ecx.check_and_update_readiness(&peer_fd)?;
+
         return Ok(Ok(actual_write_size));
     }
 }
@@ -268,51 +264,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             );
         }
 
-        let buffer1 = Rc::new(RefCell::new(Buffer {
-            buf: VecDeque::new(),
-            clock: VClock::default(),
-            buf_has_writer: true,
-        }));
-
-        let buffer2 = Rc::new(RefCell::new(Buffer {
-            buf: VecDeque::new(),
-            clock: VClock::default(),
-            buf_has_writer: true,
-        }));
-
-        let socketpair_0 = SocketPair {
-            writebuf: Rc::downgrade(&buffer1),
-            readbuf: Rc::clone(&buffer2),
-            peer_fd: WeakFileDescriptionRef::default(),
+        // Generate file descriptions.
+        let fds = &mut this.machine.fds;
+        let fd0 = fds.new_ref(SocketPair {
+            readbuf: RefCell::new(Buffer::new()),
+            peer_fd: OnceCell::new(),
             is_nonblock: is_sock_nonblock,
-        };
-        let socketpair_1 = SocketPair {
-            writebuf: Rc::downgrade(&buffer2),
-            readbuf: Rc::clone(&buffer1),
-            peer_fd: WeakFileDescriptionRef::default(),
+        });
+        let fd1 = fds.new_ref(SocketPair {
+            readbuf: RefCell::new(Buffer::new()),
+            peer_fd: OnceCell::new(),
             is_nonblock: is_sock_nonblock,
-        };
-
-        // Insert the file description to the fd table.
-        let fds = &mut this.machine.fds;
-        let sv0 = fds.insert_new(socketpair_0);
-        let sv1 = fds.insert_new(socketpair_1);
-
-        // Get weak file descriptor and file description id value.
-        let fd_ref0 = fds.get_ref(sv0).unwrap();
-        let fd_ref1 = fds.get_ref(sv1).unwrap();
-        let weak_fd_ref0 = fd_ref0.downgrade();
-        let weak_fd_ref1 = fd_ref1.downgrade();
+        });
 
-        // Update peer_fd and id field.
-        fd_ref1.borrow_mut().downcast_mut::<SocketPair>().unwrap().peer_fd = weak_fd_ref0;
+        // Make the file descriptions point to each other.
+        fd0.downcast::<SocketPair>().unwrap().peer_fd.set(fd1.downgrade()).unwrap();
+        fd1.downcast::<SocketPair>().unwrap().peer_fd.set(fd0.downgrade()).unwrap();
 
-        fd_ref0.borrow_mut().downcast_mut::<SocketPair>().unwrap().peer_fd = weak_fd_ref1;
+        // Insert the file description to the fd table, generating the file descriptors.
+        let sv0 = fds.insert(fd0);
+        let sv1 = fds.insert(fd1);
 
-        // Return socketpair file description value to the caller.
+        // Return socketpair file descriptors to the caller.
         let sv0 = Scalar::from_int(sv0, sv.layout.size);
         let sv1 = Scalar::from_int(sv1, sv.layout.size);
-
         this.write_scalar(sv0, &sv)?;
         this.write_scalar(sv1, &sv.offset(sv.layout.size, sv.layout, this)?)?;
 
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll.rs
index 11a0257dc4e..841761e53dd 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-epoll.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll.rs
@@ -5,34 +5,40 @@ use std::convert::TryInto;
 use std::mem::MaybeUninit;
 
 fn main() {
+    test_epoll_socketpair();
+    test_epoll_socketpair_both_sides();
+    test_socketpair_read();
+    test_epoll_eventfd();
+
     test_event_overwrite();
     test_not_fully_closed_fd();
     test_closed_fd();
-    test_epoll_socketpair_special_case();
     test_two_epoll_instance();
     test_epoll_ctl_mod();
-    test_epoll_socketpair();
-    test_epoll_eventfd();
     test_epoll_ctl_del();
     test_pointer();
     test_two_same_fd_in_same_epoll_instance();
-    test_socketpair_read();
 }
 
-fn check_epoll_wait<const N: usize>(
-    epfd: i32,
-    mut expected_notifications: Vec<(u32, u64)>,
-) -> bool {
+// Using `as` cast since `EPOLLET` wraps around
+const EPOLL_IN_OUT_ET: u32 = (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET) as _;
+
+#[track_caller]
+fn check_epoll_wait<const N: usize>(epfd: i32, expected_notifications: &[(u32, u64)]) -> bool {
     let epoll_event = libc::epoll_event { events: 0, u64: 0 };
     let mut array: [libc::epoll_event; N] = [epoll_event; N];
     let maxsize = N;
     let array_ptr = array.as_mut_ptr();
     let res = unsafe { libc::epoll_wait(epfd, array_ptr, maxsize.try_into().unwrap(), 0) };
+    if res < 0 {
+        panic!("epoll_wait failed: {}", std::io::Error::last_os_error());
+    }
     assert_eq!(res, expected_notifications.len().try_into().unwrap());
     let slice = unsafe { std::slice::from_raw_parts(array_ptr, res.try_into().unwrap()) };
     let mut return_events = slice.iter();
+    let mut expected_notifications = expected_notifications.iter();
     while let Some(return_event) = return_events.next() {
-        if let Some(notification) = expected_notifications.pop() {
+        if let Some(notification) = expected_notifications.next() {
             let event = return_event.events;
             let data = return_event.u64;
             assert_eq!(event, notification.0);
@@ -41,10 +47,8 @@ fn check_epoll_wait<const N: usize>(
             return false;
         }
     }
-    if !expected_notifications.is_empty() {
-        return false;
-    }
-    return true;
+    // There shouldn't be any more expected.
+    return expected_notifications.next().is_none();
 }
 
 fn test_epoll_socketpair() {
@@ -54,21 +58,17 @@ fn test_epoll_socketpair() {
 
     // Create a socketpair instance.
     let mut fds = [-1, -1];
-    let mut res =
-        unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
+    let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
     assert_eq!(res, 0);
 
     // Write to fd[0]
     let data = "abcde".as_bytes().as_ptr();
-    res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5).try_into().unwrap() };
+    let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) };
     assert_eq!(res, 5);
 
-    // Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLET
-    // EPOLLET is negative number for i32 so casting is needed to do proper bitwise OR for u32.
-    let epollet = libc::EPOLLET as u32;
-    let flags = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLRDHUP).unwrap() | epollet;
+    // Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLET|EPOLLRDHUP
     let mut ev = libc::epoll_event {
-        events: u32::try_from(flags).unwrap(),
+        events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET | libc::EPOLLRDHUP) as _,
         u64: u64::try_from(fds[1]).unwrap(),
     };
     let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) };
@@ -77,16 +77,29 @@ fn test_epoll_socketpair() {
     // Check result from epoll_wait.
     let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap();
     let expected_value = u64::try_from(fds[1]).unwrap();
-    assert!(check_epoll_wait::<8>(epfd, vec![(expected_event, expected_value)]));
+    assert!(check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]));
+
+    // Check that this is indeed using "ET" (edge-trigger) semantics: a second epoll should return nothing.
+    assert!(check_epoll_wait::<8>(epfd, &[]));
+
+    // Write some more to fd[0].
+    let data = "abcde".as_bytes().as_ptr();
+    let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) };
+    assert_eq!(res, 5);
+
+    // This did not change the readiness of fd[1]. And yet, we're seeing the event reported
+    // again by the kernel, so Miri does the same.
+    assert!(check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]));
 
     // Close the peer socketpair.
     let res = unsafe { libc::close(fds[0]) };
     assert_eq!(res, 0);
 
     // Check result from epoll_wait.
+    // We expect to get a read, write, HUP notification from the close since closing an FD always unblocks reads and writes on its peer.
     let expected_event = u32::try_from(libc::EPOLLRDHUP | libc::EPOLLIN | libc::EPOLLOUT).unwrap();
     let expected_value = u64::try_from(fds[1]).unwrap();
-    assert!(check_epoll_wait::<8>(epfd, vec![(expected_event, expected_value)]));
+    assert!(check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]));
 }
 
 fn test_epoll_ctl_mod() {
@@ -96,35 +109,28 @@ fn test_epoll_ctl_mod() {
 
     // Create a socketpair instance.
     let mut fds = [-1, -1];
-    let mut res =
-        unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
+    let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
     assert_eq!(res, 0);
 
     // Write to fd[0].
     let data = "abcde".as_bytes().as_ptr();
-    res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5).try_into().unwrap() };
+    let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) };
     assert_eq!(res, 5);
 
     // Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLET.
-    // EPOLLET is negative number for i32 so casting is needed to do proper bitwise OR for u32.
-    let epollet = libc::EPOLLET as u32;
-    let mut flags = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap() | epollet;
-    let mut ev = libc::epoll_event {
-        events: u32::try_from(flags).unwrap(),
-        u64: u64::try_from(fds[1]).unwrap(),
-    };
+    // (Not using checked cast as EPOLLET wraps around.)
+    let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: u64::try_from(fds[1]).unwrap() };
     let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) };
     assert_ne!(res, -1);
 
     // Check result from epoll_wait.
     let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap();
     let expected_value = u64::try_from(fds[1]).unwrap();
-    assert!(check_epoll_wait::<8>(epfd, vec![(expected_event, expected_value)]));
+    assert!(check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]));
 
     // Test EPOLLRDHUP.
-    flags |= u32::try_from(libc::EPOLLRDHUP).unwrap();
     let mut ev = libc::epoll_event {
-        events: u32::try_from(flags).unwrap(),
+        events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET | libc::EPOLLRDHUP) as _,
         u64: u64::try_from(fds[1]).unwrap(),
     };
     let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_MOD, fds[1], &mut ev) };
@@ -137,7 +143,7 @@ fn test_epoll_ctl_mod() {
     // Check result from epoll_wait.
     let expected_event = u32::try_from(libc::EPOLLRDHUP | libc::EPOLLIN | libc::EPOLLOUT).unwrap();
     let expected_value = u64::try_from(fds[1]).unwrap();
-    assert!(check_epoll_wait::<8>(epfd, vec![(expected_event, expected_value)]));
+    assert!(check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]));
 }
 
 fn test_epoll_ctl_del() {
@@ -147,28 +153,21 @@ fn test_epoll_ctl_del() {
 
     // Create a socketpair instance.
     let mut fds = [-1, -1];
-    let mut res =
-        unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
+    let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
     assert_eq!(res, 0);
 
     // Write to fd[0]
     let data = "abcde".as_bytes().as_ptr();
-    res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5).try_into().unwrap() };
+    let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) };
     assert_eq!(res, 5);
 
     // Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLET
-    // EPOLLET is negative number for i32 so casting is needed to do proper bitwise OR for u32.
-    let epollet = libc::EPOLLET as u32;
-    let flags = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap() | epollet;
-    let mut ev = libc::epoll_event {
-        events: u32::try_from(flags).unwrap(),
-        u64: u64::try_from(fds[1]).unwrap(),
-    };
+    let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: u64::try_from(fds[1]).unwrap() };
     let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) };
     assert_ne!(res, -1);
 
     // Test EPOLL_CTL_DEL.
-    assert!(check_epoll_wait::<0>(epfd, vec![]));
+    assert!(check_epoll_wait::<0>(epfd, &[]));
 }
 
 // This test is for one fd registered under two different epoll instance.
@@ -181,22 +180,16 @@ fn test_two_epoll_instance() {
 
     // Create a socketpair instance.
     let mut fds = [-1, -1];
-    let mut res =
-        unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
+    let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
     assert_eq!(res, 0);
 
     // Write to the socketpair.
     let data = "abcde".as_bytes().as_ptr();
-    res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5).try_into().unwrap() };
+    let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) };
     assert_eq!(res, 5);
 
     // Register one side of the socketpair with EPOLLIN | EPOLLOUT | EPOLLET.
-    let epollet = libc::EPOLLET as u32;
-    let flags = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap() | epollet;
-    let mut ev = libc::epoll_event {
-        events: u32::try_from(flags).unwrap(),
-        u64: u64::try_from(fds[1]).unwrap(),
-    };
+    let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: u64::try_from(fds[1]).unwrap() };
     let res = unsafe { libc::epoll_ctl(epfd1, libc::EPOLL_CTL_ADD, fds[1], &mut ev) };
     assert_ne!(res, -1);
     let res = unsafe { libc::epoll_ctl(epfd2, libc::EPOLL_CTL_ADD, fds[1], &mut ev) };
@@ -205,8 +198,8 @@ fn test_two_epoll_instance() {
     // Notification should be received from both instance of epoll.
     let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap();
     let expected_value = u64::try_from(fds[1]).unwrap();
-    assert!(check_epoll_wait::<8>(epfd1, vec![(expected_event, expected_value)]));
-    assert!(check_epoll_wait::<8>(epfd2, vec![(expected_event, expected_value)]));
+    assert!(check_epoll_wait::<8>(epfd1, &[(expected_event, expected_value)]));
+    assert!(check_epoll_wait::<8>(epfd2, &[(expected_event, expected_value)]));
 }
 
 // This test is for two same file description registered under the same epoll instance through dup.
@@ -226,17 +219,15 @@ fn test_two_same_fd_in_same_epoll_instance() {
     assert_ne!(newfd, -1);
 
     // Register both fd to the same epoll instance.
-    let epollet = libc::EPOLLET as u32;
-    let flags = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap() | epollet;
-    let mut ev = libc::epoll_event { events: u32::try_from(flags).unwrap(), u64: 5 as u64 };
-    let mut res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) };
+    let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: 5 as u64 };
+    let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) };
     assert_ne!(res, -1);
-    res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, newfd, &mut ev) };
+    let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, newfd, &mut ev) };
     assert_ne!(res, -1);
 
     // Write to the socketpair.
     let data = "abcde".as_bytes().as_ptr();
-    res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5).try_into().unwrap() };
+    let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) };
     assert_eq!(res, 5);
 
     //Two notification should be received.
@@ -244,7 +235,7 @@ fn test_two_same_fd_in_same_epoll_instance() {
     let expected_value = 5 as u64;
     assert!(check_epoll_wait::<8>(
         epfd,
-        vec![(expected_event, expected_value), (expected_event, expected_value)]
+        &[(expected_event, expected_value), (expected_event, expected_value)]
     ));
 }
 
@@ -255,9 +246,7 @@ fn test_epoll_eventfd() {
 
     // Write to the eventfd instance.
     let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes();
-    let res: i32 = unsafe {
-        libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
-    };
+    let res = unsafe { libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) };
     assert_eq!(res, 8);
 
     // Create an epoll instance.
@@ -265,20 +254,14 @@ fn test_epoll_eventfd() {
     assert_ne!(epfd, -1);
 
     // Register eventfd with EPOLLIN | EPOLLOUT | EPOLLET
-    // EPOLLET is negative number for i32 so casting is needed to do proper bitwise OR for u32.
-    let epollet = libc::EPOLLET as u32;
-    let flags = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap() | epollet;
-    let mut ev = libc::epoll_event {
-        events: u32::try_from(flags).unwrap(),
-        u64: u64::try_from(fd).unwrap(),
-    };
+    let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: u64::try_from(fd).unwrap() };
     let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd, &mut ev) };
     assert_ne!(res, -1);
 
     // Check result from epoll_wait.
     let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap();
     let expected_value = u64::try_from(fd).unwrap();
-    assert!(check_epoll_wait::<8>(epfd, vec![(expected_event, expected_value)]));
+    assert!(check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]));
 }
 
 fn test_pointer() {
@@ -292,20 +275,15 @@ fn test_pointer() {
     assert_eq!(res, 0);
 
     // Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLET
-    // EPOLLET is negative number for i32 so casting is needed to do proper bitwise OR for u32.
-    let epollet = libc::EPOLLET as u32;
-    let flags = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLRDHUP).unwrap() | epollet;
     let data = MaybeUninit::<u64>::uninit().as_ptr();
-    let mut ev = libc::epoll_event {
-        events: u32::try_from(flags).unwrap(),
-        u64: data.expose_provenance() as u64,
-    };
+    let mut ev =
+        libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: data.expose_provenance() as u64 };
     let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) };
     assert_ne!(res, -1);
 }
 
 // When read/write happened on one side of the socketpair, only the other side will be notified.
-fn test_epoll_socketpair_special_case() {
+fn test_epoll_socketpair_both_sides() {
     // Create an epoll instance.
     let epfd = unsafe { libc::epoll_create1(0) };
     assert_ne!(epfd, -1);
@@ -316,18 +294,16 @@ fn test_epoll_socketpair_special_case() {
     assert_eq!(res, 0);
 
     // Register both fd to the same epoll instance.
-    let epollet = libc::EPOLLET as u32;
-    let flags = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap() | epollet;
-    let mut ev = libc::epoll_event { events: u32::try_from(flags).unwrap(), u64: fds[0] as u64 };
-    let mut res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) };
+    let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[0] as u64 };
+    let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) };
     assert_ne!(res, -1);
-    let mut ev = libc::epoll_event { events: u32::try_from(flags).unwrap(), u64: fds[1] as u64 };
-    res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) };
+    let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[1] as u64 };
+    let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) };
     assert_ne!(res, -1);
 
     // Write to fds[1].
     let data = "abcde".as_bytes().as_ptr();
-    res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5).try_into().unwrap() };
+    let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) };
     assert_eq!(res, 5);
 
     //Two notification should be received.
@@ -337,21 +313,19 @@ fn test_epoll_socketpair_special_case() {
     let expected_value1 = fds[1] as u64;
     assert!(check_epoll_wait::<8>(
         epfd,
-        vec![(expected_event1, expected_value1), (expected_event0, expected_value0)]
+        &[(expected_event0, expected_value0), (expected_event1, expected_value1)]
     ));
 
     // Read from fds[0].
     let mut buf: [u8; 5] = [0; 5];
-    res = unsafe {
-        libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t).try_into().unwrap()
-    };
+    let res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
     assert_eq!(res, 5);
     assert_eq!(buf, "abcde".as_bytes());
 
     // Notification should be provided for fds[1].
     let expected_event = u32::try_from(libc::EPOLLOUT).unwrap();
     let expected_value = fds[1] as u64;
-    assert!(check_epoll_wait::<8>(epfd, vec![(expected_event, expected_value)]));
+    assert!(check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]));
 }
 
 // When file description is fully closed, epoll_wait should not provide any notification for
@@ -366,21 +340,13 @@ fn test_closed_fd() {
     let fd = unsafe { libc::eventfd(0, flags) };
 
     // Register eventfd with EPOLLIN | EPOLLOUT | EPOLLET
-    // EPOLLET is negative number for i32 so casting is needed to do proper bitwise OR for u32.
-    let epollet = libc::EPOLLET as u32;
-    let flags = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap() | epollet;
-    let mut ev = libc::epoll_event {
-        events: u32::try_from(flags).unwrap(),
-        u64: u64::try_from(fd).unwrap(),
-    };
+    let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: u64::try_from(fd).unwrap() };
     let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd, &mut ev) };
     assert_ne!(res, -1);
 
     // Write to the eventfd instance.
     let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes();
-    let res: i32 = unsafe {
-        libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
-    };
+    let res = unsafe { libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) };
     assert_eq!(res, 8);
 
     // Close the eventfd.
@@ -388,7 +354,7 @@ fn test_closed_fd() {
     assert_eq!(res, 0);
 
     // No notification should be provided because the file description is closed.
-    assert!(check_epoll_wait::<8>(epfd, vec![]));
+    assert!(check_epoll_wait::<8>(epfd, &[]));
 }
 
 // When a certain file descriptor registered with epoll is closed, but the underlying file description
@@ -411,13 +377,7 @@ fn test_not_fully_closed_fd() {
     assert_ne!(newfd, -1);
 
     // Register eventfd with EPOLLIN | EPOLLOUT | EPOLLET
-    // EPOLLET is negative number for i32 so casting is needed to do proper bitwise OR for u32.
-    let epollet = libc::EPOLLET as u32;
-    let flags = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap() | epollet;
-    let mut ev = libc::epoll_event {
-        events: u32::try_from(flags).unwrap(),
-        u64: u64::try_from(fd).unwrap(),
-    };
+    let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: u64::try_from(fd).unwrap() };
     let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd, &mut ev) };
     assert_ne!(res, -1);
 
@@ -428,13 +388,11 @@ fn test_not_fully_closed_fd() {
     // Notification should still be provided because the file description is not closed.
     let expected_event = u32::try_from(libc::EPOLLOUT).unwrap();
     let expected_value = fd as u64;
-    assert!(check_epoll_wait::<1>(epfd, vec![(expected_event, expected_value)]));
+    assert!(check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)]));
 
     // Write to the eventfd instance to produce notification.
     let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes();
-    let res: i32 = unsafe {
-        libc::write(newfd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
-    };
+    let res = unsafe { libc::write(newfd, sized_8_data.as_ptr() as *const libc::c_void, 8) };
     assert_eq!(res, 8);
 
     // Close the dupped fd.
@@ -442,7 +400,7 @@ fn test_not_fully_closed_fd() {
     assert_eq!(res, 0);
 
     // No notification should be provided.
-    assert!(check_epoll_wait::<1>(epfd, vec![]));
+    assert!(check_epoll_wait::<1>(epfd, &[]));
 }
 
 // Each time a notification is provided, it should reflect the file description's readiness
@@ -454,9 +412,7 @@ fn test_event_overwrite() {
 
     // Write to the eventfd instance.
     let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes();
-    let res: i32 = unsafe {
-        libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
-    };
+    let res = unsafe { libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) };
     assert_eq!(res, 8);
 
     // Create an epoll instance.
@@ -464,11 +420,8 @@ fn test_event_overwrite() {
     assert_ne!(epfd, -1);
 
     // Register eventfd with EPOLLIN | EPOLLOUT | EPOLLET
-    // EPOLLET is negative number for i32 so casting is needed to do proper bitwise OR for u32.
-    let epollet = libc::EPOLLET as u32;
-    let flags = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap() | epollet;
     let mut ev = libc::epoll_event {
-        events: u32::try_from(flags).unwrap(),
+        events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET) as _,
         u64: u64::try_from(fd).unwrap(),
     };
     let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd, &mut ev) };
@@ -476,13 +429,13 @@ fn test_event_overwrite() {
 
     // Read from the eventfd instance.
     let mut buf: [u8; 8] = [0; 8];
-    let res: i32 = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), 8).try_into().unwrap() };
+    let res = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), 8) };
     assert_eq!(res, 8);
 
     // Check result from epoll_wait.
     let expected_event = u32::try_from(libc::EPOLLOUT).unwrap();
     let expected_value = u64::try_from(fd).unwrap();
-    assert!(check_epoll_wait::<8>(epfd, vec![(expected_event, expected_value)]));
+    assert!(check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]));
 }
 
 // An epoll notification will be provided for every succesful read in a socketpair.
@@ -498,18 +451,22 @@ fn test_socketpair_read() {
     assert_eq!(res, 0);
 
     // Register both fd to the same epoll instance.
-    let epollet = libc::EPOLLET as u32;
-    let flags = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap() | epollet;
-    let mut ev = libc::epoll_event { events: u32::try_from(flags).unwrap(), u64: fds[0] as u64 };
-    let mut res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) };
+    let mut ev = libc::epoll_event {
+        events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET) as _,
+        u64: fds[0] as u64,
+    };
+    let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) };
     assert_ne!(res, -1);
-    let mut ev = libc::epoll_event { events: u32::try_from(flags).unwrap(), u64: fds[1] as u64 };
-    res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) };
+    let mut ev = libc::epoll_event {
+        events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET) as _,
+        u64: fds[1] as u64,
+    };
+    let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) };
     assert_ne!(res, -1);
 
     // Write 5 bytes to fds[1].
     let data = "abcde".as_bytes().as_ptr();
-    res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5).try_into().unwrap() };
+    let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) };
     assert_eq!(res, 5);
 
     //Two notification should be received.
@@ -519,14 +476,12 @@ fn test_socketpair_read() {
     let expected_value1 = fds[1] as u64;
     assert!(check_epoll_wait::<8>(
         epfd,
-        vec![(expected_event1, expected_value1), (expected_event0, expected_value0)]
+        &[(expected_event0, expected_value0), (expected_event1, expected_value1)]
     ));
 
     // Read 3 bytes from fds[0].
     let mut buf: [u8; 3] = [0; 3];
-    res = unsafe {
-        libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t).try_into().unwrap()
-    };
+    let res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
     assert_eq!(res, 3);
     assert_eq!(buf, "abc".as_bytes());
 
@@ -534,13 +489,11 @@ fn test_socketpair_read() {
     // But in real system, no notification will be provided here.
     let expected_event = u32::try_from(libc::EPOLLOUT).unwrap();
     let expected_value = fds[1] as u64;
-    assert!(check_epoll_wait::<8>(epfd, vec![(expected_event, expected_value)]));
+    assert!(check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]));
 
     // Read until the buffer is empty.
     let mut buf: [u8; 2] = [0; 2];
-    res = unsafe {
-        libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t).try_into().unwrap()
-    };
+    let res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
     assert_eq!(res, 2);
     assert_eq!(buf, "de".as_bytes());
 
@@ -548,5 +501,5 @@ fn test_socketpair_read() {
     // In real system, notification will be provided too.
     let expected_event = u32::try_from(libc::EPOLLOUT).unwrap();
     let expected_value = fds[1] as u64;
-    assert!(check_epoll_wait::<8>(epfd, vec![(expected_event, expected_value)]));
+    assert!(check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]));
 }