Skip to content

Commit 13ac242

Browse files
committed
remove Arc
1 parent 3a768bb commit 13ac242

File tree

5 files changed

+78
-69
lines changed

5 files changed

+78
-69
lines changed

src/concurrency/thread.rs

Lines changed: 27 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
use std::cell::RefCell;
44
use std::collections::hash_map::Entry;
55
use std::num::TryFromIntError;
6-
use std::sync::atomic::AtomicU64;
7-
use std::sync::Arc;
86
use std::time::{Duration, Instant, SystemTime};
97

108
use log::trace;
@@ -19,7 +17,7 @@ use rustc_target::spec::abi::Abi;
1917

2018
use crate::concurrency::data_race;
2119
use crate::concurrency::sync::SynchronizationState;
22-
use crate::shims::time::TimeAnchor;
20+
use crate::shims::time::Clock;
2321
use crate::*;
2422

2523
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -188,22 +186,17 @@ impl<'mir, 'tcx> Thread<'mir, 'tcx> {
188186
#[derive(Debug)]
189187
pub enum Time {
190188
Monotonic(Instant),
191-
Virtual {
192-
/// The instant for this moment.
193-
instant: u64,
194-
/// A reference to the time anchor used to read the current global virtual time.
195-
time_anchor: Arc<AtomicU64>,
196-
},
189+
Virtual { nanoseconds: u64 },
197190
RealTime(SystemTime),
198191
}
199192

200193
impl Time {
201194
/// How long do we have to wait from now until the specified time?
202-
fn get_wait_time(&self) -> Duration {
195+
fn get_wait_time(&self, clock: &Clock) -> Duration {
203196
match self {
204197
Time::Monotonic(instant) => instant.saturating_duration_since(Instant::now()),
205-
Time::Virtual { instant, time_anchor } =>
206-
Duration::from_nanos(instant - time_anchor.load(Ordering::Relaxed)),
198+
Time::Virtual { nanoseconds } =>
199+
Duration::from_nanos(nanoseconds - clock.assert_virtual().load(Ordering::Relaxed)),
207200
Time::RealTime(time) =>
208201
time.duration_since(SystemTime::now()).unwrap_or(Duration::new(0, 0)),
209202
}
@@ -246,8 +239,6 @@ pub struct ThreadManager<'mir, 'tcx> {
246239
yield_active_thread: bool,
247240
/// Callbacks that are called once the specified time passes.
248241
timeout_callbacks: FxHashMap<ThreadId, TimeoutCallbackInfo<'mir, 'tcx>>,
249-
/// The "time anchor" for this machine's monotone clock (for `Instant` simulation).
250-
pub(crate) time_anchor: TimeAnchor,
251242
}
252243

253244
impl<'mir, 'tcx> Default for ThreadManager<'mir, 'tcx> {
@@ -262,7 +253,6 @@ impl<'mir, 'tcx> Default for ThreadManager<'mir, 'tcx> {
262253
thread_local_alloc_ids: Default::default(),
263254
yield_active_thread: false,
264255
timeout_callbacks: FxHashMap::default(),
265-
time_anchor: TimeAnchor::new(false),
266256
}
267257
}
268258
}
@@ -273,8 +263,6 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
273263
// The main thread can *not* be joined on except on windows.
274264
ecx.machine.threads.threads[ThreadId::new(0)].join_status = ThreadJoinStatus::Detached;
275265
}
276-
277-
ecx.machine.threads.time_anchor = TimeAnchor::new(ecx.machine.communicate());
278266
}
279267

280268
/// Check if we have an allocation for the given thread local static for the
@@ -503,13 +491,16 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
503491
}
504492

505493
/// Get a callback that is ready to be called.
506-
fn get_ready_callback(&mut self) -> Option<(ThreadId, TimeoutCallback<'mir, 'tcx>)> {
494+
fn get_ready_callback(
495+
&mut self,
496+
clock: &Clock,
497+
) -> Option<(ThreadId, TimeoutCallback<'mir, 'tcx>)> {
507498
// We iterate over all threads in the order of their indices because
508499
// this allows us to have a deterministic scheduler.
509500
for thread in self.threads.indices() {
510501
match self.timeout_callbacks.entry(thread) {
511502
Entry::Occupied(entry) =>
512-
if entry.get().call_time.get_wait_time() == Duration::new(0, 0) {
503+
if entry.get().call_time.get_wait_time(clock) == Duration::new(0, 0) {
513504
return Some((thread, entry.remove().callback));
514505
},
515506
Entry::Vacant(_) => {}
@@ -566,7 +557,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
566557
/// used in stateless model checkers such as Loom: run the active thread as
567558
/// long as we can and switch only when we have to (the active thread was
568559
/// blocked, terminated, or has explicitly asked to be preempted).
569-
fn schedule(&mut self) -> InterpResult<'tcx, SchedulingAction> {
560+
fn schedule(&mut self, clock: &Clock) -> InterpResult<'tcx, SchedulingAction> {
570561
// Check whether the thread has **just** terminated (`check_terminated`
571562
// checks whether the thread has popped all its stack and if yes, sets
572563
// the thread state to terminated).
@@ -593,7 +584,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
593584
// at the time of the call".
594585
// <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html>
595586
let potential_sleep_time =
596-
self.timeout_callbacks.values().map(|info| info.call_time.get_wait_time()).min();
587+
self.timeout_callbacks.values().map(|info| info.call_time.get_wait_time(clock)).min();
597588
if potential_sleep_time == Some(Duration::new(0, 0)) {
598589
return Ok(SchedulingAction::ExecuteTimeoutCallback);
599590
}
@@ -629,7 +620,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
629620
// timeout_callbacks, which may unblock some of the threads. Hence,
630621
// sleep until the first callback.
631622

632-
self.time_anchor.sleep(sleep_time);
623+
clock.sleep(sleep_time);
633624
Ok(SchedulingAction::ExecuteTimeoutCallback)
634625
} else {
635626
throw_machine_stop!(TerminationInfo::Deadlock);
@@ -892,18 +883,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
892883
#[inline]
893884
fn run_timeout_callback(&mut self) -> InterpResult<'tcx> {
894885
let this = self.eval_context_mut();
895-
let (thread, callback) =
896-
if let Some((thread, callback)) = this.machine.threads.get_ready_callback() {
897-
(thread, callback)
898-
} else {
899-
// get_ready_callback can return None if the computer's clock
900-
// was shifted after calling the scheduler and before the call
901-
// to get_ready_callback (see issue
902-
// https://github.com/rust-lang/miri/issues/1763). In this case,
903-
// just do nothing, which effectively just returns to the
904-
// scheduler.
905-
return Ok(());
906-
};
886+
let (thread, callback) = if let Some((thread, callback)) =
887+
this.machine.threads.get_ready_callback(&this.machine.clock)
888+
{
889+
(thread, callback)
890+
} else {
891+
// get_ready_callback can return None if the computer's clock
892+
// was shifted after calling the scheduler and before the call
893+
// to get_ready_callback (see issue
894+
// https://github.com/rust-lang/miri/issues/1763). In this case,
895+
// just do nothing, which effectively just returns to the
896+
// scheduler.
897+
return Ok(());
898+
};
907899
// This back-and-forth with `set_active_thread` is here because of two
908900
// design decisions:
909901
// 1. Make the caller and not the callback responsible for changing
@@ -920,7 +912,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
920912
#[inline]
921913
fn schedule(&mut self) -> InterpResult<'tcx, SchedulingAction> {
922914
let this = self.eval_context_mut();
923-
this.machine.threads.schedule()
915+
this.machine.threads.schedule(&this.machine.clock)
924916
}
925917

926918
/// Handles thread termination of the active thread: wakes up threads joining on this one,

src/machine.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use rustc_target::spec::abi::Abi;
2727

2828
use crate::{
2929
concurrency::{data_race, weak_memory},
30-
shims::unix::FileHandler,
30+
shims::{unix::FileHandler, time::Clock},
3131
*,
3232
};
3333

@@ -293,6 +293,9 @@ pub struct Evaluator<'mir, 'tcx> {
293293
/// The table of directory descriptors.
294294
pub(crate) dir_handler: shims::unix::DirHandler,
295295

296+
/// FIXME: docs
297+
pub(crate) clock: Clock,
298+
296299
/// The set of threads.
297300
pub(crate) threads: ThreadManager<'mir, 'tcx>,
298301

@@ -407,6 +410,7 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
407410
preemption_rate: config.preemption_rate,
408411
report_progress: config.report_progress,
409412
since_progress_report: 0,
413+
clock: Clock::new(config.isolated_op == IsolatedOp::Allow),
410414
}
411415
}
412416

@@ -957,7 +961,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
957961
// These are our preemption points.
958962
ecx.maybe_preempt_active_thread();
959963

960-
ecx.machine.threads.time_anchor.tick();
964+
ecx.machine.clock.tick();
961965

962966
Ok(())
963967
}

src/shims/time.rs

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use std::sync::atomic::AtomicU64;
2-
use std::sync::Arc;
32
use std::time::{Duration, Instant, SystemTime};
43

54
use rustc_data_structures::sync::Ordering;
@@ -8,60 +7,75 @@ use crate::concurrency::thread::Time;
87
use crate::*;
98

109
#[derive(Debug)]
11-
pub enum TimeAnchor {
12-
Host(Instant),
13-
Virtual(Arc<AtomicU64>),
10+
pub enum Clock {
11+
Host {
12+
/// The "time anchor" for this machine's monotone clock (for `Instant` simulation).
13+
time_anchor: Instant,
14+
},
15+
Virtual {
16+
/// The "current virtual time".
17+
nanoseconds: AtomicU64,
18+
},
1419
}
1520

16-
impl TimeAnchor {
21+
impl Clock {
1722
pub fn new(communicate: bool) -> Self {
18-
if communicate { Self::Host(Instant::now()) } else { Self::Virtual(Arc::new(0.into())) }
23+
if communicate {
24+
Self::Host { time_anchor: Instant::now() }
25+
} else {
26+
Self::Virtual { nanoseconds: 0.into() }
27+
}
1928
}
2029

2130
pub fn get(&self) -> Duration {
2231
match self {
23-
Self::Host(instant) => Instant::now().saturating_duration_since(*instant),
24-
Self::Virtual(nanoseconds) => Duration::from_nanos(nanoseconds.load(Ordering::Relaxed)),
32+
Self::Host { time_anchor } => Instant::now().saturating_duration_since(*time_anchor),
33+
Self::Virtual { nanoseconds } =>
34+
Duration::from_nanos(nanoseconds.load(Ordering::Relaxed)),
2535
}
2636
}
2737

2838
pub fn tick(&self) {
2939
match self {
30-
Self::Host(_) => (),
31-
Self::Virtual(nanoseconds) => {
40+
Self::Host { .. } => (),
41+
Self::Virtual { nanoseconds } => {
3242
nanoseconds.fetch_add(1, Ordering::Relaxed);
3343
}
3444
}
3545
}
3646

3747
pub fn sleep(&self, duration: Duration) {
3848
match self {
39-
Self::Host(_) => std::thread::sleep(duration),
40-
Self::Virtual(nanoseconds) => {
49+
Self::Host { .. } => std::thread::sleep(duration),
50+
Self::Virtual { nanoseconds } => {
4151
nanoseconds.fetch_add(duration.as_nanos().try_into().unwrap(), Ordering::Relaxed);
4252
}
4353
}
4454
}
4555

4656
pub fn checked_add_since_now(&self, duration: Duration) -> Option<Time> {
4757
match self {
48-
Self::Host(_) => Instant::now().checked_add(duration).map(Time::Monotonic),
49-
Self::Virtual(time_anchor) =>
50-
time_anchor
58+
Self::Host { .. } => Instant::now().checked_add(duration).map(Time::Monotonic),
59+
Self::Virtual { nanoseconds } =>
60+
nanoseconds
5161
.load(Ordering::Relaxed)
5262
.checked_add(duration.as_nanos().try_into().unwrap())
53-
.map(|instant| Time::Virtual { instant, time_anchor: time_anchor.clone() }),
63+
.map(|nanoseconds| Time::Virtual { nanoseconds }),
5464
}
5565
}
5666

5767
pub fn checked_add_since_start(&self, duration: Duration) -> Option<Time> {
5868
match self {
59-
Self::Host(instant) => instant.checked_add(duration).map(Time::Monotonic),
60-
Self::Virtual(time_anchor) =>
61-
Some(Time::Virtual {
62-
instant: duration.as_nanos().try_into().unwrap(),
63-
time_anchor: time_anchor.clone(),
64-
}),
69+
Self::Host { time_anchor } => time_anchor.checked_add(duration).map(Time::Monotonic),
70+
Self::Virtual { .. } =>
71+
Some(Time::Virtual { nanoseconds: duration.as_nanos().try_into().unwrap() }),
72+
}
73+
}
74+
75+
pub(crate) fn assert_virtual(&self) -> &AtomicU64 {
76+
match self {
77+
Clock::Host { .. } => panic!(),
78+
Clock::Virtual { nanoseconds } => nanoseconds,
6579
}
6680
}
6781
}
@@ -105,7 +119,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
105119
this.check_no_isolation("`clock_gettime` with real time clocks")?;
106120
system_time_to_duration(&SystemTime::now())?
107121
} else if relative_clocks.contains(&clk_id) {
108-
this.machine.threads.time_anchor.get()
122+
this.machine.clock.get()
109123
} else {
110124
let einval = this.eval_libc("EINVAL")?;
111125
this.set_last_error(einval)?;
@@ -190,7 +204,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
190204

191205
// QueryPerformanceCounter uses a hardware counter as its basis.
192206
// Miri will emulate a counter with a resolution of 1 nanosecond.
193-
let duration = this.machine.threads.time_anchor.get();
207+
let duration = this.machine.clock.get();
194208
let qpc = i64::try_from(duration.as_nanos()).map_err(|_| {
195209
err_unsup_format!("programs running longer than 2^63 nanoseconds are not supported")
196210
})?;
@@ -231,7 +245,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
231245

232246
// This returns a u64, with time units determined dynamically by `mach_timebase_info`.
233247
// We return plain nanoseconds.
234-
let duration = this.machine.threads.time_anchor.get();
248+
let duration = this.machine.clock.get();
235249
let res = u64::try_from(duration.as_nanos()).map_err(|_| {
236250
err_unsup_format!("programs running longer than 2^64 nanoseconds are not supported")
237251
})?;
@@ -276,10 +290,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
276290
};
277291
// If adding the duration overflows, let's just sleep for an hour. Waking up early is always acceptable.
278292
let timeout_time =
279-
this.machine.threads.time_anchor.checked_add_since_now(duration).unwrap_or_else(|| {
293+
this.machine.clock.checked_add_since_now(duration).unwrap_or_else(|| {
280294
this.machine
281-
.threads
282-
.time_anchor
295+
.clock
283296
.checked_add_since_now(Duration::from_secs(3600))
284297
.unwrap()
285298
});
@@ -309,7 +322,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
309322

310323
let duration = Duration::from_millis(timeout_ms.into());
311324
let timeout_time =
312-
this.machine.threads.time_anchor.checked_add_since_now(duration).unwrap();
325+
this.machine.clock.checked_add_since_now(duration).unwrap();
313326

314327
let active_thread = this.get_active_thread();
315328
this.block_thread(active_thread);

src/shims/unix/linux/sync.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,14 @@ pub fn futex<'tcx>(
106106
if op & futex_realtime != 0 {
107107
Time::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap())
108108
} else {
109-
this.machine.threads.time_anchor.checked_add_since_start(duration).unwrap()
109+
this.machine.clock.checked_add_since_start(duration).unwrap()
110110
}
111111
} else {
112112
// FUTEX_WAIT uses a relative timestamp.
113113
if op & futex_realtime != 0 {
114114
Time::RealTime(SystemTime::now().checked_add(duration).unwrap())
115115
} else {
116-
this.machine.threads.time_anchor.checked_add_since_now(duration).unwrap()
116+
this.machine.clock.checked_add_since_now(duration).unwrap()
117117
}
118118
})
119119
};

src/shims/unix/sync.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
859859
let timeout_time = if clock_id == this.eval_libc_i32("CLOCK_REALTIME")? {
860860
Time::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap())
861861
} else if clock_id == this.eval_libc_i32("CLOCK_MONOTONIC")? {
862-
this.machine.threads.time_anchor.checked_add_since_start(duration).unwrap()
862+
this.machine.clock.checked_add_since_start(duration).unwrap()
863863
} else {
864864
throw_unsup_format!("unsupported clock id: {}", clock_id);
865865
};

0 commit comments

Comments
 (0)