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).
This commit is contained in:
commit
e6ffc689aa
@ -477,6 +477,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
|
||||
if self.threads[self.active_thread].check_terminated() {
|
||||
return Ok(SchedulingAction::ExecuteDtors);
|
||||
}
|
||||
// If we get here again and the thread is *still* terminated, there are no more dtors to run.
|
||||
if self.threads[MAIN_THREAD].state == ThreadState::Terminated {
|
||||
// The main thread terminated; stop the program.
|
||||
if self.threads.iter().any(|thread| thread.state != ThreadState::Terminated) {
|
||||
@ -490,26 +491,25 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
|
||||
}
|
||||
return Ok(SchedulingAction::Stop);
|
||||
}
|
||||
// At least for `pthread_cond_timedwait` we need to report timeout when
|
||||
// the function is called already after the specified time even if a
|
||||
// signal is received before the thread gets scheduled. Therefore, we
|
||||
// need to schedule all timeout callbacks before we continue regular
|
||||
// execution.
|
||||
//
|
||||
// Documentation:
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html#
|
||||
let potential_sleep_time =
|
||||
self.timeout_callbacks.values().map(|info| info.call_time.get_wait_time()).min();
|
||||
if potential_sleep_time == Some(Duration::new(0, 0)) {
|
||||
return Ok(SchedulingAction::ExecuteTimeoutCallback);
|
||||
}
|
||||
// No callbacks scheduled, pick a regular thread to execute.
|
||||
// This thread and the program can keep going.
|
||||
if self.threads[self.active_thread].state == ThreadState::Enabled
|
||||
&& !self.yield_active_thread
|
||||
{
|
||||
// The currently active thread is still enabled, just continue with it.
|
||||
return Ok(SchedulingAction::ExecuteStep);
|
||||
}
|
||||
// The active thread yielded. Let's see if there are any timeouts to take care of. We do
|
||||
// this *before* running any other thread, to ensure that timeouts "in the past" fire before
|
||||
// any other thread can take an action. This ensures that for `pthread_cond_timedwait`, "an
|
||||
// error is returned if [...] the absolute time specified by abstime has already been passed
|
||||
// at the time of the call".
|
||||
// <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html>
|
||||
let potential_sleep_time =
|
||||
self.timeout_callbacks.values().map(|info| info.call_time.get_wait_time()).min();
|
||||
if potential_sleep_time == Some(Duration::new(0, 0)) {
|
||||
return Ok(SchedulingAction::ExecuteTimeoutCallback);
|
||||
}
|
||||
// No callbacks scheduled, pick a regular thread to execute.
|
||||
// We need to pick a new thread for execution.
|
||||
for (id, thread) in self.threads.iter_enumerated() {
|
||||
if thread.state == ThreadState::Enabled {
|
||||
|
@ -38,6 +38,12 @@ fn test_timed_wait_timeout(clock_id: i32) {
|
||||
let elapsed_time = current_time.elapsed().as_millis();
|
||||
assert!(900 <= elapsed_time && elapsed_time <= 1300);
|
||||
|
||||
// Test calling `pthread_cond_timedwait` again with an already elapsed timeout.
|
||||
assert_eq!(
|
||||
libc::pthread_cond_timedwait(&mut cond as *mut _, &mut mutex as *mut _, &timeout),
|
||||
libc::ETIMEDOUT
|
||||
);
|
||||
|
||||
// Test that invalid nanosecond values (above 10^9 or negative) are rejected with the
|
||||
// correct error code.
|
||||
let invalid_timeout_1 = libc::timespec { tv_sec: now.tv_sec + 1, tv_nsec: 1_000_000_000 };
|
||||
|
Loading…
x
Reference in New Issue
Block a user