make sleep
work with isolation enabled
This commit is contained in:
parent
7e66a9ff16
commit
6a37643265
src
tests/pass/shims
@ -3,7 +3,7 @@
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
use std::num::TryFromIntError;
|
use std::num::TryFromIntError;
|
||||||
use std::time::{Duration, Instant, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
use log::trace;
|
use log::trace;
|
||||||
|
|
||||||
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
use crate::concurrency::data_race;
|
use crate::concurrency::data_race;
|
||||||
use crate::concurrency::sync::SynchronizationState;
|
use crate::concurrency::sync::SynchronizationState;
|
||||||
|
use crate::shims::time::{Clock, Instant};
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
@ -187,17 +188,6 @@ pub enum Time {
|
|||||||
RealTime(SystemTime),
|
RealTime(SystemTime),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Time {
|
|
||||||
/// How long do we have to wait from now until the specified time?
|
|
||||||
fn get_wait_time(&self) -> Duration {
|
|
||||||
match self {
|
|
||||||
Time::Monotonic(instant) => instant.saturating_duration_since(Instant::now()),
|
|
||||||
Time::RealTime(time) =>
|
|
||||||
time.duration_since(SystemTime::now()).unwrap_or(Duration::new(0, 0)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Callbacks are used to implement timeouts. For example, waiting on a
|
/// Callbacks are used to implement timeouts. For example, waiting on a
|
||||||
/// conditional variable with a timeout creates a callback that is called after
|
/// conditional variable with a timeout creates a callback that is called after
|
||||||
/// the specified time and unblocks the thread. If another thread signals on the
|
/// the specified time and unblocks the thread. If another thread signals on the
|
||||||
@ -490,13 +480,16 @@ fn unregister_timeout_callback_if_exists(&mut self, thread: ThreadId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get a callback that is ready to be called.
|
/// Get a callback that is ready to be called.
|
||||||
fn get_ready_callback(&mut self) -> Option<(ThreadId, TimeoutCallback<'mir, 'tcx>)> {
|
fn get_ready_callback(
|
||||||
|
&mut self,
|
||||||
|
clock: &Clock,
|
||||||
|
) -> Option<(ThreadId, TimeoutCallback<'mir, 'tcx>)> {
|
||||||
// We iterate over all threads in the order of their indices because
|
// We iterate over all threads in the order of their indices because
|
||||||
// this allows us to have a deterministic scheduler.
|
// this allows us to have a deterministic scheduler.
|
||||||
for thread in self.threads.indices() {
|
for thread in self.threads.indices() {
|
||||||
match self.timeout_callbacks.entry(thread) {
|
match self.timeout_callbacks.entry(thread) {
|
||||||
Entry::Occupied(entry) =>
|
Entry::Occupied(entry) =>
|
||||||
if entry.get().call_time.get_wait_time() == Duration::new(0, 0) {
|
if clock.get_wait_time(&entry.get().call_time) == Duration::new(0, 0) {
|
||||||
return Some((thread, entry.remove().callback));
|
return Some((thread, entry.remove().callback));
|
||||||
},
|
},
|
||||||
Entry::Vacant(_) => {}
|
Entry::Vacant(_) => {}
|
||||||
@ -553,7 +546,7 @@ fn thread_terminated(
|
|||||||
/// used in stateless model checkers such as Loom: run the active thread as
|
/// used in stateless model checkers such as Loom: run the active thread as
|
||||||
/// long as we can and switch only when we have to (the active thread was
|
/// long as we can and switch only when we have to (the active thread was
|
||||||
/// blocked, terminated, or has explicitly asked to be preempted).
|
/// blocked, terminated, or has explicitly asked to be preempted).
|
||||||
fn schedule(&mut self) -> InterpResult<'tcx, SchedulingAction> {
|
fn schedule(&mut self, clock: &Clock) -> InterpResult<'tcx, SchedulingAction> {
|
||||||
// Check whether the thread has **just** terminated (`check_terminated`
|
// Check whether the thread has **just** terminated (`check_terminated`
|
||||||
// checks whether the thread has popped all its stack and if yes, sets
|
// checks whether the thread has popped all its stack and if yes, sets
|
||||||
// the thread state to terminated).
|
// the thread state to terminated).
|
||||||
@ -580,7 +573,7 @@ fn schedule(&mut self) -> InterpResult<'tcx, SchedulingAction> {
|
|||||||
// at the time of the call".
|
// at the time of the call".
|
||||||
// <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html>
|
// <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html>
|
||||||
let potential_sleep_time =
|
let potential_sleep_time =
|
||||||
self.timeout_callbacks.values().map(|info| info.call_time.get_wait_time()).min();
|
self.timeout_callbacks.values().map(|info| clock.get_wait_time(&info.call_time)).min();
|
||||||
if potential_sleep_time == Some(Duration::new(0, 0)) {
|
if potential_sleep_time == Some(Duration::new(0, 0)) {
|
||||||
return Ok(SchedulingAction::ExecuteTimeoutCallback);
|
return Ok(SchedulingAction::ExecuteTimeoutCallback);
|
||||||
}
|
}
|
||||||
@ -615,7 +608,8 @@ fn schedule(&mut self) -> InterpResult<'tcx, SchedulingAction> {
|
|||||||
// All threads are currently blocked, but we have unexecuted
|
// All threads are currently blocked, but we have unexecuted
|
||||||
// timeout_callbacks, which may unblock some of the threads. Hence,
|
// timeout_callbacks, which may unblock some of the threads. Hence,
|
||||||
// sleep until the first callback.
|
// sleep until the first callback.
|
||||||
std::thread::sleep(sleep_time);
|
|
||||||
|
clock.sleep(sleep_time);
|
||||||
Ok(SchedulingAction::ExecuteTimeoutCallback)
|
Ok(SchedulingAction::ExecuteTimeoutCallback)
|
||||||
} else {
|
} else {
|
||||||
throw_machine_stop!(TerminationInfo::Deadlock);
|
throw_machine_stop!(TerminationInfo::Deadlock);
|
||||||
@ -878,8 +872,9 @@ fn unregister_timeout_callback_if_exists(&mut self, thread: ThreadId) {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn run_timeout_callback(&mut self) -> InterpResult<'tcx> {
|
fn run_timeout_callback(&mut self) -> InterpResult<'tcx> {
|
||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
let (thread, callback) =
|
let (thread, callback) = if let Some((thread, callback)) =
|
||||||
if let Some((thread, callback)) = this.machine.threads.get_ready_callback() {
|
this.machine.threads.get_ready_callback(&this.machine.clock)
|
||||||
|
{
|
||||||
(thread, callback)
|
(thread, callback)
|
||||||
} else {
|
} else {
|
||||||
// get_ready_callback can return None if the computer's clock
|
// get_ready_callback can return None if the computer's clock
|
||||||
@ -906,7 +901,7 @@ fn run_timeout_callback(&mut self) -> InterpResult<'tcx> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn schedule(&mut self) -> InterpResult<'tcx, SchedulingAction> {
|
fn schedule(&mut self) -> InterpResult<'tcx, SchedulingAction> {
|
||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
this.machine.threads.schedule()
|
this.machine.threads.schedule(&this.machine.clock)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles thread termination of the active thread: wakes up threads joining on this one,
|
/// Handles thread termination of the active thread: wakes up threads joining on this one,
|
||||||
|
@ -359,11 +359,6 @@ pub fn eval_entry<'tcx>(
|
|||||||
assert!(ecx.step()?, "a terminated thread was scheduled for execution");
|
assert!(ecx.step()?, "a terminated thread was scheduled for execution");
|
||||||
}
|
}
|
||||||
SchedulingAction::ExecuteTimeoutCallback => {
|
SchedulingAction::ExecuteTimeoutCallback => {
|
||||||
assert!(
|
|
||||||
ecx.machine.communicate(),
|
|
||||||
"scheduler callbacks require disabled isolation, but the code \
|
|
||||||
that created the callback did not check it"
|
|
||||||
);
|
|
||||||
ecx.run_timeout_callback()?;
|
ecx.run_timeout_callback()?;
|
||||||
}
|
}
|
||||||
SchedulingAction::ExecuteDtors => {
|
SchedulingAction::ExecuteDtors => {
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::time::Instant;
|
|
||||||
|
|
||||||
use rand::rngs::StdRng;
|
use rand::rngs::StdRng;
|
||||||
use rand::SeedableRng;
|
use rand::SeedableRng;
|
||||||
@ -28,7 +27,7 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
concurrency::{data_race, weak_memory},
|
concurrency::{data_race, weak_memory},
|
||||||
shims::unix::FileHandler,
|
shims::{time::Clock, unix::FileHandler},
|
||||||
*,
|
*,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -327,8 +326,8 @@ pub struct Evaluator<'mir, 'tcx> {
|
|||||||
/// The table of directory descriptors.
|
/// The table of directory descriptors.
|
||||||
pub(crate) dir_handler: shims::unix::DirHandler,
|
pub(crate) dir_handler: shims::unix::DirHandler,
|
||||||
|
|
||||||
/// The "time anchor" for this machine's monotone clock (for `Instant` simulation).
|
/// This machine's monotone clock.
|
||||||
pub(crate) time_anchor: Instant,
|
pub(crate) clock: Clock,
|
||||||
|
|
||||||
/// The set of threads.
|
/// The set of threads.
|
||||||
pub(crate) threads: ThreadManager<'mir, 'tcx>,
|
pub(crate) threads: ThreadManager<'mir, 'tcx>,
|
||||||
@ -434,7 +433,6 @@ pub(crate) fn new(config: &MiriConfig, layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>)
|
|||||||
enforce_abi: config.check_abi,
|
enforce_abi: config.check_abi,
|
||||||
file_handler: FileHandler::new(config.mute_stdout_stderr),
|
file_handler: FileHandler::new(config.mute_stdout_stderr),
|
||||||
dir_handler: Default::default(),
|
dir_handler: Default::default(),
|
||||||
time_anchor: Instant::now(),
|
|
||||||
layouts,
|
layouts,
|
||||||
threads: ThreadManager::default(),
|
threads: ThreadManager::default(),
|
||||||
static_roots: Vec::new(),
|
static_roots: Vec::new(),
|
||||||
@ -454,6 +452,7 @@ pub(crate) fn new(config: &MiriConfig, layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>)
|
|||||||
preemption_rate: config.preemption_rate,
|
preemption_rate: config.preemption_rate,
|
||||||
report_progress: config.report_progress,
|
report_progress: config.report_progress,
|
||||||
basic_block_count: 0,
|
basic_block_count: 0,
|
||||||
|
clock: Clock::new(config.isolated_op == IsolatedOp::Allow),
|
||||||
external_so_lib: config.external_so_file.as_ref().map(|lib_file_path| {
|
external_so_lib: config.external_so_file.as_ref().map(|lib_file_path| {
|
||||||
// Check if host target == the session target.
|
// Check if host target == the session target.
|
||||||
if env!("TARGET") != target_triple {
|
if env!("TARGET") != target_triple {
|
||||||
@ -1036,6 +1035,9 @@ fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx>
|
|||||||
|
|
||||||
// These are our preemption points.
|
// These are our preemption points.
|
||||||
ecx.maybe_preempt_active_thread();
|
ecx.maybe_preempt_active_thread();
|
||||||
|
|
||||||
|
ecx.machine.clock.tick();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,128 @@
|
|||||||
use std::time::{Duration, Instant, SystemTime};
|
use std::sync::atomic::AtomicU64;
|
||||||
|
use std::time::{Duration, Instant as StdInstant, SystemTime};
|
||||||
|
|
||||||
|
use rustc_data_structures::sync::Ordering;
|
||||||
|
|
||||||
use crate::concurrency::thread::Time;
|
use crate::concurrency::thread::Time;
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
/// When using a virtual clock, this defines how many nanoseconds do we pretend
|
||||||
|
/// are passing for each basic block.
|
||||||
|
const NANOSECOND_PER_BASIC_BLOCK: u64 = 10;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Instant {
|
||||||
|
Host(StdInstant),
|
||||||
|
Virtual { nanoseconds: u64 },
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A monotone clock used for `Instant` simulation.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Clock {
|
||||||
|
Host {
|
||||||
|
/// The "time anchor" for this machine's monotone clock.
|
||||||
|
time_anchor: StdInstant,
|
||||||
|
},
|
||||||
|
Virtual {
|
||||||
|
/// The "current virtual time".
|
||||||
|
nanoseconds: AtomicU64,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clock {
|
||||||
|
/// Create a new clock based on the availability of communication with the host.
|
||||||
|
pub fn new(communicate: bool) -> Self {
|
||||||
|
if communicate {
|
||||||
|
Self::Host { time_anchor: StdInstant::now() }
|
||||||
|
} else {
|
||||||
|
Self::Virtual { nanoseconds: 0.into() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current time relative to this clock.
|
||||||
|
pub fn get(&self) -> Duration {
|
||||||
|
match self {
|
||||||
|
Self::Host { time_anchor } => StdInstant::now().saturating_duration_since(*time_anchor),
|
||||||
|
Self::Virtual { nanoseconds } =>
|
||||||
|
Duration::from_nanos(nanoseconds.load(Ordering::Relaxed)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Let the time pass for a small interval.
|
||||||
|
pub fn tick(&self) {
|
||||||
|
match self {
|
||||||
|
Self::Host { .. } => {
|
||||||
|
// Time will pass without us doing anything.
|
||||||
|
}
|
||||||
|
Self::Virtual { nanoseconds } => {
|
||||||
|
nanoseconds.fetch_add(NANOSECOND_PER_BASIC_BLOCK, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sleep for the desired duration.
|
||||||
|
pub fn sleep(&self, duration: Duration) {
|
||||||
|
match self {
|
||||||
|
Self::Host { .. } => std::thread::sleep(duration),
|
||||||
|
Self::Virtual { nanoseconds } => {
|
||||||
|
// Just pretend that we have slept for some time.
|
||||||
|
nanoseconds.fetch_add(duration.as_nanos().try_into().unwrap(), Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute `now + duration` relative to this clock.
|
||||||
|
pub fn get_time_relative(&self, duration: Duration) -> Option<Time> {
|
||||||
|
match self {
|
||||||
|
Self::Host { .. } =>
|
||||||
|
StdInstant::now()
|
||||||
|
.checked_add(duration)
|
||||||
|
.map(|instant| Time::Monotonic(Instant::Host(instant))),
|
||||||
|
Self::Virtual { nanoseconds } =>
|
||||||
|
nanoseconds
|
||||||
|
.load(Ordering::Relaxed)
|
||||||
|
.checked_add(duration.as_nanos().try_into().unwrap())
|
||||||
|
.map(|nanoseconds| Time::Monotonic(Instant::Virtual { nanoseconds })),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute `start + duration` relative to this clock where `start` is the instant of time when
|
||||||
|
/// this clock was created.
|
||||||
|
pub fn get_time_absolute(&self, duration: Duration) -> Option<Time> {
|
||||||
|
match self {
|
||||||
|
Self::Host { time_anchor } =>
|
||||||
|
time_anchor
|
||||||
|
.checked_add(duration)
|
||||||
|
.map(|instant| Time::Monotonic(Instant::Host(instant))),
|
||||||
|
Self::Virtual { .. } =>
|
||||||
|
Some(Time::Monotonic(Instant::Virtual {
|
||||||
|
nanoseconds: duration.as_nanos().try_into().unwrap(),
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// How long do we have to wait from now until the specified time?
|
||||||
|
pub fn get_wait_time(&self, time: &Time) -> Duration {
|
||||||
|
match time {
|
||||||
|
Time::Monotonic(instant) =>
|
||||||
|
match (instant, self) {
|
||||||
|
(Instant::Host(instant), Clock::Host { .. }) =>
|
||||||
|
instant.saturating_duration_since(StdInstant::now()),
|
||||||
|
(
|
||||||
|
Instant::Virtual { nanoseconds },
|
||||||
|
Clock::Virtual { nanoseconds: current_ns },
|
||||||
|
) =>
|
||||||
|
Duration::from_nanos(
|
||||||
|
nanoseconds.saturating_sub(current_ns.load(Ordering::Relaxed)),
|
||||||
|
),
|
||||||
|
_ => panic!(),
|
||||||
|
},
|
||||||
|
Time::RealTime(time) =>
|
||||||
|
time.duration_since(SystemTime::now()).unwrap_or(Duration::new(0, 0)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the time elapsed between the provided time and the unix epoch as a `Duration`.
|
/// Returns the time elapsed between the provided time and the unix epoch as a `Duration`.
|
||||||
pub fn system_time_to_duration<'tcx>(time: &SystemTime) -> InterpResult<'tcx, Duration> {
|
pub fn system_time_to_duration<'tcx>(time: &SystemTime) -> InterpResult<'tcx, Duration> {
|
||||||
time.duration_since(SystemTime::UNIX_EPOCH)
|
time.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
@ -23,7 +143,6 @@ fn clock_gettime(
|
|||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
|
|
||||||
this.assert_target_os("linux", "clock_gettime");
|
this.assert_target_os("linux", "clock_gettime");
|
||||||
this.check_no_isolation("`clock_gettime`")?;
|
|
||||||
|
|
||||||
let clk_id = this.read_scalar(clk_id_op)?.to_i32()?;
|
let clk_id = this.read_scalar(clk_id_op)?.to_i32()?;
|
||||||
|
|
||||||
@ -40,9 +159,10 @@ fn clock_gettime(
|
|||||||
[this.eval_libc_i32("CLOCK_MONOTONIC")?, this.eval_libc_i32("CLOCK_MONOTONIC_COARSE")?];
|
[this.eval_libc_i32("CLOCK_MONOTONIC")?, this.eval_libc_i32("CLOCK_MONOTONIC_COARSE")?];
|
||||||
|
|
||||||
let duration = if absolute_clocks.contains(&clk_id) {
|
let duration = if absolute_clocks.contains(&clk_id) {
|
||||||
|
this.check_no_isolation("`clock_gettime` with real time clocks")?;
|
||||||
system_time_to_duration(&SystemTime::now())?
|
system_time_to_duration(&SystemTime::now())?
|
||||||
} else if relative_clocks.contains(&clk_id) {
|
} else if relative_clocks.contains(&clk_id) {
|
||||||
Instant::now().duration_since(this.machine.time_anchor)
|
this.machine.clock.get()
|
||||||
} else {
|
} else {
|
||||||
let einval = this.eval_libc("EINVAL")?;
|
let einval = this.eval_libc("EINVAL")?;
|
||||||
this.set_last_error(einval)?;
|
this.set_last_error(einval)?;
|
||||||
@ -123,11 +243,10 @@ fn QueryPerformanceCounter(
|
|||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
|
|
||||||
this.assert_target_os("windows", "QueryPerformanceCounter");
|
this.assert_target_os("windows", "QueryPerformanceCounter");
|
||||||
this.check_no_isolation("`QueryPerformanceCounter`")?;
|
|
||||||
|
|
||||||
// QueryPerformanceCounter uses a hardware counter as its basis.
|
// QueryPerformanceCounter uses a hardware counter as its basis.
|
||||||
// Miri will emulate a counter with a resolution of 1 nanosecond.
|
// Miri will emulate a counter with a resolution of 1 nanosecond.
|
||||||
let duration = Instant::now().duration_since(this.machine.time_anchor);
|
let duration = this.machine.clock.get();
|
||||||
let qpc = i64::try_from(duration.as_nanos()).map_err(|_| {
|
let qpc = i64::try_from(duration.as_nanos()).map_err(|_| {
|
||||||
err_unsup_format!("programs running longer than 2^63 nanoseconds are not supported")
|
err_unsup_format!("programs running longer than 2^63 nanoseconds are not supported")
|
||||||
})?;
|
})?;
|
||||||
@ -146,7 +265,6 @@ fn QueryPerformanceFrequency(
|
|||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
|
|
||||||
this.assert_target_os("windows", "QueryPerformanceFrequency");
|
this.assert_target_os("windows", "QueryPerformanceFrequency");
|
||||||
this.check_no_isolation("`QueryPerformanceFrequency`")?;
|
|
||||||
|
|
||||||
// Retrieves the frequency of the hardware performance counter.
|
// Retrieves the frequency of the hardware performance counter.
|
||||||
// The frequency of the performance counter is fixed at system boot and
|
// The frequency of the performance counter is fixed at system boot and
|
||||||
@ -164,11 +282,10 @@ fn mach_absolute_time(&self) -> InterpResult<'tcx, Scalar<Provenance>> {
|
|||||||
let this = self.eval_context_ref();
|
let this = self.eval_context_ref();
|
||||||
|
|
||||||
this.assert_target_os("macos", "mach_absolute_time");
|
this.assert_target_os("macos", "mach_absolute_time");
|
||||||
this.check_no_isolation("`mach_absolute_time`")?;
|
|
||||||
|
|
||||||
// This returns a u64, with time units determined dynamically by `mach_timebase_info`.
|
// This returns a u64, with time units determined dynamically by `mach_timebase_info`.
|
||||||
// We return plain nanoseconds.
|
// We return plain nanoseconds.
|
||||||
let duration = Instant::now().duration_since(this.machine.time_anchor);
|
let duration = this.machine.clock.get();
|
||||||
let res = u64::try_from(duration.as_nanos()).map_err(|_| {
|
let res = u64::try_from(duration.as_nanos()).map_err(|_| {
|
||||||
err_unsup_format!("programs running longer than 2^64 nanoseconds are not supported")
|
err_unsup_format!("programs running longer than 2^64 nanoseconds are not supported")
|
||||||
})?;
|
})?;
|
||||||
@ -182,7 +299,6 @@ fn mach_timebase_info(
|
|||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
|
|
||||||
this.assert_target_os("macos", "mach_timebase_info");
|
this.assert_target_os("macos", "mach_timebase_info");
|
||||||
this.check_no_isolation("`mach_timebase_info`")?;
|
|
||||||
|
|
||||||
let info = this.deref_operand(info_op)?;
|
let info = this.deref_operand(info_op)?;
|
||||||
|
|
||||||
@ -202,7 +318,6 @@ fn nanosleep(
|
|||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
|
|
||||||
this.assert_target_os_is_unix("nanosleep");
|
this.assert_target_os_is_unix("nanosleep");
|
||||||
this.check_no_isolation("`nanosleep`")?;
|
|
||||||
|
|
||||||
let duration = match this.read_timespec(&this.deref_operand(req_op)?)? {
|
let duration = match this.read_timespec(&this.deref_operand(req_op)?)? {
|
||||||
Some(duration) => duration,
|
Some(duration) => duration,
|
||||||
@ -213,10 +328,9 @@ fn nanosleep(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
// If adding the duration overflows, let's just sleep for an hour. Waking up early is always acceptable.
|
// If adding the duration overflows, let's just sleep for an hour. Waking up early is always acceptable.
|
||||||
let timeout_time = Instant::now()
|
let timeout_time = this.machine.clock.get_time_relative(duration).unwrap_or_else(|| {
|
||||||
.checked_add(duration)
|
this.machine.clock.get_time_relative(Duration::from_secs(3600)).unwrap()
|
||||||
.unwrap_or_else(|| Instant::now().checked_add(Duration::from_secs(3600)).unwrap());
|
});
|
||||||
let timeout_time = Time::Monotonic(timeout_time);
|
|
||||||
|
|
||||||
let active_thread = this.get_active_thread();
|
let active_thread = this.get_active_thread();
|
||||||
this.block_thread(active_thread);
|
this.block_thread(active_thread);
|
||||||
@ -238,12 +352,11 @@ fn Sleep(&mut self, timeout: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> {
|
|||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
|
|
||||||
this.assert_target_os("windows", "Sleep");
|
this.assert_target_os("windows", "Sleep");
|
||||||
this.check_no_isolation("`Sleep`")?;
|
|
||||||
|
|
||||||
let timeout_ms = this.read_scalar(timeout)?.to_u32()?;
|
let timeout_ms = this.read_scalar(timeout)?.to_u32()?;
|
||||||
|
|
||||||
let duration = Duration::from_millis(timeout_ms.into());
|
let duration = Duration::from_millis(timeout_ms.into());
|
||||||
let timeout_time = Time::Monotonic(Instant::now().checked_add(duration).unwrap());
|
let timeout_time = this.machine.clock.get_time_relative(duration).unwrap();
|
||||||
|
|
||||||
let active_thread = this.get_active_thread();
|
let active_thread = this.get_active_thread();
|
||||||
this.block_thread(active_thread);
|
this.block_thread(active_thread);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::concurrency::thread::Time;
|
use crate::concurrency::thread::Time;
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use rustc_target::abi::{Align, Size};
|
use rustc_target::abi::{Align, Size};
|
||||||
use std::time::{Instant, SystemTime};
|
use std::time::SystemTime;
|
||||||
|
|
||||||
/// Implementation of the SYS_futex syscall.
|
/// Implementation of the SYS_futex syscall.
|
||||||
/// `args` is the arguments *after* the syscall number.
|
/// `args` is the arguments *after* the syscall number.
|
||||||
@ -106,14 +106,14 @@ pub fn futex<'tcx>(
|
|||||||
if op & futex_realtime != 0 {
|
if op & futex_realtime != 0 {
|
||||||
Time::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap())
|
Time::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap())
|
||||||
} else {
|
} else {
|
||||||
Time::Monotonic(this.machine.time_anchor.checked_add(duration).unwrap())
|
this.machine.clock.get_time_absolute(duration).unwrap()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// FUTEX_WAIT uses a relative timestamp.
|
// FUTEX_WAIT uses a relative timestamp.
|
||||||
if op & futex_realtime != 0 {
|
if op & futex_realtime != 0 {
|
||||||
Time::RealTime(SystemTime::now().checked_add(duration).unwrap())
|
Time::RealTime(SystemTime::now().checked_add(duration).unwrap())
|
||||||
} else {
|
} else {
|
||||||
Time::Monotonic(Instant::now().checked_add(duration).unwrap())
|
this.machine.clock.get_time_relative(duration).unwrap()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
@ -840,7 +840,7 @@ fn pthread_cond_timedwait(
|
|||||||
let timeout_time = if clock_id == this.eval_libc_i32("CLOCK_REALTIME")? {
|
let timeout_time = if clock_id == this.eval_libc_i32("CLOCK_REALTIME")? {
|
||||||
Time::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap())
|
Time::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap())
|
||||||
} else if clock_id == this.eval_libc_i32("CLOCK_MONOTONIC")? {
|
} else if clock_id == this.eval_libc_i32("CLOCK_MONOTONIC")? {
|
||||||
Time::Monotonic(this.machine.time_anchor.checked_add(duration).unwrap())
|
this.machine.clock.get_time_absolute(duration).unwrap()
|
||||||
} else {
|
} else {
|
||||||
throw_unsup_format!("unsupported clock id: {}", clock_id);
|
throw_unsup_format!("unsupported clock id: {}", clock_id);
|
||||||
};
|
};
|
||||||
|
34
tests/pass/shims/time-with-isolation.rs
Normal file
34
tests/pass/shims/time-with-isolation.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
fn duration_sanity(diff: Duration) {
|
||||||
|
// The virtual clock is deterministic and I got 29us on a 64-bit Linux machine. However, this
|
||||||
|
// changes according to the platform so we use an interval to be safe. This should be updated
|
||||||
|
// if `NANOSECONDS_PER_BASIC_BLOCK` changes.
|
||||||
|
assert!(diff.as_micros() > 10);
|
||||||
|
assert!(diff.as_micros() < 40);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_sleep() {
|
||||||
|
let before = Instant::now();
|
||||||
|
std::thread::sleep(Duration::from_millis(100));
|
||||||
|
let after = Instant::now();
|
||||||
|
assert!((after - before).as_millis() >= 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Check `Instant`.
|
||||||
|
let now1 = Instant::now();
|
||||||
|
// Do some work to make time pass.
|
||||||
|
for _ in 0..10 {
|
||||||
|
drop(vec![42]);
|
||||||
|
}
|
||||||
|
let now2 = Instant::now();
|
||||||
|
assert!(now2 > now1);
|
||||||
|
// Sanity-check the difference we got.
|
||||||
|
let diff = now2.duration_since(now1);
|
||||||
|
assert_eq!(now1 + diff, now2);
|
||||||
|
assert_eq!(now2 - diff, now1);
|
||||||
|
duration_sanity(diff);
|
||||||
|
|
||||||
|
test_sleep();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user