busy waiting implementation for sleep
This commit is contained in:
parent
0aaa9ea5c0
commit
15b9f45cca
5 changed files with 111 additions and 4 deletions
|
|
@ -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))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
|
|
|
|||
14
src/tools/miri/tests/pass-dep/tokio/sleep.rs
Normal file
14
src/tools/miri/tests/pass-dep/tokio/sleep.rs
Normal file
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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]
|
||||
Loading…
Add table
Add a link
Reference in a new issue