Skip to content

Commit ef40ce0

Browse files
authored
Detect spinning and abort (#63)
1 parent 9f175e3 commit ef40ce0

File tree

1 file changed

+33
-4
lines changed

1 file changed

+33
-4
lines changed

msim/src/sim/time/mod.rs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -282,20 +282,44 @@ struct Clock {
282282
base_instant: Instant,
283283
/// The amount of mock time which has elapsed.
284284
elapsed_time: Duration,
285+
286+
/// Reset every time the clock is advanced.
287+
/// Decremented every time the clock is read.
288+
/// If it reaches 0, we panic - otherwise code like
289+
/// while Instant::now() < deadline { ... } will spin forever.
290+
abort_counter: u32,
291+
}
292+
293+
impl Clock {
294+
const ABORT_COUNTER_START: u32 = 100_000_000;
295+
296+
#[inline(always)]
297+
fn check_abort(&mut self) {
298+
self.abort_counter -= 1;
299+
if self.abort_counter == 0 {
300+
panic!("Likely infinite loop detected: clock sampled too many times without advancing");
301+
}
302+
}
303+
304+
#[inline(always)]
305+
fn reset_abort_counter(&mut self) {
306+
self.abort_counter = Self::ABORT_COUNTER_START;
307+
}
285308
}
286309

287310
impl ClockHandle {
288311
const CLOCK_BASE: Duration = Duration::from_secs(86400);
289312

290313
fn new(base_time: SystemTime) -> Self {
291314
let base_instant: Instant = unsafe { std::mem::zeroed() };
292-
let clock = Clock {
315+
let mut clock = Clock {
293316
base_time,
294317
base_instant,
295318
// Some code subtracts constant durations from Instant::now(), which underflows if the base
296319
// instant is too small. That code is incorrect but we'll just make life easy anyway by
297320
// starting the clock with one day of elapsed time.
298321
elapsed_time: Self::CLOCK_BASE,
322+
abort_counter: Clock::ABORT_COUNTER_START,
299323
};
300324
ClockHandle {
301325
inner: Arc::new(Mutex::new(clock)),
@@ -310,15 +334,18 @@ impl ClockHandle {
310334
let mut inner = self.inner.lock().unwrap();
311335
// prevent time from going backwards - otherwise this can happen when timers are late.
312336
inner.elapsed_time = std::cmp::max(inner.elapsed_time, time);
337+
inner.reset_abort_counter();
313338
}
314339

315340
fn elapsed(&self) -> Duration {
316-
let inner = self.inner.lock().unwrap();
341+
let mut inner = self.inner.lock().unwrap();
342+
inner.check_abort();
317343
inner.elapsed_time
318344
}
319345

320346
fn advance(&self, duration: Duration) {
321347
let mut inner = self.inner.lock().unwrap();
348+
inner.reset_abort_counter();
322349
inner.elapsed_time += duration;
323350
}
324351

@@ -328,12 +355,14 @@ impl ClockHandle {
328355
}
329356

330357
fn now_instant(&self) -> Instant {
331-
let inner = self.inner.lock().unwrap();
358+
let mut inner = self.inner.lock().unwrap();
359+
inner.check_abort();
332360
inner.base_instant + inner.elapsed_time
333361
}
334362

335363
fn now_time(&self) -> SystemTime {
336-
let inner = self.inner.lock().unwrap();
364+
let mut inner = self.inner.lock().unwrap();
365+
inner.check_abort();
337366
inner.base_time + inner.elapsed_time
338367
}
339368
}

0 commit comments

Comments
 (0)