use std::cell::RefCell;
use std::collections::hash_map::Entry;
use std::convert::TryFrom;
+use std::rc::Rc;
use std::num::TryFromIntError;
use std::time::{Duration, Instant, SystemTime};
fn set_thread_local_alloc_id(&self, def_id: DefId, new_alloc_id: AllocId) {
self.thread_local_alloc_ids
.borrow_mut()
- .insert((def_id, self.active_thread), new_alloc_id)
- .unwrap_none();
+ .try_insert((def_id, self.active_thread), new_alloc_id)
+ .unwrap();
}
/// Borrow the stack of the active thread.
}
/// Mark that the active thread tries to join the thread with `joined_thread_id`.
- fn join_thread(&mut self, joined_thread_id: ThreadId, data_race: &data_race::GlobalState) -> InterpResult<'tcx> {
+ fn join_thread(&mut self, joined_thread_id: ThreadId, data_race: &Option<Rc<data_race::GlobalState>>) -> InterpResult<'tcx> {
if self.threads[joined_thread_id].join_status != ThreadJoinStatus::Joinable {
throw_ub_format!("trying to join a detached or already joined thread");
}
self.active_thread,
joined_thread_id
);
- }else{
+ } else {
// The thread has already terminated - mark join happens-before
- data_race.thread_joined(self.active_thread, joined_thread_id);
+ if let Some(data_race) = data_race {
+ data_race.thread_joined(self.active_thread, joined_thread_id);
+ }
}
Ok(())
}
callback: TimeoutCallback<'mir, 'tcx>,
) {
self.timeout_callbacks
- .insert(thread, TimeoutCallbackInfo { call_time, callback })
- .unwrap_none();
+ .try_insert(thread, TimeoutCallbackInfo { call_time, callback })
+ .unwrap();
}
/// Unregister the callback for the `thread`.
/// Wakes up threads joining on the active one and deallocates thread-local statics.
/// The `AllocId` that can now be freed is returned.
- fn thread_terminated(&mut self, data_race: &data_race::GlobalState) -> Vec<AllocId> {
+ fn thread_terminated(&mut self, data_race: &Option<Rc<data_race::GlobalState>>) -> Vec<AllocId> {
let mut free_tls_statics = Vec::new();
{
let mut thread_local_statics = self.thread_local_alloc_ids.borrow_mut();
});
}
// Set the thread into a terminated state in the data-race detector
- data_race.thread_terminated();
+ if let Some(data_race) = data_race {
+ data_race.thread_terminated();
+ }
// Check if we need to unblock any threads.
for (i, thread) in self.threads.iter_enumerated_mut() {
if thread.state == ThreadState::BlockedOnJoin(self.active_thread) {
// The thread has terminated, mark happens-before edge to joining thread
- data_race.thread_joined(i, self.active_thread);
+ if let Some(data_race) = data_race {
+ data_race.thread_joined(i, self.active_thread);
+ }
trace!("unblocking {:?} because {:?} terminated", i, self.active_thread);
thread.state = ThreadState::Enabled;
}
/// used in stateless model checkers such as Loom: run the active thread as
/// long as we can and switch only when we have to (the active thread was
/// blocked, terminated, or has explicitly asked to be preempted).
- fn schedule(&mut self, data_race: &data_race::GlobalState) -> InterpResult<'tcx, SchedulingAction> {
+ fn schedule(&mut self, data_race: &Option<Rc<data_race::GlobalState>>) -> InterpResult<'tcx, SchedulingAction> {
// Check whether the thread has **just** terminated (`check_terminated`
// checks whether the thread has popped all its stack and if yes, sets
// the thread state to terminated).
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) {
}
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 {
if !self.yield_active_thread || id != self.active_thread {
self.active_thread = id;
- data_race.thread_set_active(self.active_thread);
+ if let Some(data_race) = data_race {
+ data_race.thread_set_active(self.active_thread);
+ }
break;
}
}
fn create_thread(&mut self) -> ThreadId {
let this = self.eval_context_mut();
let id = this.machine.threads.create_thread();
- this.memory.extra.data_race.thread_created(id);
+ if let Some(data_race) = &this.memory.extra.data_race {
+ data_race.thread_created(id);
+ }
id
}
#[inline]
fn join_thread(&mut self, joined_thread_id: ThreadId) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
- let data_race = &*this.memory.extra.data_race;
+ let data_race = &this.memory.extra.data_race;
this.machine.threads.join_thread(joined_thread_id, data_race)?;
Ok(())
}
#[inline]
fn set_active_thread(&mut self, thread_id: ThreadId) -> ThreadId {
let this = self.eval_context_mut();
- this.memory.extra.data_race.thread_set_active(thread_id);
+ if let Some(data_race) = &this.memory.extra.data_race {
+ data_race.thread_set_active(thread_id);
+ }
this.machine.threads.set_active_thread_id(thread_id)
}
#[inline]
fn set_active_thread_name(&mut self, new_thread_name: Vec<u8>) {
let this = self.eval_context_mut();
- if let Ok(string) = String::from_utf8(new_thread_name.clone()) {
- this.memory.extra.data_race.thread_set_name(
- this.machine.threads.active_thread, string
- );
+ if let Some(data_race) = &this.memory.extra.data_race {
+ if let Ok(string) = String::from_utf8(new_thread_name.clone()) {
+ data_race.thread_set_name(
+ this.machine.threads.active_thread, string
+ );
+ }
}
this.machine.threads.set_thread_name(new_thread_name);
}
#[inline]
fn schedule(&mut self) -> InterpResult<'tcx, SchedulingAction> {
let this = self.eval_context_mut();
- let data_race = &*this.memory.extra.data_race;
+ let data_race = &this.memory.extra.data_race;
this.machine.threads.schedule(data_race)
}
#[inline]
fn thread_terminated(&mut self) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
- let data_race = &*this.memory.extra.data_race;
+ let data_race = &this.memory.extra.data_race;
for alloc_id in this.machine.threads.thread_terminated(data_race) {
let ptr = this.memory.global_base_pointer(alloc_id.into())?;
this.memory.deallocate(ptr, None, MiriMemoryKind::Tls.into())?;