Skip to content

Commit 8286c13

Browse files
committed
busy waiting implementation for sleep
1 parent e81f742 commit 8286c13

File tree

5 files changed

+110
-6
lines changed

5 files changed

+110
-6
lines changed

src/shims/unix/linux/fd.rs

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use socketpair::SocketPair;
77

88
use shims::unix::fs::EvalContextExt as _;
99

10+
use std::cell::Cell;
11+
1012
pub mod epoll;
1113
pub mod event;
1214
pub mod socketpair;
@@ -101,6 +103,59 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
101103
}
102104
}
103105

106+
/// The `epoll_wait()` system call waits for events on the `Epoll`
107+
/// instance referred to by the file descriptor `epfd`. The buffer
108+
/// pointed to by `events` is used to return information from the ready
109+
/// list about file descriptors in the interest list that have some
110+
/// events available. Up to `maxevents` are returned by `epoll_wait()`.
111+
/// The `maxevents` argument must be greater than zero.
112+
113+
/// The `timeout` argument specifies the number of milliseconds that
114+
/// `epoll_wait()` will block. Time is measured against the
115+
/// CLOCK_MONOTONIC clock.
116+
117+
/// A call to `epoll_wait()` will block until either:
118+
/// • a file descriptor delivers an event;
119+
/// • the call is interrupted by a signal handler; or
120+
/// • the timeout expires.
121+
122+
/// Note that the timeout interval will be rounded up to the system
123+
/// clock granularity, and kernel scheduling delays mean that the
124+
/// blocking interval may overrun by a small amount. Specifying a
125+
/// timeout of -1 causes `epoll_wait()` to block indefinitely, while
126+
/// specifying a timeout equal to zero cause `epoll_wait()` to return
127+
/// immediately, even if no events are available.
128+
///
129+
/// On success, `epoll_wait()` returns the number of file descriptors
130+
/// ready for the requested I/O, or zero if no file descriptor became
131+
/// ready during the requested timeout milliseconds. On failure,
132+
/// `epoll_wait()` returns -1 and errno is set to indicate the error.
133+
///
134+
/// <https://man7.org/linux/man-pages/man2/epoll_wait.2.html>
135+
fn epoll_wait(
136+
&mut self,
137+
epfd: &OpTy<'tcx, Provenance>,
138+
events: &OpTy<'tcx, Provenance>,
139+
maxevents: &OpTy<'tcx, Provenance>,
140+
timeout: &OpTy<'tcx, Provenance>,
141+
) -> InterpResult<'tcx, Scalar<Provenance>> {
142+
let this = self.eval_context_mut();
143+
144+
let epfd = this.read_scalar(epfd)?.to_i32()?;
145+
let _events = this.read_scalar(events)?.to_pointer(this)?;
146+
let _maxevents = this.read_scalar(maxevents)?.to_i32()?;
147+
let _timeout = this.read_scalar(timeout)?.to_i32()?;
148+
149+
let numevents = 0;
150+
if let Some(epfd) = this.machine.file_handler.handles.get_mut(&epfd) {
151+
let _epfd = epfd.as_epoll_handle()?;
152+
153+
Ok(Scalar::from_i32(numevents))
154+
} else {
155+
Ok(Scalar::from_i32(this.handle_not_found()?))
156+
}
157+
}
158+
104159
/// This function creates an `Event` that is used as an event wait/notify mechanism by
105160
/// user-space applications, and by the kernel to notify user-space applications of events.
106161
/// The `Event` contains an `u64` counter maintained by the kernel. The counter is initialized
@@ -142,7 +197,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
142197
}
143198

144199
let fh = &mut this.machine.file_handler;
145-
let fd = fh.insert_fd(Box::new(Event { val }));
200+
let fd = fh.insert_fd(Box::new(Event { val: Cell::new(val.into()) }));
146201
Ok(Scalar::from_i32(fd))
147202
}
148203

src/shims/unix/linux/fd/event.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::shims::unix::fs::FileDescriptor;
22

33
use rustc_const_eval::interpret::InterpResult;
44

5+
use std::cell::Cell;
56
use std::io;
67

78
/// A kind of file descriptor created by `eventfd`.
@@ -13,7 +14,9 @@ use std::io;
1314
/// <https://man.netbsd.org/eventfd.2>
1415
#[derive(Debug)]
1516
pub struct Event {
16-
pub val: u32,
17+
/// The object contains an unsigned 64-bit integer (uint64_t) counter that is maintained by the
18+
/// kernel. This counter is initialized with the value specified in the argument initval.
19+
pub val: Cell<u64>,
1720
}
1821

1922
impl FileDescriptor for Event {
@@ -22,7 +25,7 @@ impl FileDescriptor for Event {
2225
}
2326

2427
fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
25-
Ok(Box::new(Event { val: self.val }))
28+
Ok(Box::new(Event { val: self.val.clone() }))
2629
}
2730

2831
fn is_tty(&self) -> bool {
@@ -35,4 +38,30 @@ impl FileDescriptor for Event {
3538
) -> InterpResult<'tcx, io::Result<i32>> {
3639
Ok(Ok(0))
3740
}
41+
42+
/// A write call adds the 8-byte integer value supplied in
43+
/// its buffer to the counter. The maximum value that may be
44+
/// stored in the counter is the largest unsigned 64-bit value
45+
/// minus 1 (i.e., 0xfffffffffffffffe). If the addition would
46+
/// cause the counter's value to exceed the maximum, then the
47+
/// write either blocks until a read is performed on the
48+
/// file descriptor, or fails with the error EAGAIN if the
49+
/// file descriptor has been made nonblocking.
50+
51+
/// A write fails with the error EINVAL if the size of the
52+
/// supplied buffer is less than 8 bytes, or if an attempt is
53+
/// made to write the value 0xffffffffffffffff.
54+
///
55+
/// FIXME: use endianness
56+
fn write<'tcx>(
57+
&self,
58+
_communicate_allowed: bool,
59+
bytes: &[u8],
60+
) -> InterpResult<'tcx, io::Result<usize>> {
61+
let v1 = self.val.get();
62+
let v2 = v1.checked_add(u64::from_be_bytes(bytes.try_into().unwrap())).unwrap();
63+
self.val.set(v2);
64+
assert_eq!(8, bytes.len());
65+
Ok(Ok(8))
66+
}
3867
}

src/shims/unix/linux/foreign_items.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
5555
let result = this.epoll_ctl(epfd, op, fd, event)?;
5656
this.write_scalar(result, dest)?;
5757
}
58+
"epoll_wait" => {
59+
let [epfd, events, maxevents, timeout] =
60+
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
61+
let result = this.epoll_wait(epfd, events, maxevents, timeout)?;
62+
this.write_scalar(result, dest)?;
63+
}
5864
"eventfd" => {
5965
let [val, flag] =
6066
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
@@ -130,7 +136,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
130136
"incorrect number of arguments for syscall: got 0, expected at least 1"
131137
);
132138
}
133-
match this.read_machine_usize(&args[0])? {
139+
match this.read_scalar(&args[0])?.to_machine_usize(this)? {
134140
// `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
135141
// is called if a `HashMap` is created the regular way (e.g. HashMap<K, V>).
136142
id if id == sys_getrandom => {
@@ -178,7 +184,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
178184
let [pid, cpusetsize, mask] =
179185
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
180186
this.read_scalar(pid)?.to_i32()?;
181-
this.read_machine_usize(cpusetsize)?;
187+
this.read_scalar(cpusetsize)?.to_machine_usize(this)?;
182188
this.deref_operand(mask)?;
183189
// FIXME: we just return an error; `num_cpus` then falls back to `sysconf`.
184190
let einval = this.eval_libc("EINVAL");
@@ -210,7 +216,7 @@ fn getrandom<'tcx>(
210216
dest: &PlaceTy<'tcx, Provenance>,
211217
) -> InterpResult<'tcx> {
212218
let ptr = this.read_pointer(ptr)?;
213-
let len = this.read_machine_usize(len)?;
219+
let len = this.read_scalar(len)?.to_machine_usize(this)?;
214220

215221
// The only supported flags are GRND_RANDOM and GRND_NONBLOCK,
216222
// neither of which have any effect on our current PRNG.

tests/pass-dep/tokio/sleep.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//@compile-flags: -Zmiri-disable-isolation -Zmiri-permissive-provenance -Zmiri-preemption-rate=0 -Zmiri-backtrace=full
2+
//@only-target-x86_64-unknown-linux: support for tokio only on linux and x86
3+
4+
use tokio::time::{sleep, Duration, Instant};
5+
6+
#[tokio::main]
7+
async fn main() {
8+
let start = Instant::now();
9+
sleep(Duration::from_secs(1)).await;
10+
// It takes 96 millisecond to sleep for 1 millisecond
11+
// It takes 1025 millisecond to sleep for 1 second
12+
let time_elapsed = &start.elapsed().as_millis();
13+
assert!((1000..2000).contains(time_elapsed), "{}", time_elapsed);
14+
}
File renamed without changes.

0 commit comments

Comments
 (0)