about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-02-03 17:47:10 +0000
committerbors <bors@rust-lang.org>2023-02-03 17:47:10 +0000
commit929bc0f8e092c6430a843f3c0dd72bd287bcdb8a (patch)
tree38adc3226493ea49df50a31e200bdcb612603181
parent104048f81b226d667208ed4497368906a9120ec9 (diff)
parent15b9f45cca87abf30415daefaa85218756e994ed (diff)
downloadrust-929bc0f8e092c6430a843f3c0dd72bd287bcdb8a.tar.gz
rust-929bc0f8e092c6430a843f3c0dd72bd287bcdb8a.zip
Auto merge of #2764 - DebugSteven:sleep, r=oli-obk
Implement epoll_wait

This PR continues working on https://github.com/rust-lang/miri/issues/602.

This is an implementation for `sleep`, though admittedly not a good one. It just does busy waiting.
-rw-r--r--src/tools/miri/src/shims/unix/linux/fd.rs58
-rw-r--r--src/tools/miri/src/shims/unix/linux/fd/event.rs35
-rw-r--r--src/tools/miri/src/shims/unix/linux/foreign_items.rs6
-rw-r--r--src/tools/miri/tests/pass-dep/tokio/sleep.rs14
-rw-r--r--src/tools/miri/tests/pass-dep/tokio/tokio_mvp.rs (renamed from src/tools/miri/tests/pass-dep/tokio_mvp.rs)2
5 files changed, 111 insertions, 4 deletions
diff --git a/src/tools/miri/src/shims/unix/linux/fd.rs b/src/tools/miri/src/shims/unix/linux/fd.rs
index 212b7936341..fd4927fa10c 100644
--- a/src/tools/miri/src/shims/unix/linux/fd.rs
+++ b/src/tools/miri/src/shims/unix/linux/fd.rs
@@ -7,6 +7,8 @@ use socketpair::SocketPair;
 
 use shims::unix::fs::EvalContextExt as _;
 
+use std::cell::Cell;
+
 pub mod epoll;
 pub mod event;
 pub mod socketpair;
@@ -101,6 +103,60 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         }
     }
 
+    /// The `epoll_wait()` system call waits for events on the `Epoll`
+    /// instance referred to by the file descriptor `epfd`. The buffer
+    /// pointed to by `events` is used to return information from the ready
+    /// list about file descriptors in the interest list that have some
+    /// events available. Up to `maxevents` are returned by `epoll_wait()`.
+    /// The `maxevents` argument must be greater than zero.
+
+    /// The `timeout` argument specifies the number of milliseconds that
+    /// `epoll_wait()` will block. Time is measured against the
+    /// CLOCK_MONOTONIC clock.
+
+    /// A call to `epoll_wait()` will block until either:
+    /// • a file descriptor delivers an event;
+    /// • the call is interrupted by a signal handler; or
+    /// • the timeout expires.
+
+    /// Note that the timeout interval will be rounded up to the system
+    /// clock granularity, and kernel scheduling delays mean that the
+    /// blocking interval may overrun by a small amount. Specifying a
+    /// timeout of -1 causes `epoll_wait()` to block indefinitely, while
+    /// specifying a timeout equal to zero cause `epoll_wait()` to return
+    /// immediately, even if no events are available.
+    ///
+    /// On success, `epoll_wait()` returns the number of file descriptors
+    /// ready for the requested I/O, or zero if no file descriptor became
+    /// ready during the requested timeout milliseconds. On failure,
+    /// `epoll_wait()` returns -1 and errno is set to indicate the error.
+    ///
+    /// <https://man7.org/linux/man-pages/man2/epoll_wait.2.html>
+    fn epoll_wait(
+        &mut self,
+        epfd: &OpTy<'tcx, Provenance>,
+        events: &OpTy<'tcx, Provenance>,
+        maxevents: &OpTy<'tcx, Provenance>,
+        timeout: &OpTy<'tcx, Provenance>,
+    ) -> InterpResult<'tcx, Scalar<Provenance>> {
+        let this = self.eval_context_mut();
+
+        let epfd = this.read_scalar(epfd)?.to_i32()?;
+        let _events = this.read_scalar(events)?.to_pointer(this)?;
+        let _maxevents = this.read_scalar(maxevents)?.to_i32()?;
+        let _timeout = this.read_scalar(timeout)?.to_i32()?;
+
+        let numevents = 0;
+        if let Some(epfd) = this.machine.file_handler.handles.get_mut(&epfd) {
+            let _epfd = epfd.as_epoll_handle()?;
+
+            // FIXME return number of events ready when scheme for marking events ready exists
+            Ok(Scalar::from_i32(numevents))
+        } else {
+            Ok(Scalar::from_i32(this.handle_not_found()?))
+        }
+    }
+
     /// This function creates an `Event` that is used as an event wait/notify mechanism by
     /// user-space applications, and by the kernel to notify user-space applications of events.
     /// The `Event` contains an `u64` counter maintained by the kernel. The counter is initialized
@@ -142,7 +198,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         }
 
         let fh = &mut this.machine.file_handler;
-        let fd = fh.insert_fd(Box::new(Event { val }));
+        let fd = fh.insert_fd(Box::new(Event { val: Cell::new(val.into()) }));
         Ok(Scalar::from_i32(fd))
     }
 
diff --git a/src/tools/miri/src/shims/unix/linux/fd/event.rs b/src/tools/miri/src/shims/unix/linux/fd/event.rs
index 239eb462a1d..b28a6e0c56e 100644
--- a/src/tools/miri/src/shims/unix/linux/fd/event.rs
+++ b/src/tools/miri/src/shims/unix/linux/fd/event.rs
@@ -2,6 +2,7 @@ use crate::shims::unix::fs::FileDescriptor;
 
 use rustc_const_eval::interpret::InterpResult;
 
+use std::cell::Cell;
 use std::io;
 
 /// A kind of file descriptor created by `eventfd`.
@@ -13,7 +14,9 @@ use std::io;
 /// <https://man.netbsd.org/eventfd.2>
 #[derive(Debug)]
 pub struct Event {
-    pub val: u32,
+    /// 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.
+    pub val: Cell<u64>,
 }
 
 impl FileDescriptor for Event {
@@ -22,7 +25,7 @@ impl FileDescriptor for Event {
     }
 
     fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
-        Ok(Box::new(Event { val: self.val }))
+        Ok(Box::new(Event { val: self.val.clone() }))
     }
 
     fn is_tty(&self) -> bool {
@@ -35,4 +38,32 @@ impl FileDescriptor for Event {
     ) -> InterpResult<'tcx, io::Result<i32>> {
         Ok(Ok(0))
     }
+
+    /// A write call adds the 8-byte integer value supplied in
+    /// its buffer to the counter.  The maximum value that may be
+    /// stored in the counter is the largest unsigned 64-bit value
+    /// minus 1 (i.e., 0xfffffffffffffffe).  If the addition would
+    /// cause the counter's value to exceed the maximum, then the
+    /// write either blocks until a read is performed on the
+    /// file descriptor, or fails with the error EAGAIN if the
+    /// file descriptor has been made nonblocking.
+
+    /// A write fails with the error EINVAL if the size of the
+    /// supplied buffer is less than 8 bytes, or if an attempt is
+    /// made to write the value 0xffffffffffffffff.
+    ///
+    /// FIXME: use endianness
+    fn write<'tcx>(
+        &self,
+        _communicate_allowed: bool,
+        bytes: &[u8],
+    ) -> InterpResult<'tcx, io::Result<usize>> {
+        let v1 = self.val.get();
+        // FIXME handle blocking when addition results in exceeding the max u64 value
+        // or fail with EAGAIN if the file descriptor is nonblocking.
+        let v2 = v1.checked_add(u64::from_be_bytes(bytes.try_into().unwrap())).unwrap();
+        self.val.set(v2);
+        assert_eq!(8, bytes.len());
+        Ok(Ok(8))
+    }
 }
diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs
index 82cb21c124a..56d8d428210 100644
--- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs
@@ -55,6 +55,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 let result = this.epoll_ctl(epfd, op, fd, event)?;
                 this.write_scalar(result, dest)?;
             }
+            "epoll_wait" => {
+                let [epfd, events, maxevents, timeout] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let result = this.epoll_wait(epfd, events, maxevents, timeout)?;
+                this.write_scalar(result, dest)?;
+            }
             "eventfd" => {
                 let [val, flag] =
                     this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
diff --git a/src/tools/miri/tests/pass-dep/tokio/sleep.rs b/src/tools/miri/tests/pass-dep/tokio/sleep.rs
new file mode 100644
index 00000000000..1341484dda4
--- /dev/null
+++ b/src/tools/miri/tests/pass-dep/tokio/sleep.rs
@@ -0,0 +1,14 @@
+//@compile-flags: -Zmiri-disable-isolation -Zmiri-permissive-provenance -Zmiri-backtrace=full
+//@only-target-x86_64-unknown-linux: support for tokio only on linux and x86
+
+use tokio::time::{sleep, Duration, Instant};
+
+#[tokio::main]
+async fn main() {
+    let start = Instant::now();
+    sleep(Duration::from_secs(1)).await;
+    // It takes 96 millisecond to sleep for 1 millisecond
+    // It takes 1025 millisecond to sleep for 1 second
+    let time_elapsed = &start.elapsed().as_millis();
+    assert!(time_elapsed > &1000, "{}", time_elapsed);
+}
diff --git a/src/tools/miri/tests/pass-dep/tokio_mvp.rs b/src/tools/miri/tests/pass-dep/tokio/tokio_mvp.rs
index 642168253c2..0bca7cc069a 100644
--- a/src/tools/miri/tests/pass-dep/tokio_mvp.rs
+++ b/src/tools/miri/tests/pass-dep/tokio/tokio_mvp.rs
@@ -1,5 +1,5 @@
 // Need to disable preemption to stay on the supported MVP codepath in mio.
-//@compile-flags: -Zmiri-disable-isolation -Zmiri-permissive-provenance -Zmiri-preemption-rate=0
+//@compile-flags: -Zmiri-disable-isolation -Zmiri-permissive-provenance
 //@only-target-x86_64-unknown-linux: support for tokio exists only on linux and x86
 
 #[tokio::main]