move clock stuff to separate file
This commit is contained in:
parent
ad69e0b36a
commit
f5e2f73178
131
src/clock.rs
Normal file
131
src/clock.rs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
use std::sync::atomic::AtomicU64;
|
||||||
|
use std::time::{Duration, Instant as StdInstant};
|
||||||
|
|
||||||
|
use rustc_data_structures::sync::Ordering;
|
||||||
|
|
||||||
|
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 struct Instant {
|
||||||
|
kind: InstantKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum InstantKind {
|
||||||
|
Host(StdInstant),
|
||||||
|
Virtual { nanoseconds: u64 },
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A monotone clock used for `Instant` simulation.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Clock {
|
||||||
|
kind: ClockKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum ClockKind {
|
||||||
|
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 {
|
||||||
|
let kind = if communicate {
|
||||||
|
ClockKind::Host { time_anchor: StdInstant::now() }
|
||||||
|
} else {
|
||||||
|
ClockKind::Virtual { nanoseconds: 0.into() }
|
||||||
|
};
|
||||||
|
|
||||||
|
Self { kind }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current time relative to this clock.
|
||||||
|
pub fn get(&self) -> Duration {
|
||||||
|
match &self.kind {
|
||||||
|
ClockKind::Host { time_anchor } =>
|
||||||
|
StdInstant::now().saturating_duration_since(*time_anchor),
|
||||||
|
ClockKind::Virtual { nanoseconds } =>
|
||||||
|
Duration::from_nanos(nanoseconds.load(Ordering::Relaxed)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Let the time pass for a small interval.
|
||||||
|
pub fn tick(&self) {
|
||||||
|
match &self.kind {
|
||||||
|
ClockKind::Host { .. } => {
|
||||||
|
// Time will pass without us doing anything.
|
||||||
|
}
|
||||||
|
ClockKind::Virtual { nanoseconds } => {
|
||||||
|
nanoseconds.fetch_add(NANOSECOND_PER_BASIC_BLOCK, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sleep for the desired duration.
|
||||||
|
pub fn sleep(&self, duration: Duration) {
|
||||||
|
match &self.kind {
|
||||||
|
ClockKind::Host { .. } => std::thread::sleep(duration),
|
||||||
|
ClockKind::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<Instant> {
|
||||||
|
match &self.kind {
|
||||||
|
ClockKind::Host { .. } =>
|
||||||
|
StdInstant::now()
|
||||||
|
.checked_add(duration)
|
||||||
|
.map(|instant| Instant { kind: InstantKind::Host(instant) }),
|
||||||
|
ClockKind::Virtual { nanoseconds } =>
|
||||||
|
nanoseconds
|
||||||
|
.load(Ordering::Relaxed)
|
||||||
|
.checked_add(duration.as_nanos().try_into().unwrap())
|
||||||
|
.map(|nanoseconds| Instant { kind: InstantKind::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<Instant> {
|
||||||
|
match &self.kind {
|
||||||
|
ClockKind::Host { time_anchor } =>
|
||||||
|
time_anchor
|
||||||
|
.checked_add(duration)
|
||||||
|
.map(|instant| Instant { kind: InstantKind::Host(instant) }),
|
||||||
|
ClockKind::Virtual { .. } =>
|
||||||
|
Some(Instant {
|
||||||
|
kind: InstantKind::Virtual {
|
||||||
|
nanoseconds: duration.as_nanos().try_into().unwrap(),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the duration until the given instant.
|
||||||
|
pub fn duration_until(&self, instant: &Instant) -> Duration {
|
||||||
|
match (&instant.kind, &self.kind) {
|
||||||
|
(InstantKind::Host(instant), ClockKind::Host { .. }) =>
|
||||||
|
instant.saturating_duration_since(StdInstant::now()),
|
||||||
|
(
|
||||||
|
InstantKind::Virtual { nanoseconds },
|
||||||
|
ClockKind::Virtual { nanoseconds: current_ns },
|
||||||
|
) =>
|
||||||
|
Duration::from_nanos(nanoseconds.saturating_sub(current_ns.load(Ordering::Relaxed))),
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -16,7 +16,6 @@ use rustc_target::spec::abi::Abi;
|
|||||||
|
|
||||||
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)]
|
||||||
@ -188,6 +187,17 @@ 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, clock: &Clock) -> Duration {
|
||||||
|
match self {
|
||||||
|
Time::Monotonic(instant) => clock.duration_until(instant),
|
||||||
|
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
|
||||||
@ -489,7 +499,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
|
|||||||
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 clock.get_wait_time(&entry.get().call_time) == Duration::new(0, 0) {
|
if entry.get().call_time.get_wait_time(clock) == Duration::new(0, 0) {
|
||||||
return Some((thread, entry.remove().callback));
|
return Some((thread, entry.remove().callback));
|
||||||
},
|
},
|
||||||
Entry::Vacant(_) => {}
|
Entry::Vacant(_) => {}
|
||||||
@ -573,7 +583,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
|
|||||||
// 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| clock.get_wait_time(&info.call_time)).min();
|
self.timeout_callbacks.values().map(|info| info.call_time.get_wait_time(clock)).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);
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,7 @@ extern crate rustc_session;
|
|||||||
extern crate rustc_span;
|
extern crate rustc_span;
|
||||||
extern crate rustc_target;
|
extern crate rustc_target;
|
||||||
|
|
||||||
|
mod clock;
|
||||||
mod concurrency;
|
mod concurrency;
|
||||||
mod diagnostics;
|
mod diagnostics;
|
||||||
mod eval;
|
mod eval;
|
||||||
@ -81,6 +82,7 @@ pub use crate::shims::time::EvalContextExt as _;
|
|||||||
pub use crate::shims::tls::{EvalContextExt as _, TlsData};
|
pub use crate::shims::tls::{EvalContextExt as _, TlsData};
|
||||||
pub use crate::shims::EvalContextExt as _;
|
pub use crate::shims::EvalContextExt as _;
|
||||||
|
|
||||||
|
pub use crate::clock::{Clock, Instant};
|
||||||
pub use crate::concurrency::{
|
pub use crate::concurrency::{
|
||||||
data_race::{
|
data_race::{
|
||||||
AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd,
|
AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd,
|
||||||
@ -89,7 +91,7 @@ pub use crate::concurrency::{
|
|||||||
sync::{CondvarId, EvalContextExt as SyncEvalContextExt, MutexId, RwLockId},
|
sync::{CondvarId, EvalContextExt as SyncEvalContextExt, MutexId, RwLockId},
|
||||||
thread::{
|
thread::{
|
||||||
EvalContextExt as ThreadsEvalContextExt, SchedulingAction, ThreadId, ThreadManager,
|
EvalContextExt as ThreadsEvalContextExt, SchedulingAction, ThreadId, ThreadManager,
|
||||||
ThreadState,
|
ThreadState, Time,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
pub use crate::diagnostics::{
|
pub use crate::diagnostics::{
|
||||||
|
@ -27,7 +27,7 @@ use rustc_target::spec::abi::Abi;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
concurrency::{data_race, weak_memory},
|
concurrency::{data_race, weak_memory},
|
||||||
shims::{time::Clock, unix::FileHandler},
|
shims::unix::FileHandler,
|
||||||
*,
|
*,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,145 +1,7 @@
|
|||||||
use std::sync::atomic::AtomicU64;
|
use std::time::{Duration, SystemTime};
|
||||||
use std::time::{Duration, Instant as StdInstant, SystemTime};
|
|
||||||
|
|
||||||
use rustc_data_structures::sync::Ordering;
|
|
||||||
|
|
||||||
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 struct Instant {
|
|
||||||
kind: InstantKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum InstantKind {
|
|
||||||
Host(StdInstant),
|
|
||||||
Virtual { nanoseconds: u64 },
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A monotone clock used for `Instant` simulation.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Clock {
|
|
||||||
kind: ClockKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum ClockKind {
|
|
||||||
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 {
|
|
||||||
let kind = if communicate {
|
|
||||||
ClockKind::Host { time_anchor: StdInstant::now() }
|
|
||||||
} else {
|
|
||||||
ClockKind::Virtual { nanoseconds: 0.into() }
|
|
||||||
};
|
|
||||||
|
|
||||||
Self { kind }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the current time relative to this clock.
|
|
||||||
pub fn get(&self) -> Duration {
|
|
||||||
match &self.kind {
|
|
||||||
ClockKind::Host { time_anchor } =>
|
|
||||||
StdInstant::now().saturating_duration_since(*time_anchor),
|
|
||||||
ClockKind::Virtual { nanoseconds } =>
|
|
||||||
Duration::from_nanos(nanoseconds.load(Ordering::Relaxed)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Let the time pass for a small interval.
|
|
||||||
pub fn tick(&self) {
|
|
||||||
match &self.kind {
|
|
||||||
ClockKind::Host { .. } => {
|
|
||||||
// Time will pass without us doing anything.
|
|
||||||
}
|
|
||||||
ClockKind::Virtual { nanoseconds } => {
|
|
||||||
nanoseconds.fetch_add(NANOSECOND_PER_BASIC_BLOCK, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sleep for the desired duration.
|
|
||||||
pub fn sleep(&self, duration: Duration) {
|
|
||||||
match &self.kind {
|
|
||||||
ClockKind::Host { .. } => std::thread::sleep(duration),
|
|
||||||
ClockKind::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.kind {
|
|
||||||
ClockKind::Host { .. } =>
|
|
||||||
StdInstant::now()
|
|
||||||
.checked_add(duration)
|
|
||||||
.map(|instant| Time::Monotonic(Instant { kind: InstantKind::Host(instant) })),
|
|
||||||
ClockKind::Virtual { nanoseconds } =>
|
|
||||||
nanoseconds
|
|
||||||
.load(Ordering::Relaxed)
|
|
||||||
.checked_add(duration.as_nanos().try_into().unwrap())
|
|
||||||
.map(|nanoseconds| {
|
|
||||||
Time::Monotonic(Instant { kind: InstantKind::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.kind {
|
|
||||||
ClockKind::Host { time_anchor } =>
|
|
||||||
time_anchor
|
|
||||||
.checked_add(duration)
|
|
||||||
.map(|instant| Time::Monotonic(Instant { kind: InstantKind::Host(instant) })),
|
|
||||||
ClockKind::Virtual { .. } =>
|
|
||||||
Some(Time::Monotonic(Instant {
|
|
||||||
kind: InstantKind::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.kind, &self.kind) {
|
|
||||||
(InstantKind::Host(instant), ClockKind::Host { .. }) =>
|
|
||||||
instant.saturating_duration_since(StdInstant::now()),
|
|
||||||
(
|
|
||||||
InstantKind::Virtual { nanoseconds },
|
|
||||||
ClockKind::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)
|
||||||
@ -354,7 +216,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||||||
|
|
||||||
this.register_timeout_callback(
|
this.register_timeout_callback(
|
||||||
active_thread,
|
active_thread,
|
||||||
timeout_time,
|
Time::Monotonic(timeout_time),
|
||||||
Box::new(move |ecx| {
|
Box::new(move |ecx| {
|
||||||
ecx.unblock_thread(active_thread);
|
ecx.unblock_thread(active_thread);
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -380,7 +242,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||||||
|
|
||||||
this.register_timeout_callback(
|
this.register_timeout_callback(
|
||||||
active_thread,
|
active_thread,
|
||||||
timeout_time,
|
Time::Monotonic(timeout_time),
|
||||||
Box::new(move |ecx| {
|
Box::new(move |ecx| {
|
||||||
ecx.unblock_thread(active_thread);
|
ecx.unblock_thread(active_thread);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -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 {
|
||||||
this.machine.clock.get_time_absolute(duration).unwrap()
|
Time::Monotonic(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 {
|
||||||
this.machine.clock.get_time_relative(duration).unwrap()
|
Time::Monotonic(this.machine.clock.get_time_relative(duration).unwrap())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
@ -840,7 +840,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||||||
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")? {
|
||||||
this.machine.clock.get_time_absolute(duration).unwrap()
|
Time::Monotonic(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);
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user