Skip to content

Commit e6ffc68

Browse files
committed
Auto merge of #1772 - RalfJung:less-timeout-checking, r=RalfJung
only check timeouts when a thread yields Currently, we check for expired timeouts after each step of execution. That seems excessive. This changes the scheduler to only check for timeouts when the active thread cannot continue running any more. `@vakaras` does this sound right? `pthread_cond_timedwait` anyway already yields, of course, since it blocks on getting the signal (or the timeout).
2 parents afabaf3 + 0674d43 commit e6ffc68

File tree

2 files changed

+20
-14
lines changed

2 files changed

+20
-14
lines changed

src/thread.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
477477
if self.threads[self.active_thread].check_terminated() {
478478
return Ok(SchedulingAction::ExecuteDtors);
479479
}
480+
// If we get here again and the thread is *still* terminated, there are no more dtors to run.
480481
if self.threads[MAIN_THREAD].state == ThreadState::Terminated {
481482
// The main thread terminated; stop the program.
482483
if self.threads.iter().any(|thread| thread.state != ThreadState::Terminated) {
@@ -490,26 +491,25 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
490491
}
491492
return Ok(SchedulingAction::Stop);
492493
}
493-
// At least for `pthread_cond_timedwait` we need to report timeout when
494-
// the function is called already after the specified time even if a
495-
// signal is received before the thread gets scheduled. Therefore, we
496-
// need to schedule all timeout callbacks before we continue regular
497-
// execution.
498-
//
499-
// Documentation:
500-
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html#
501-
let potential_sleep_time =
502-
self.timeout_callbacks.values().map(|info| info.call_time.get_wait_time()).min();
503-
if potential_sleep_time == Some(Duration::new(0, 0)) {
504-
return Ok(SchedulingAction::ExecuteTimeoutCallback);
505-
}
506-
// No callbacks scheduled, pick a regular thread to execute.
494+
// This thread and the program can keep going.
507495
if self.threads[self.active_thread].state == ThreadState::Enabled
508496
&& !self.yield_active_thread
509497
{
510498
// The currently active thread is still enabled, just continue with it.
511499
return Ok(SchedulingAction::ExecuteStep);
512500
}
501+
// The active thread yielded. Let's see if there are any timeouts to take care of. We do
502+
// this *before* running any other thread, to ensure that timeouts "in the past" fire before
503+
// any other thread can take an action. This ensures that for `pthread_cond_timedwait`, "an
504+
// error is returned if [...] the absolute time specified by abstime has already been passed
505+
// at the time of the call".
506+
// <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html>
507+
let potential_sleep_time =
508+
self.timeout_callbacks.values().map(|info| info.call_time.get_wait_time()).min();
509+
if potential_sleep_time == Some(Duration::new(0, 0)) {
510+
return Ok(SchedulingAction::ExecuteTimeoutCallback);
511+
}
512+
// No callbacks scheduled, pick a regular thread to execute.
513513
// We need to pick a new thread for execution.
514514
for (id, thread) in self.threads.iter_enumerated() {
515515
if thread.state == ThreadState::Enabled {

tests/run-pass/concurrency/libc_pthread_cond.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ fn test_timed_wait_timeout(clock_id: i32) {
3838
let elapsed_time = current_time.elapsed().as_millis();
3939
assert!(900 <= elapsed_time && elapsed_time <= 1300);
4040

41+
// Test calling `pthread_cond_timedwait` again with an already elapsed timeout.
42+
assert_eq!(
43+
libc::pthread_cond_timedwait(&mut cond as *mut _, &mut mutex as *mut _, &timeout),
44+
libc::ETIMEDOUT
45+
);
46+
4147
// Test that invalid nanosecond values (above 10^9 or negative) are rejected with the
4248
// correct error code.
4349
let invalid_timeout_1 = libc::timespec { tv_sec: now.tv_sec + 1, tv_nsec: 1_000_000_000 };

0 commit comments

Comments
 (0)