Skip to content

Commit

Permalink
Fix infinite looping timer thread
Browse files Browse the repository at this point in the history
Due to undefined behavior in pthread when passing in large timeout
values, the park_timeout call of the timer thread was spinning very
tightly and almost continuously causing poll wakeups. This commit
changes the behavior to use a blocking park call when the timeout value
is large.

Fixes #483

Signed-off-by: Nick Stevens <nick@bitcurry.com>
  • Loading branch information
nastevens authored and alexcrichton committed Oct 31, 2016
1 parent bfdb617 commit 42a4172
Showing 1 changed file with 17 additions and 3 deletions.
20 changes: 17 additions & 3 deletions src/timer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ impl<T> Timer<T> {
if actual == curr {
// Signal to the wakeup thread that the wakeup time has
// been changed.
trace!("unparking wakeup thread");
inner.wakeup_thread.thread().unpark();
return;
}
Expand Down Expand Up @@ -425,9 +426,22 @@ fn spawn_wakeup_thread(state: WakeupState, set_readiness: SetReadiness, start: I
trace!("wakeup thread: sleep_until_tick={:?}; now_tick={:?}", sleep_until_tick, now_tick);

if now_tick < sleep_until_tick {
let sleep_duration = tick_ms.checked_mul(sleep_until_tick - now_tick).unwrap_or(u64::MAX);
trace!("sleeping; tick_ms={}; now_tick={}; sleep_until_tick={}; duration={:?}", tick_ms, now_tick, sleep_until_tick, sleep_duration);
thread::park_timeout(Duration::from_millis(sleep_duration));
// Calling park_timeout with u64::MAX leads to undefined
// behavior in pthread, causing the park to return immediately
// and causing the thread to tightly spin. Instead of u64::MAX
// on large values, simply use a blocking park.
match tick_ms.checked_mul(sleep_until_tick - now_tick) {
Some(sleep_duration) => {
trace!("sleeping; tick_ms={}; now_tick={}; sleep_until_tick={}; duration={:?}",
tick_ms, now_tick, sleep_until_tick, sleep_duration);
thread::park_timeout(Duration::from_millis(sleep_duration));
}
None => {
trace!("sleeping; tick_ms={}; now_tick={}; blocking sleep",
tick_ms, now_tick);
thread::park();
}
}
sleep_until_tick = state.load(Ordering::Acquire) as Tick;
} else {
let actual = state.compare_and_swap(sleep_until_tick as usize, usize::MAX, Ordering::AcqRel) as Tick;
Expand Down

0 comments on commit 42a4172

Please sign in to comment.