1 //! Implements threads. 2 3 use std::cell::RefCell; 4 use std::collections::hash_map::Entry; 5 use std::convert::TryFrom; 6 use std::num::TryFromIntError; 7 use std::time::{Duration, Instant, SystemTime}; 8 9 use log::trace; 10 11 use rustc_data_structures::fx::FxHashMap; 12 use rustc_hir::def_id::DefId; 13 use rustc_index::vec::{Idx, IndexVec}; 14 15 use crate::sync::SynchronizationState; 16 use crate::*; 17 18 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 19 pub enum SchedulingAction { 20 /// Execute step on the active thread. 21 ExecuteStep, 22 /// Execute a timeout callback. 23 ExecuteTimeoutCallback, 24 /// Execute destructors of the active thread. 25 ExecuteDtors, 26 /// Stop the program. 27 Stop, 28 } 29 30 /// Timeout callbacks can be created by synchronization primitives to tell the 31 /// scheduler that they should be called once some period of time passes. 32 type TimeoutCallback<'mir, 'tcx> = 33 Box<dyn FnOnce(&mut InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>) -> InterpResult<'tcx> + 'tcx>; 34 35 /// A thread identifier. 36 #[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] 37 pub struct ThreadId(u32); 38 39 /// The main thread. When it terminates, the whole application terminates. 40 const MAIN_THREAD: ThreadId = ThreadId(0); 41 42 impl ThreadId { to_u32(self) -> u3243 pub fn to_u32(self) -> u32 { 44 self.0 45 } 46 } 47 48 impl Idx for ThreadId { new(idx: usize) -> Self49 fn new(idx: usize) -> Self { 50 ThreadId(u32::try_from(idx).unwrap()) 51 } 52 index(self) -> usize53 fn index(self) -> usize { 54 usize::try_from(self.0).unwrap() 55 } 56 } 57 58 impl TryFrom<u64> for ThreadId { 59 type Error = TryFromIntError; try_from(id: u64) -> Result<Self, Self::Error>60 fn try_from(id: u64) -> Result<Self, Self::Error> { 61 u32::try_from(id).map(|id_u32| Self(id_u32)) 62 } 63 } 64 65 impl From<u32> for ThreadId { from(id: u32) -> Self66 fn from(id: u32) -> Self { 67 Self(id) 68 } 69 } 70 71 impl ThreadId { to_u32_scalar<'tcx>(&self) -> Scalar<Tag>72 pub fn to_u32_scalar<'tcx>(&self) -> Scalar<Tag> { 73 Scalar::from_u32(u32::try_from(self.0).unwrap()) 74 } 75 } 76 77 /// The state of a thread. 78 #[derive(Debug, Copy, Clone, PartialEq, Eq)] 79 pub enum ThreadState { 80 /// The thread is enabled and can be executed. 81 Enabled, 82 /// The thread tried to join the specified thread and is blocked until that 83 /// thread terminates. 84 BlockedOnJoin(ThreadId), 85 /// The thread is blocked on some synchronization primitive. It is the 86 /// responsibility of the synchronization primitives to track threads that 87 /// are blocked by them. 88 BlockedOnSync, 89 /// The thread has terminated its execution. We do not delete terminated 90 /// threads (FIXME: why?). 91 Terminated, 92 } 93 94 /// The join status of a thread. 95 #[derive(Debug, Copy, Clone, PartialEq, Eq)] 96 enum ThreadJoinStatus { 97 /// The thread can be joined. 98 Joinable, 99 /// A thread is detached if its join handle was destroyed and no other 100 /// thread can join it. 101 Detached, 102 /// The thread was already joined by some thread and cannot be joined again. 103 Joined, 104 } 105 106 /// A thread. 107 pub struct Thread<'mir, 'tcx> { 108 state: ThreadState, 109 110 /// Name of the thread. 111 thread_name: Option<Vec<u8>>, 112 113 /// The virtual call stack. 114 stack: Vec<Frame<'mir, 'tcx, Tag, FrameData<'tcx>>>, 115 116 /// The join status. 117 join_status: ThreadJoinStatus, 118 119 /// The temporary used for storing the argument of 120 /// the call to `miri_start_panic` (the panic payload) when unwinding. 121 /// This is pointer-sized, and matches the `Payload` type in `src/libpanic_unwind/miri.rs`. 122 pub(crate) panic_payload: Option<Scalar<Tag>>, 123 124 /// Last OS error location in memory. It is a 32-bit integer. 125 pub(crate) last_error: Option<MPlaceTy<'tcx, Tag>>, 126 } 127 128 impl<'mir, 'tcx> Thread<'mir, 'tcx> { 129 /// Check if the thread is done executing (no more stack frames). If yes, 130 /// change the state to terminated and return `true`. check_terminated(&mut self) -> bool131 fn check_terminated(&mut self) -> bool { 132 if self.state == ThreadState::Enabled { 133 if self.stack.is_empty() { 134 self.state = ThreadState::Terminated; 135 return true; 136 } 137 } 138 false 139 } 140 141 /// Get the name of the current thread, or `<unnamed>` if it was not set. thread_name(&self) -> &[u8]142 fn thread_name(&self) -> &[u8] { 143 if let Some(ref thread_name) = self.thread_name { thread_name } else { b"<unnamed>" } 144 } 145 } 146 147 impl<'mir, 'tcx> std::fmt::Debug for Thread<'mir, 'tcx> { fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 149 write!( 150 f, 151 "{}({:?}, {:?})", 152 String::from_utf8_lossy(self.thread_name()), 153 self.state, 154 self.join_status 155 ) 156 } 157 } 158 159 impl<'mir, 'tcx> Default for Thread<'mir, 'tcx> { default() -> Self160 fn default() -> Self { 161 Self { 162 state: ThreadState::Enabled, 163 thread_name: None, 164 stack: Vec::new(), 165 join_status: ThreadJoinStatus::Joinable, 166 panic_payload: None, 167 last_error: None, 168 } 169 } 170 } 171 172 /// A specific moment in time. 173 #[derive(Debug)] 174 pub enum Time { 175 Monotonic(Instant), 176 RealTime(SystemTime), 177 } 178 179 impl Time { 180 /// How long do we have to wait from now until the specified time? get_wait_time(&self) -> Duration181 fn get_wait_time(&self) -> Duration { 182 match self { 183 Time::Monotonic(instant) => instant.saturating_duration_since(Instant::now()), 184 Time::RealTime(time) => 185 time.duration_since(SystemTime::now()).unwrap_or(Duration::new(0, 0)), 186 } 187 } 188 } 189 190 /// Callbacks are used to implement timeouts. For example, waiting on a 191 /// conditional variable with a timeout creates a callback that is called after 192 /// the specified time and unblocks the thread. If another thread signals on the 193 /// conditional variable, the signal handler deletes the callback. 194 struct TimeoutCallbackInfo<'mir, 'tcx> { 195 /// The callback should be called no earlier than this time. 196 call_time: Time, 197 /// The called function. 198 callback: TimeoutCallback<'mir, 'tcx>, 199 } 200 201 impl<'mir, 'tcx> std::fmt::Debug for TimeoutCallbackInfo<'mir, 'tcx> { fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result202 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 203 write!(f, "TimeoutCallback({:?})", self.call_time) 204 } 205 } 206 207 /// A set of threads. 208 #[derive(Debug)] 209 pub struct ThreadManager<'mir, 'tcx> { 210 /// Identifier of the currently active thread. 211 active_thread: ThreadId, 212 /// Threads used in the program. 213 /// 214 /// Note that this vector also contains terminated threads. 215 threads: IndexVec<ThreadId, Thread<'mir, 'tcx>>, 216 /// This field is pub(crate) because the synchronization primitives 217 /// (`crate::sync`) need a way to access it. 218 pub(crate) sync: SynchronizationState, 219 /// A mapping from a thread-local static to an allocation id of a thread 220 /// specific allocation. 221 thread_local_alloc_ids: RefCell<FxHashMap<(DefId, ThreadId), Pointer<Tag>>>, 222 /// A flag that indicates that we should change the active thread. 223 yield_active_thread: bool, 224 /// Callbacks that are called once the specified time passes. 225 timeout_callbacks: FxHashMap<ThreadId, TimeoutCallbackInfo<'mir, 'tcx>>, 226 } 227 228 impl<'mir, 'tcx> Default for ThreadManager<'mir, 'tcx> { default() -> Self229 fn default() -> Self { 230 let mut threads = IndexVec::new(); 231 // Create the main thread and add it to the list of threads. 232 let mut main_thread = Thread::default(); 233 // The main thread can *not* be joined on. 234 main_thread.join_status = ThreadJoinStatus::Detached; 235 threads.push(main_thread); 236 Self { 237 active_thread: ThreadId::new(0), 238 threads: threads, 239 sync: SynchronizationState::default(), 240 thread_local_alloc_ids: Default::default(), 241 yield_active_thread: false, 242 timeout_callbacks: FxHashMap::default(), 243 } 244 } 245 } 246 247 impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { 248 /// Check if we have an allocation for the given thread local static for the 249 /// active thread. get_thread_local_alloc_id(&self, def_id: DefId) -> Option<Pointer<Tag>>250 fn get_thread_local_alloc_id(&self, def_id: DefId) -> Option<Pointer<Tag>> { 251 self.thread_local_alloc_ids.borrow().get(&(def_id, self.active_thread)).cloned() 252 } 253 254 /// Set the pointer for the allocation of the given thread local 255 /// static for the active thread. 256 /// 257 /// Panics if a thread local is initialized twice for the same thread. set_thread_local_alloc(&self, def_id: DefId, ptr: Pointer<Tag>)258 fn set_thread_local_alloc(&self, def_id: DefId, ptr: Pointer<Tag>) { 259 self.thread_local_alloc_ids 260 .borrow_mut() 261 .try_insert((def_id, self.active_thread), ptr) 262 .unwrap(); 263 } 264 265 /// Borrow the stack of the active thread. active_thread_stack(&self) -> &[Frame<'mir, 'tcx, Tag, FrameData<'tcx>>]266 fn active_thread_stack(&self) -> &[Frame<'mir, 'tcx, Tag, FrameData<'tcx>>] { 267 &self.threads[self.active_thread].stack 268 } 269 270 /// Mutably borrow the stack of the active thread. active_thread_stack_mut(&mut self) -> &mut Vec<Frame<'mir, 'tcx, Tag, FrameData<'tcx>>>271 fn active_thread_stack_mut(&mut self) -> &mut Vec<Frame<'mir, 'tcx, Tag, FrameData<'tcx>>> { 272 &mut self.threads[self.active_thread].stack 273 } 274 275 /// Create a new thread and returns its id. create_thread(&mut self) -> ThreadId276 fn create_thread(&mut self) -> ThreadId { 277 let new_thread_id = ThreadId::new(self.threads.len()); 278 self.threads.push(Default::default()); 279 new_thread_id 280 } 281 282 /// Set an active thread and return the id of the thread that was active before. set_active_thread_id(&mut self, id: ThreadId) -> ThreadId283 fn set_active_thread_id(&mut self, id: ThreadId) -> ThreadId { 284 let active_thread_id = self.active_thread; 285 self.active_thread = id; 286 assert!(self.active_thread.index() < self.threads.len()); 287 active_thread_id 288 } 289 290 /// Get the id of the currently active thread. get_active_thread_id(&self) -> ThreadId291 fn get_active_thread_id(&self) -> ThreadId { 292 self.active_thread 293 } 294 295 /// Get the total number of threads that were ever spawn by this program. get_total_thread_count(&self) -> usize296 fn get_total_thread_count(&self) -> usize { 297 self.threads.len() 298 } 299 300 /// Has the given thread terminated? has_terminated(&self, thread_id: ThreadId) -> bool301 fn has_terminated(&self, thread_id: ThreadId) -> bool { 302 self.threads[thread_id].state == ThreadState::Terminated 303 } 304 305 /// Have all threads terminated? have_all_terminated(&self) -> bool306 fn have_all_terminated(&self) -> bool { 307 self.threads.iter().all(|thread| thread.state == ThreadState::Terminated) 308 } 309 310 /// Enable the thread for execution. The thread must be terminated. enable_thread(&mut self, thread_id: ThreadId)311 fn enable_thread(&mut self, thread_id: ThreadId) { 312 assert!(self.has_terminated(thread_id)); 313 self.threads[thread_id].state = ThreadState::Enabled; 314 } 315 316 /// Get a mutable borrow of the currently active thread. active_thread_mut(&mut self) -> &mut Thread<'mir, 'tcx>317 fn active_thread_mut(&mut self) -> &mut Thread<'mir, 'tcx> { 318 &mut self.threads[self.active_thread] 319 } 320 321 /// Get a shared borrow of the currently active thread. active_thread_ref(&self) -> &Thread<'mir, 'tcx>322 fn active_thread_ref(&self) -> &Thread<'mir, 'tcx> { 323 &self.threads[self.active_thread] 324 } 325 326 /// Mark the thread as detached, which means that no other thread will try 327 /// to join it and the thread is responsible for cleaning up. detach_thread(&mut self, id: ThreadId) -> InterpResult<'tcx>328 fn detach_thread(&mut self, id: ThreadId) -> InterpResult<'tcx> { 329 if self.threads[id].join_status != ThreadJoinStatus::Joinable { 330 throw_ub_format!("trying to detach thread that was already detached or joined"); 331 } 332 self.threads[id].join_status = ThreadJoinStatus::Detached; 333 Ok(()) 334 } 335 336 /// Mark that the active thread tries to join the thread with `joined_thread_id`. join_thread( &mut self, joined_thread_id: ThreadId, data_race: Option<&mut data_race::GlobalState>, ) -> InterpResult<'tcx>337 fn join_thread( 338 &mut self, 339 joined_thread_id: ThreadId, 340 data_race: Option<&mut data_race::GlobalState>, 341 ) -> InterpResult<'tcx> { 342 if self.threads[joined_thread_id].join_status != ThreadJoinStatus::Joinable { 343 throw_ub_format!("trying to join a detached or already joined thread"); 344 } 345 if joined_thread_id == self.active_thread { 346 throw_ub_format!("trying to join itself"); 347 } 348 assert!( 349 self.threads 350 .iter() 351 .all(|thread| thread.state != ThreadState::BlockedOnJoin(joined_thread_id)), 352 "a joinable thread already has threads waiting for its termination" 353 ); 354 // Mark the joined thread as being joined so that we detect if other 355 // threads try to join it. 356 self.threads[joined_thread_id].join_status = ThreadJoinStatus::Joined; 357 if self.threads[joined_thread_id].state != ThreadState::Terminated { 358 // The joined thread is still running, we need to wait for it. 359 self.active_thread_mut().state = ThreadState::BlockedOnJoin(joined_thread_id); 360 trace!( 361 "{:?} blocked on {:?} when trying to join", 362 self.active_thread, 363 joined_thread_id 364 ); 365 } else { 366 // The thread has already terminated - mark join happens-before 367 if let Some(data_race) = data_race { 368 data_race.thread_joined(self.active_thread, joined_thread_id); 369 } 370 } 371 Ok(()) 372 } 373 374 /// Set the name of the active thread. set_thread_name(&mut self, new_thread_name: Vec<u8>)375 fn set_thread_name(&mut self, new_thread_name: Vec<u8>) { 376 self.active_thread_mut().thread_name = Some(new_thread_name); 377 } 378 379 /// Get the name of the active thread. get_thread_name(&self) -> &[u8]380 fn get_thread_name(&self) -> &[u8] { 381 self.active_thread_ref().thread_name() 382 } 383 384 /// Put the thread into the blocked state. block_thread(&mut self, thread: ThreadId)385 fn block_thread(&mut self, thread: ThreadId) { 386 let state = &mut self.threads[thread].state; 387 assert_eq!(*state, ThreadState::Enabled); 388 *state = ThreadState::BlockedOnSync; 389 } 390 391 /// Put the blocked thread into the enabled state. unblock_thread(&mut self, thread: ThreadId)392 fn unblock_thread(&mut self, thread: ThreadId) { 393 let state = &mut self.threads[thread].state; 394 assert_eq!(*state, ThreadState::BlockedOnSync); 395 *state = ThreadState::Enabled; 396 } 397 398 /// Change the active thread to some enabled thread. yield_active_thread(&mut self)399 fn yield_active_thread(&mut self) { 400 // We do not yield immediately, as swapping out the current stack while executing a MIR statement 401 // could lead to all sorts of confusion. 402 // We should only switch stacks between steps. 403 self.yield_active_thread = true; 404 } 405 406 /// Register the given `callback` to be called once the `call_time` passes. 407 /// 408 /// The callback will be called with `thread` being the active thread, and 409 /// the callback may not change the active thread. register_timeout_callback( &mut self, thread: ThreadId, call_time: Time, callback: TimeoutCallback<'mir, 'tcx>, )410 fn register_timeout_callback( 411 &mut self, 412 thread: ThreadId, 413 call_time: Time, 414 callback: TimeoutCallback<'mir, 'tcx>, 415 ) { 416 self.timeout_callbacks 417 .try_insert(thread, TimeoutCallbackInfo { call_time, callback }) 418 .unwrap(); 419 } 420 421 /// Unregister the callback for the `thread`. unregister_timeout_callback_if_exists(&mut self, thread: ThreadId)422 fn unregister_timeout_callback_if_exists(&mut self, thread: ThreadId) { 423 self.timeout_callbacks.remove(&thread); 424 } 425 426 /// Get a callback that is ready to be called. get_ready_callback(&mut self) -> Option<(ThreadId, TimeoutCallback<'mir, 'tcx>)>427 fn get_ready_callback(&mut self) -> Option<(ThreadId, TimeoutCallback<'mir, 'tcx>)> { 428 // We iterate over all threads in the order of their indices because 429 // this allows us to have a deterministic scheduler. 430 for thread in self.threads.indices() { 431 match self.timeout_callbacks.entry(thread) { 432 Entry::Occupied(entry) => 433 if entry.get().call_time.get_wait_time() == Duration::new(0, 0) { 434 return Some((thread, entry.remove().callback)); 435 }, 436 Entry::Vacant(_) => {} 437 } 438 } 439 None 440 } 441 442 /// Wakes up threads joining on the active one and deallocates thread-local statics. 443 /// The `AllocId` that can now be freed are returned. thread_terminated( &mut self, mut data_race: Option<&mut data_race::GlobalState>, ) -> Vec<Pointer<Tag>>444 fn thread_terminated( 445 &mut self, 446 mut data_race: Option<&mut data_race::GlobalState>, 447 ) -> Vec<Pointer<Tag>> { 448 let mut free_tls_statics = Vec::new(); 449 { 450 let mut thread_local_statics = self.thread_local_alloc_ids.borrow_mut(); 451 thread_local_statics.retain(|&(_def_id, thread), &mut alloc_id| { 452 if thread != self.active_thread { 453 // Keep this static around. 454 return true; 455 } 456 // Delete this static from the map and from memory. 457 // We cannot free directly here as we cannot use `?` in this context. 458 free_tls_statics.push(alloc_id); 459 return false; 460 }); 461 } 462 // Set the thread into a terminated state in the data-race detector 463 if let Some(ref mut data_race) = data_race { 464 data_race.thread_terminated(); 465 } 466 // Check if we need to unblock any threads. 467 for (i, thread) in self.threads.iter_enumerated_mut() { 468 if thread.state == ThreadState::BlockedOnJoin(self.active_thread) { 469 // The thread has terminated, mark happens-before edge to joining thread 470 if let Some(ref mut data_race) = data_race { 471 data_race.thread_joined(i, self.active_thread); 472 } 473 trace!("unblocking {:?} because {:?} terminated", i, self.active_thread); 474 thread.state = ThreadState::Enabled; 475 } 476 } 477 return free_tls_statics; 478 } 479 480 /// Decide which action to take next and on which thread. 481 /// 482 /// The currently implemented scheduling policy is the one that is commonly 483 /// used in stateless model checkers such as Loom: run the active thread as 484 /// long as we can and switch only when we have to (the active thread was 485 /// blocked, terminated, or has explicitly asked to be preempted). schedule( &mut self, data_race: &Option<data_race::GlobalState>, ) -> InterpResult<'tcx, SchedulingAction>486 fn schedule( 487 &mut self, 488 data_race: &Option<data_race::GlobalState>, 489 ) -> InterpResult<'tcx, SchedulingAction> { 490 // Check whether the thread has **just** terminated (`check_terminated` 491 // checks whether the thread has popped all its stack and if yes, sets 492 // the thread state to terminated). 493 if self.threads[self.active_thread].check_terminated() { 494 return Ok(SchedulingAction::ExecuteDtors); 495 } 496 // If we get here again and the thread is *still* terminated, there are no more dtors to run. 497 if self.threads[MAIN_THREAD].state == ThreadState::Terminated { 498 // The main thread terminated; stop the program. 499 // We do *not* run TLS dtors of remaining threads, which seems to match rustc behavior. 500 return Ok(SchedulingAction::Stop); 501 } 502 // This thread and the program can keep going. 503 if self.threads[self.active_thread].state == ThreadState::Enabled 504 && !self.yield_active_thread 505 { 506 // The currently active thread is still enabled, just continue with it. 507 return Ok(SchedulingAction::ExecuteStep); 508 } 509 // The active thread yielded. Let's see if there are any timeouts to take care of. We do 510 // this *before* running any other thread, to ensure that timeouts "in the past" fire before 511 // any other thread can take an action. This ensures that for `pthread_cond_timedwait`, "an 512 // error is returned if [...] the absolute time specified by abstime has already been passed 513 // at the time of the call". 514 // <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html> 515 let potential_sleep_time = 516 self.timeout_callbacks.values().map(|info| info.call_time.get_wait_time()).min(); 517 if potential_sleep_time == Some(Duration::new(0, 0)) { 518 return Ok(SchedulingAction::ExecuteTimeoutCallback); 519 } 520 // No callbacks scheduled, pick a regular thread to execute. 521 // We need to pick a new thread for execution. 522 for (id, thread) in self.threads.iter_enumerated() { 523 if thread.state == ThreadState::Enabled { 524 if !self.yield_active_thread || id != self.active_thread { 525 self.active_thread = id; 526 if let Some(data_race) = data_race { 527 data_race.thread_set_active(self.active_thread); 528 } 529 break; 530 } 531 } 532 } 533 self.yield_active_thread = false; 534 if self.threads[self.active_thread].state == ThreadState::Enabled { 535 return Ok(SchedulingAction::ExecuteStep); 536 } 537 // We have not found a thread to execute. 538 if self.threads.iter().all(|thread| thread.state == ThreadState::Terminated) { 539 unreachable!("all threads terminated without the main thread terminating?!"); 540 } else if let Some(sleep_time) = potential_sleep_time { 541 // All threads are currently blocked, but we have unexecuted 542 // timeout_callbacks, which may unblock some of the threads. Hence, 543 // sleep until the first callback. 544 std::thread::sleep(sleep_time); 545 Ok(SchedulingAction::ExecuteTimeoutCallback) 546 } else { 547 throw_machine_stop!(TerminationInfo::Deadlock); 548 } 549 } 550 } 551 552 // Public interface to thread management. 553 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} 554 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { 555 /// Get a thread-specific allocation id for the given thread-local static. 556 /// If needed, allocate a new one. get_or_create_thread_local_alloc( &mut self, def_id: DefId, ) -> InterpResult<'tcx, Pointer<Tag>>557 fn get_or_create_thread_local_alloc( 558 &mut self, 559 def_id: DefId, 560 ) -> InterpResult<'tcx, Pointer<Tag>> { 561 let this = self.eval_context_mut(); 562 let tcx = this.tcx; 563 if let Some(old_alloc) = this.machine.threads.get_thread_local_alloc_id(def_id) { 564 // We already have a thread-specific allocation id for this 565 // thread-local static. 566 Ok(old_alloc) 567 } else { 568 // We need to allocate a thread-specific allocation id for this 569 // thread-local static. 570 // First, we compute the initial value for this static. 571 if tcx.is_foreign_item(def_id) { 572 throw_unsup_format!("foreign thread-local statics are not supported"); 573 } 574 let allocation = tcx.eval_static_initializer(def_id)?; 575 // Create a fresh allocation with this content. 576 let new_alloc = 577 this.memory.allocate_with(allocation.clone(), MiriMemoryKind::Tls.into()); 578 this.machine.threads.set_thread_local_alloc(def_id, new_alloc); 579 Ok(new_alloc) 580 } 581 } 582 583 #[inline] create_thread(&mut self) -> ThreadId584 fn create_thread(&mut self) -> ThreadId { 585 let this = self.eval_context_mut(); 586 let id = this.machine.threads.create_thread(); 587 if let Some(data_race) = &mut this.memory.extra.data_race { 588 data_race.thread_created(id); 589 } 590 id 591 } 592 593 #[inline] detach_thread(&mut self, thread_id: ThreadId) -> InterpResult<'tcx>594 fn detach_thread(&mut self, thread_id: ThreadId) -> InterpResult<'tcx> { 595 let this = self.eval_context_mut(); 596 this.machine.threads.detach_thread(thread_id) 597 } 598 599 #[inline] join_thread(&mut self, joined_thread_id: ThreadId) -> InterpResult<'tcx>600 fn join_thread(&mut self, joined_thread_id: ThreadId) -> InterpResult<'tcx> { 601 let this = self.eval_context_mut(); 602 this.machine.threads.join_thread(joined_thread_id, this.memory.extra.data_race.as_mut())?; 603 Ok(()) 604 } 605 606 #[inline] set_active_thread(&mut self, thread_id: ThreadId) -> ThreadId607 fn set_active_thread(&mut self, thread_id: ThreadId) -> ThreadId { 608 let this = self.eval_context_mut(); 609 if let Some(data_race) = &this.memory.extra.data_race { 610 data_race.thread_set_active(thread_id); 611 } 612 this.machine.threads.set_active_thread_id(thread_id) 613 } 614 615 #[inline] get_active_thread(&self) -> ThreadId616 fn get_active_thread(&self) -> ThreadId { 617 let this = self.eval_context_ref(); 618 this.machine.threads.get_active_thread_id() 619 } 620 621 #[inline] active_thread_mut(&mut self) -> &mut Thread<'mir, 'tcx>622 fn active_thread_mut(&mut self) -> &mut Thread<'mir, 'tcx> { 623 let this = self.eval_context_mut(); 624 this.machine.threads.active_thread_mut() 625 } 626 627 #[inline] active_thread_ref(&self) -> &Thread<'mir, 'tcx>628 fn active_thread_ref(&self) -> &Thread<'mir, 'tcx> { 629 let this = self.eval_context_ref(); 630 this.machine.threads.active_thread_ref() 631 } 632 633 #[inline] get_total_thread_count(&self) -> usize634 fn get_total_thread_count(&self) -> usize { 635 let this = self.eval_context_ref(); 636 this.machine.threads.get_total_thread_count() 637 } 638 639 #[inline] has_terminated(&self, thread_id: ThreadId) -> bool640 fn has_terminated(&self, thread_id: ThreadId) -> bool { 641 let this = self.eval_context_ref(); 642 this.machine.threads.has_terminated(thread_id) 643 } 644 645 #[inline] have_all_terminated(&self) -> bool646 fn have_all_terminated(&self) -> bool { 647 let this = self.eval_context_ref(); 648 this.machine.threads.have_all_terminated() 649 } 650 651 #[inline] enable_thread(&mut self, thread_id: ThreadId)652 fn enable_thread(&mut self, thread_id: ThreadId) { 653 let this = self.eval_context_mut(); 654 this.machine.threads.enable_thread(thread_id); 655 } 656 657 #[inline] active_thread_stack(&self) -> &[Frame<'mir, 'tcx, Tag, FrameData<'tcx>>]658 fn active_thread_stack(&self) -> &[Frame<'mir, 'tcx, Tag, FrameData<'tcx>>] { 659 let this = self.eval_context_ref(); 660 this.machine.threads.active_thread_stack() 661 } 662 663 #[inline] active_thread_stack_mut(&mut self) -> &mut Vec<Frame<'mir, 'tcx, Tag, FrameData<'tcx>>>664 fn active_thread_stack_mut(&mut self) -> &mut Vec<Frame<'mir, 'tcx, Tag, FrameData<'tcx>>> { 665 let this = self.eval_context_mut(); 666 this.machine.threads.active_thread_stack_mut() 667 } 668 669 #[inline] set_active_thread_name(&mut self, new_thread_name: Vec<u8>)670 fn set_active_thread_name(&mut self, new_thread_name: Vec<u8>) { 671 let this = self.eval_context_mut(); 672 if let Some(data_race) = &mut this.memory.extra.data_race { 673 if let Ok(string) = String::from_utf8(new_thread_name.clone()) { 674 data_race.thread_set_name(this.machine.threads.active_thread, string); 675 } 676 } 677 this.machine.threads.set_thread_name(new_thread_name); 678 } 679 680 #[inline] get_active_thread_name<'c>(&'c self) -> &'c [u8] where 'mir: 'c,681 fn get_active_thread_name<'c>(&'c self) -> &'c [u8] 682 where 683 'mir: 'c, 684 { 685 let this = self.eval_context_ref(); 686 this.machine.threads.get_thread_name() 687 } 688 689 #[inline] block_thread(&mut self, thread: ThreadId)690 fn block_thread(&mut self, thread: ThreadId) { 691 let this = self.eval_context_mut(); 692 this.machine.threads.block_thread(thread); 693 } 694 695 #[inline] unblock_thread(&mut self, thread: ThreadId)696 fn unblock_thread(&mut self, thread: ThreadId) { 697 let this = self.eval_context_mut(); 698 this.machine.threads.unblock_thread(thread); 699 } 700 701 #[inline] yield_active_thread(&mut self)702 fn yield_active_thread(&mut self) { 703 let this = self.eval_context_mut(); 704 this.machine.threads.yield_active_thread(); 705 } 706 707 #[inline] register_timeout_callback( &mut self, thread: ThreadId, call_time: Time, callback: TimeoutCallback<'mir, 'tcx>, )708 fn register_timeout_callback( 709 &mut self, 710 thread: ThreadId, 711 call_time: Time, 712 callback: TimeoutCallback<'mir, 'tcx>, 713 ) { 714 let this = self.eval_context_mut(); 715 this.machine.threads.register_timeout_callback(thread, call_time, callback); 716 } 717 718 #[inline] unregister_timeout_callback_if_exists(&mut self, thread: ThreadId)719 fn unregister_timeout_callback_if_exists(&mut self, thread: ThreadId) { 720 let this = self.eval_context_mut(); 721 this.machine.threads.unregister_timeout_callback_if_exists(thread); 722 } 723 724 /// Execute a timeout callback on the callback's thread. 725 #[inline] run_timeout_callback(&mut self) -> InterpResult<'tcx>726 fn run_timeout_callback(&mut self) -> InterpResult<'tcx> { 727 let this = self.eval_context_mut(); 728 let (thread, callback) = 729 if let Some((thread, callback)) = this.machine.threads.get_ready_callback() { 730 (thread, callback) 731 } else { 732 // get_ready_callback can return None if the computer's clock 733 // was shifted after calling the scheduler and before the call 734 // to get_ready_callback (see issue 735 // https://github.com/rust-lang/miri/issues/1763). In this case, 736 // just do nothing, which effectively just returns to the 737 // scheduler. 738 return Ok(()); 739 }; 740 // This back-and-forth with `set_active_thread` is here because of two 741 // design decisions: 742 // 1. Make the caller and not the callback responsible for changing 743 // thread. 744 // 2. Make the scheduler the only place that can change the active 745 // thread. 746 let old_thread = this.set_active_thread(thread); 747 callback(this)?; 748 this.set_active_thread(old_thread); 749 Ok(()) 750 } 751 752 /// Decide which action to take next and on which thread. 753 #[inline] schedule(&mut self) -> InterpResult<'tcx, SchedulingAction>754 fn schedule(&mut self) -> InterpResult<'tcx, SchedulingAction> { 755 let this = self.eval_context_mut(); 756 let data_race = &this.memory.extra.data_race; 757 this.machine.threads.schedule(data_race) 758 } 759 760 /// Handles thread termination of the active thread: wakes up threads joining on this one, 761 /// and deallocated thread-local statics. 762 /// 763 /// This is called from `tls.rs` after handling the TLS dtors. 764 #[inline] thread_terminated(&mut self) -> InterpResult<'tcx>765 fn thread_terminated(&mut self) -> InterpResult<'tcx> { 766 let this = self.eval_context_mut(); 767 for ptr in this.machine.threads.thread_terminated(this.memory.extra.data_race.as_mut()) { 768 this.memory.deallocate(ptr.into(), None, MiriMemoryKind::Tls.into())?; 769 } 770 Ok(()) 771 } 772 } 773