1use std::mem;
4use std::sync::atomic::Ordering::Relaxed;
5use std::task::Poll;
6use std::time::{Duration, SystemTime};
7
8use rand::RngExt;
9use rand::seq::IteratorRandom;
10use rustc_abi::ExternAbi;
11use rustc_const_eval::CTRL_C_RECEIVED;
12use rustc_data_structures::either::Either;
13use rustc_data_structures::fx::FxHashMap;
14use rustc_hir::def_id::DefId;
15use rustc_index::{Idx, IndexVec};
16use rustc_middle::mir::Mutability;
17use rustc_middle::ty::layout::TyAndLayout;
18use rustc_span::{DUMMY_SP, Span};
19use rustc_target::spec::Os;
20
21use crate::concurrency::GlobalDataRaceHandler;
22use crate::shims::tls;
23use crate::*;
24
25#[derive(Clone, Copy, Debug, PartialEq)]
26enum SchedulingAction {
27 ExecuteStep,
29 SleepAndWaitForIo(Option<Duration>),
34}
35
36#[derive(Clone, Copy, Debug, PartialEq)]
38pub enum TlsAllocAction {
39 Deallocate,
41 Leak,
44}
45
46#[derive(Clone, Copy, Debug, PartialEq)]
48pub enum UnblockKind {
49 Ready,
51 TimedOut,
53}
54
55pub type DynUnblockCallback<'tcx> = DynMachineCallback<'tcx, UnblockKind>;
58
59#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
61pub struct ThreadId(u32);
62
63impl ThreadId {
64 pub fn to_u32(self) -> u32 {
65 self.0
66 }
67
68 pub fn new_unchecked(id: u32) -> Self {
70 Self(id)
71 }
72
73 pub const MAIN_THREAD: ThreadId = ThreadId(0);
74}
75
76impl Idx for ThreadId {
77 fn new(idx: usize) -> Self {
78 ThreadId(u32::try_from(idx).unwrap())
79 }
80
81 fn index(self) -> usize {
82 usize::try_from(self.0).unwrap()
83 }
84}
85
86impl From<ThreadId> for u64 {
87 fn from(t: ThreadId) -> Self {
88 t.0.into()
89 }
90}
91
92#[derive(Debug, Copy, Clone, PartialEq, Eq)]
94pub enum BlockReason {
95 Join(ThreadId),
98 Sleep,
100 Mutex,
102 Condvar,
104 RwLock,
106 Futex,
108 InitOnce,
110 Readiness,
112 Eventfd,
114 VirtualSocket,
116 IO,
118 Genmc,
121}
122
123enum ThreadState<'tcx> {
125 Enabled,
127 Blocked { reason: BlockReason, deadline: Option<Deadline>, callback: DynUnblockCallback<'tcx> },
129 Terminated,
132}
133
134impl<'tcx> std::fmt::Debug for ThreadState<'tcx> {
135 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136 match self {
137 Self::Enabled => write!(f, "Enabled"),
138 Self::Blocked { reason, deadline, .. } =>
139 f.debug_struct("Blocked")
140 .field("reason", reason)
141 .field("deadline", deadline)
142 .finish(),
143 Self::Terminated => write!(f, "Terminated"),
144 }
145 }
146}
147
148impl<'tcx> ThreadState<'tcx> {
149 fn is_enabled(&self) -> bool {
150 matches!(self, ThreadState::Enabled)
151 }
152
153 fn is_terminated(&self) -> bool {
154 matches!(self, ThreadState::Terminated)
155 }
156
157 fn is_blocked_on(&self, reason: &BlockReason) -> bool {
158 matches!(self, ThreadState::Blocked { reason: actual_reason, .. } if actual_reason == reason)
159 }
160}
161
162#[derive(Debug, Copy, Clone, PartialEq, Eq)]
164enum ThreadJoinStatus {
165 Joinable,
167 Detached,
170 Joined,
172}
173
174pub struct Thread<'tcx> {
176 state: ThreadState<'tcx>,
177
178 thread_name: Option<Vec<u8>>,
180
181 stack: Vec<Frame<'tcx, Provenance, FrameExtra<'tcx>>>,
183
184 pub(crate) origin_span: Span,
187
188 pub(crate) on_stack_empty: Option<StackEmptyCallback<'tcx>>,
193
194 top_user_relevant_frame: Option<usize>,
199
200 join_status: ThreadJoinStatus,
202
203 pub(crate) unwind_payloads: Vec<ImmTy<'tcx>>,
212
213 pub(crate) last_error: Option<MPlaceTy<'tcx>>,
215}
216
217pub type StackEmptyCallback<'tcx> =
218 Box<dyn FnMut(&mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, Poll<()>> + 'tcx>;
219
220impl<'tcx> Thread<'tcx> {
221 fn thread_name(&self) -> Option<&[u8]> {
223 self.thread_name.as_deref()
224 }
225
226 pub fn is_enabled(&self) -> bool {
228 self.state.is_enabled()
229 }
230
231 fn thread_display_name(&self, id: ThreadId) -> String {
233 if let Some(ref thread_name) = self.thread_name {
234 String::from_utf8_lossy(thread_name).into_owned()
235 } else {
236 format!("unnamed-{}", id.index())
237 }
238 }
239
240 fn compute_top_user_relevant_frame(&self, skip: usize) -> Option<usize> {
246 let mut best = None;
248 for (idx, frame) in self.stack.iter().enumerate().rev().skip(skip) {
249 let relevance = frame.extra.user_relevance;
250 if relevance == u8::MAX {
251 return Some(idx);
253 }
254 if best.is_none_or(|(_best_idx, best_relevance)| best_relevance < relevance) {
255 best = Some((idx, relevance));
258 }
259 }
260 best.map(|(idx, _relevance)| idx)
261 }
262
263 pub fn recompute_top_user_relevant_frame(&mut self, skip: usize) {
266 self.top_user_relevant_frame = self.compute_top_user_relevant_frame(skip);
267 }
268
269 pub fn set_top_user_relevant_frame(&mut self, frame_idx: usize) {
272 debug_assert_eq!(Some(frame_idx), self.compute_top_user_relevant_frame(0));
273 self.top_user_relevant_frame = Some(frame_idx);
274 }
275
276 pub fn top_user_relevant_frame(&self) -> Option<usize> {
279 self.top_user_relevant_frame.or_else(|| self.stack.len().checked_sub(1))
283 }
284
285 pub fn current_user_relevance(&self) -> u8 {
286 self.top_user_relevant_frame()
287 .map(|frame_idx| self.stack[frame_idx].extra.user_relevance)
288 .unwrap_or(0)
289 }
290
291 pub fn current_user_relevant_span(&self) -> Span {
292 debug_assert_eq!(self.top_user_relevant_frame, self.compute_top_user_relevant_frame(0));
293 self.top_user_relevant_frame()
294 .map(|frame_idx| self.stack[frame_idx].current_span())
295 .unwrap_or(rustc_span::DUMMY_SP)
296 }
297}
298
299impl<'tcx> std::fmt::Debug for Thread<'tcx> {
300 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
301 write!(
302 f,
303 "{}({:?}, {:?})",
304 String::from_utf8_lossy(self.thread_name().unwrap_or(b"<unnamed>")),
305 self.state,
306 self.join_status
307 )
308 }
309}
310
311impl<'tcx> Thread<'tcx> {
312 fn new(name: Option<&str>, on_stack_empty: Option<StackEmptyCallback<'tcx>>) -> Self {
313 Self {
314 state: ThreadState::Enabled,
315 thread_name: name.map(|name| Vec::from(name.as_bytes())),
316 stack: Vec::new(),
317 origin_span: DUMMY_SP,
318 top_user_relevant_frame: None,
319 join_status: ThreadJoinStatus::Joinable,
320 unwind_payloads: Vec::new(),
321 last_error: None,
322 on_stack_empty,
323 }
324 }
325}
326
327impl VisitProvenance for Thread<'_> {
328 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
329 let Thread {
330 unwind_payloads: panic_payload,
331 last_error,
332 stack,
333 origin_span: _,
334 top_user_relevant_frame: _,
335 state: _,
336 thread_name: _,
337 join_status: _,
338 on_stack_empty: _, } = self;
340
341 for payload in panic_payload {
342 payload.visit_provenance(visit);
343 }
344 last_error.visit_provenance(visit);
345 for frame in stack {
346 frame.visit_provenance(visit)
347 }
348 }
349}
350
351impl VisitProvenance for Frame<'_, Provenance, FrameExtra<'_>> {
352 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
353 let return_place = self.return_place();
354 let Frame {
355 locals,
356 extra,
357 ..
359 } = self;
360
361 return_place.visit_provenance(visit);
363 for local in locals.iter() {
365 match local.as_mplace_or_imm() {
366 None => {}
367 Some(Either::Left((ptr, meta))) => {
368 ptr.visit_provenance(visit);
369 meta.visit_provenance(visit);
370 }
371 Some(Either::Right(imm)) => {
372 imm.visit_provenance(visit);
373 }
374 }
375 }
376
377 extra.visit_provenance(visit);
378 }
379}
380
381#[derive(Debug, Copy, Clone)]
383pub enum ThreadLookupError {
384 InvalidId,
386 Terminated(ThreadId),
388}
389
390#[derive(Debug)]
392pub struct ThreadManager<'tcx> {
393 active_thread: ThreadId,
395 threads: IndexVec<ThreadId, Thread<'tcx>>,
399 thread_local_allocs: FxHashMap<(DefId, ThreadId), StrictPointer>,
401 yield_active_thread: bool,
404 fixed_scheduling: bool,
406}
407
408impl VisitProvenance for ThreadManager<'_> {
409 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
410 let ThreadManager {
411 threads,
412 thread_local_allocs,
413 active_thread: _,
414 yield_active_thread: _,
415 fixed_scheduling: _,
416 } = self;
417
418 for thread in threads {
419 thread.visit_provenance(visit);
420 }
421 for ptr in thread_local_allocs.values() {
422 ptr.visit_provenance(visit);
423 }
424 }
425}
426
427impl<'tcx> ThreadManager<'tcx> {
428 pub(crate) fn new(config: &MiriConfig) -> Self {
429 let mut threads = IndexVec::new();
430 threads.push(Thread::new(Some("main"), None));
432 Self {
433 active_thread: ThreadId::MAIN_THREAD,
434 threads,
435 thread_local_allocs: Default::default(),
436 yield_active_thread: false,
437 fixed_scheduling: config.fixed_scheduling,
438 }
439 }
440
441 pub(crate) fn init(
442 ecx: &mut MiriInterpCx<'tcx>,
443 on_main_stack_empty: StackEmptyCallback<'tcx>,
444 ) {
445 ecx.machine.threads.threads[ThreadId::MAIN_THREAD].on_stack_empty =
446 Some(on_main_stack_empty);
447 if ecx.tcx.sess.target.os != Os::Windows {
448 ecx.machine.threads.threads[ThreadId::MAIN_THREAD].join_status =
450 ThreadJoinStatus::Detached;
451 }
452 }
453
454 pub fn thread_id_try_from(&self, id: impl TryInto<u32>) -> Result<ThreadId, ThreadLookupError> {
458 if let Ok(id) = id.try_into()
459 && usize::try_from(id).is_ok_and(|id| id < self.threads.len())
460 {
461 let thread_id = ThreadId(id);
462 if self.threads[thread_id].state.is_terminated() {
463 Err(ThreadLookupError::Terminated(thread_id))
464 } else {
465 Ok(thread_id)
466 }
467 } else {
468 Err(ThreadLookupError::InvalidId)
469 }
470 }
471
472 fn get_thread_local_alloc_id(&self, def_id: DefId) -> Option<StrictPointer> {
475 self.thread_local_allocs.get(&(def_id, self.active_thread)).cloned()
476 }
477
478 fn set_thread_local_alloc(&mut self, def_id: DefId, ptr: StrictPointer) {
483 self.thread_local_allocs.try_insert((def_id, self.active_thread), ptr).unwrap();
484 }
485
486 pub fn active_thread_stack(&self) -> &[Frame<'tcx, Provenance, FrameExtra<'tcx>>] {
488 &self.threads[self.active_thread].stack
489 }
490
491 pub fn active_thread_stack_mut(
493 &mut self,
494 ) -> &mut Vec<Frame<'tcx, Provenance, FrameExtra<'tcx>>> {
495 &mut self.threads[self.active_thread].stack
496 }
497
498 pub fn all_blocked_stacks(
499 &self,
500 ) -> impl Iterator<Item = (ThreadId, &[Frame<'tcx, Provenance, FrameExtra<'tcx>>])> {
501 self.threads
502 .iter_enumerated()
503 .filter(|(_id, t)| matches!(t.state, ThreadState::Blocked { .. }))
504 .map(|(id, t)| (id, &t.stack[..]))
505 }
506
507 fn create_thread(&mut self, on_stack_empty: StackEmptyCallback<'tcx>) -> ThreadId {
509 let new_thread_id = ThreadId::new(self.threads.len());
510 self.threads.push(Thread::new(None, Some(on_stack_empty)));
511 new_thread_id
512 }
513
514 fn set_active_thread_id(&mut self, id: ThreadId) -> ThreadId {
516 assert!(id.index() < self.threads.len());
517 info!(
518 "---------- Now executing on thread `{}` (previous: `{}`) ----------------------------------------",
519 self.get_thread_display_name(id),
520 self.get_thread_display_name(self.active_thread)
521 );
522 std::mem::replace(&mut self.active_thread, id)
523 }
524
525 pub fn active_thread(&self) -> ThreadId {
527 self.active_thread
528 }
529
530 pub fn get_total_thread_count(&self) -> usize {
532 self.threads.len()
533 }
534
535 pub fn get_live_thread_count(&self) -> usize {
538 self.threads.iter().filter(|t| !t.state.is_terminated()).count()
539 }
540
541 fn has_terminated(&self, thread_id: ThreadId) -> bool {
543 self.threads[thread_id].state.is_terminated()
544 }
545
546 fn have_all_terminated(&self) -> bool {
548 self.threads.iter().all(|thread| thread.state.is_terminated())
549 }
550
551 fn enable_thread(&mut self, thread_id: ThreadId) {
553 assert!(self.has_terminated(thread_id));
554 self.threads[thread_id].state = ThreadState::Enabled;
555 }
556
557 pub fn active_thread_mut(&mut self) -> &mut Thread<'tcx> {
559 &mut self.threads[self.active_thread]
560 }
561
562 pub fn active_thread_ref(&self) -> &Thread<'tcx> {
564 &self.threads[self.active_thread]
565 }
566
567 pub fn thread_ref(&self, thread_id: ThreadId) -> &Thread<'tcx> {
568 &self.threads[thread_id]
569 }
570
571 fn detach_thread(&mut self, id: ThreadId, allow_terminated_joined: bool) -> InterpResult<'tcx> {
580 trace!("detaching {:?}", id);
582
583 let is_ub = if allow_terminated_joined && self.threads[id].state.is_terminated() {
584 self.threads[id].join_status == ThreadJoinStatus::Detached
586 } else {
587 self.threads[id].join_status != ThreadJoinStatus::Joinable
588 };
589 if is_ub {
590 throw_ub_format!("trying to detach thread that was already detached or joined");
591 }
592
593 self.threads[id].join_status = ThreadJoinStatus::Detached;
594 interp_ok(())
595 }
596
597 pub fn set_thread_name(&mut self, thread: ThreadId, new_thread_name: Vec<u8>) {
599 self.threads[thread].thread_name = Some(new_thread_name);
600 }
601
602 pub fn get_thread_name(&self, thread: ThreadId) -> Option<&[u8]> {
604 self.threads[thread].thread_name()
605 }
606
607 pub fn get_thread_display_name(&self, thread: ThreadId) -> String {
608 self.threads[thread].thread_display_name(thread)
609 }
610
611 fn block_thread(
613 &mut self,
614 reason: BlockReason,
615 deadline: Option<Deadline>,
616 callback: DynUnblockCallback<'tcx>,
617 ) {
618 let state = &mut self.threads[self.active_thread].state;
619 assert!(state.is_enabled());
620 *state = ThreadState::Blocked { reason, deadline, callback }
621 }
622
623 fn yield_active_thread(&mut self) {
625 self.yield_active_thread = true;
629 }
630}
631
632impl<'tcx> EvalContextPrivExt<'tcx> for MiriInterpCx<'tcx> {}
633trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
634 #[inline]
635 fn run_on_stack_empty(&mut self) -> InterpResult<'tcx, Poll<()>> {
636 let this = self.eval_context_mut();
637 let active_thread = this.active_thread_mut();
638 active_thread.origin_span = DUMMY_SP; let mut callback = active_thread
640 .on_stack_empty
641 .take()
642 .expect("`on_stack_empty` not set up, or already running");
643 let res = callback(this)?;
644 this.active_thread_mut().on_stack_empty = Some(callback);
645 interp_ok(res)
646 }
647
648 fn schedule(&mut self) -> InterpResult<'tcx, SchedulingAction> {
657 let this = self.eval_context_mut();
658
659 if this.machine.data_race.as_genmc_ref().is_some() {
661 loop {
662 let genmc_ctx = this.machine.data_race.as_genmc_ref().unwrap();
663 let Some(next_thread_id) = genmc_ctx.schedule_thread(this)? else {
664 return interp_ok(SchedulingAction::ExecuteStep);
665 };
666 if this.machine.threads.threads[next_thread_id]
668 .state
669 .is_blocked_on(&BlockReason::Genmc)
670 {
671 info!(
672 "GenMC: scheduling blocked thread {next_thread_id:?}, so we unblock it now."
673 );
674 this.unblock_thread(next_thread_id, BlockReason::Genmc)?;
675 }
676 let thread_manager = &mut this.machine.threads;
679 if thread_manager.threads[next_thread_id].state.is_enabled() {
680 thread_manager.active_thread = next_thread_id;
682 return interp_ok(SchedulingAction::ExecuteStep);
683 }
684 }
685 }
686
687 let thread_manager = &this.machine.threads;
689 if thread_manager.threads[thread_manager.active_thread].state.is_enabled()
691 && !thread_manager.yield_active_thread
692 {
693 return interp_ok(SchedulingAction::ExecuteStep);
695 }
696
697 if this.machine.communicate() {
701 this.poll_and_unblock(Some(Duration::ZERO))?;
711 }
712
713 let potential_sleep_time = this.unblock_expired_deadlines()?;
719
720 let thread_manager = &mut this.machine.threads;
721 let rng = this.machine.rng.get_mut();
722
723 let mut threads_iter = thread_manager
730 .threads
731 .iter_enumerated()
732 .skip(thread_manager.active_thread.index() + 1)
733 .chain(
734 thread_manager
735 .threads
736 .iter_enumerated()
737 .take(thread_manager.active_thread.index() + 1),
738 )
739 .filter(|(_id, thread)| thread.state.is_enabled());
740 let new_thread = if thread_manager.fixed_scheduling {
742 let next = threads_iter.next();
743 drop(threads_iter);
744 next
745 } else {
746 threads_iter.choose(rng)
747 };
748
749 if let Some((id, _thread)) = new_thread {
750 if thread_manager.active_thread != id {
751 info!(
752 "---------- Now executing on thread `{}` (previous: `{}`) ----------------------------------------",
753 thread_manager.get_thread_display_name(id),
754 thread_manager.get_thread_display_name(thread_manager.active_thread)
755 );
756 thread_manager.active_thread = id;
757 }
758 }
759 thread_manager.yield_active_thread = false;
761
762 if thread_manager.threads[thread_manager.active_thread].state.is_enabled() {
763 return interp_ok(SchedulingAction::ExecuteStep);
764 }
765
766 let threads = &this.machine.threads.threads;
768
769 if threads.iter().all(|thread| thread.state.is_terminated()) {
770 unreachable!("all threads terminated without the main thread terminating?!");
771 } else if let Some(sleep_time) = potential_sleep_time {
772 interp_ok(SchedulingAction::SleepAndWaitForIo(Some(sleep_time)))
776 } else if this.any_thread_blocked_on_host() {
777 interp_ok(SchedulingAction::SleepAndWaitForIo(None))
781 } else {
782 throw_machine_stop!(TerminationInfo::GlobalDeadlock);
783 }
784 }
785
786 fn unblock_expired_deadlines(&mut self) -> InterpResult<'tcx, Option<Duration>> {
791 let this = self.eval_context_mut();
792 let communicate = this.machine.communicate();
793
794 let mut min_wait_time = Option::<Duration>::None;
795 let mut callbacks = Vec::new();
796
797 for (id, thread) in this.machine.threads.threads.iter_enumerated_mut() {
798 match &thread.state {
799 ThreadState::Blocked { deadline: Some(deadline), .. } => {
800 let wait_time = match deadline {
801 Deadline::Monotonic(instant) =>
802 instant.duration_since(this.machine.monotonic_clock.now()),
803 Deadline::RealTime(time) => {
804 assert!(communicate, "cannot have `RealTime` timeout with isolation");
805 time.duration_since(SystemTime::now()).unwrap_or(Duration::ZERO)
806 }
807 };
808
809 if wait_time.is_zero() {
810 let old_state = mem::replace(&mut thread.state, ThreadState::Enabled);
812 let ThreadState::Blocked { callback, .. } = old_state else {
813 unreachable!()
814 };
815 callbacks.push((id, callback));
817 } else {
818 min_wait_time = Some(wait_time.min(min_wait_time.unwrap_or(Duration::MAX)));
821 }
822 }
823 _ => {}
824 }
825 }
826
827 for (thread, callback) in callbacks {
828 let old_thread = this.machine.threads.set_active_thread_id(thread);
835 callback.call(this, UnblockKind::TimedOut)?;
836 this.machine.threads.set_active_thread_id(old_thread);
837 }
838
839 interp_ok(min_wait_time)
840 }
841}
842
843impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
845pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
846 fn miri_step(&mut self) -> InterpResult<'tcx> {
848 let this = self.eval_context_mut();
849
850 if !this.step()? {
851 match this.run_on_stack_empty()? {
853 Poll::Pending => {} Poll::Ready(()) => {
855 this.terminate_active_thread(TlsAllocAction::Deallocate)?;
856 }
857 }
858 }
859
860 interp_ok(())
861 }
862
863 #[inline]
864 fn thread_id_try_from(&self, id: impl TryInto<u32>) -> Result<ThreadId, ThreadLookupError> {
865 self.eval_context_ref().machine.threads.thread_id_try_from(id)
866 }
867
868 fn get_or_create_thread_local_alloc(
871 &mut self,
872 def_id: DefId,
873 ) -> InterpResult<'tcx, StrictPointer> {
874 let this = self.eval_context_mut();
875 let tcx = this.tcx;
876 if let Some(old_alloc) = this.machine.threads.get_thread_local_alloc_id(def_id) {
877 interp_ok(old_alloc)
880 } else {
881 if tcx.is_foreign_item(def_id) {
885 throw_unsup_format!("foreign thread-local statics are not supported");
886 }
887 let params = this.machine.get_default_alloc_params();
888 let alloc = this.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?;
889 let mut alloc = alloc.inner().adjust_from_tcx(
891 &this.tcx,
892 |bytes, align| {
893 interp_ok(MiriAllocBytes::from_bytes(
894 std::borrow::Cow::Borrowed(bytes),
895 align,
896 params,
897 ))
898 },
899 |ptr| this.global_root_pointer(ptr),
900 )?;
901 alloc.mutability = Mutability::Mut;
903 let ptr = this.insert_allocation(alloc, MiriMemoryKind::Tls.into())?;
905 this.machine.threads.set_thread_local_alloc(def_id, ptr);
906 interp_ok(ptr)
907 }
908 }
909
910 #[inline]
912 fn start_regular_thread(
913 &mut self,
914 thread: Option<MPlaceTy<'tcx>>,
915 start_routine: Pointer,
916 start_abi: ExternAbi,
917 func_arg: ImmTy<'tcx>,
918 ret_layout: TyAndLayout<'tcx>,
919 ) -> InterpResult<'tcx, ThreadId> {
920 let this = self.eval_context_mut();
921
922 let current_span = this.machine.current_user_relevant_span();
924 let new_thread_id = this.machine.threads.create_thread({
925 let mut state = tls::TlsDtorsState::default();
926 Box::new(move |m| state.on_stack_empty(m))
927 });
928 match &mut this.machine.data_race {
929 GlobalDataRaceHandler::None => {}
930 GlobalDataRaceHandler::Vclocks(data_race) =>
931 data_race.thread_created(&this.machine.threads, new_thread_id, current_span),
932 GlobalDataRaceHandler::Genmc(genmc_ctx) =>
933 genmc_ctx.handle_thread_create(
934 &this.machine.threads,
935 start_routine,
936 &func_arg,
937 new_thread_id,
938 )?,
939 }
940 if let Some(thread_info_place) = thread {
943 this.write_scalar(
944 Scalar::from_uint(new_thread_id.to_u32(), thread_info_place.layout.size),
945 &thread_info_place,
946 )?;
947 }
948
949 let old_thread_id = this.machine.threads.set_active_thread_id(new_thread_id);
952
953 if let Some(thread_cpu_affinity) = &mut this.machine.thread_cpu_affinity
956 && let Some(cpuset) = thread_cpu_affinity.get(&old_thread_id).cloned()
957 {
958 thread_cpu_affinity.insert(new_thread_id, cpuset);
959 }
960
961 let instance = this.get_ptr_fn(start_routine)?.as_instance()?;
963
964 let ret_place = this.allocate(ret_layout, MiriMemoryKind::Machine.into())?;
968
969 this.call_thread_root_function(
970 instance,
971 start_abi,
972 &[func_arg],
973 Some(&ret_place),
974 current_span,
975 )?;
976
977 this.machine.threads.set_active_thread_id(old_thread_id);
979
980 interp_ok(new_thread_id)
981 }
982
983 fn terminate_active_thread(&mut self, tls_alloc_action: TlsAllocAction) -> InterpResult<'tcx> {
988 let this = self.eval_context_mut();
989
990 let thread = this.active_thread_mut();
992 assert!(thread.stack.is_empty(), "only threads with an empty stack can be terminated");
993 thread.state = ThreadState::Terminated;
994
995 let gone_thread = this.active_thread();
997 {
998 let mut free_tls_statics = Vec::new();
999 this.machine.threads.thread_local_allocs.retain(|&(_def_id, thread), &mut alloc_id| {
1000 if thread != gone_thread {
1001 return true;
1003 }
1004 free_tls_statics.push(alloc_id);
1007 false
1008 });
1009 for ptr in free_tls_statics {
1011 match tls_alloc_action {
1012 TlsAllocAction::Deallocate =>
1013 this.deallocate_ptr(ptr.into(), None, MiriMemoryKind::Tls.into())?,
1014 TlsAllocAction::Leak =>
1015 if let Some(alloc) = ptr.provenance.get_alloc_id() {
1016 trace!(
1017 "Thread-local static leaked and stored as static root: {:?}",
1018 alloc
1019 );
1020 this.machine.static_roots.push(alloc);
1021 },
1022 }
1023 }
1024 }
1025
1026 match &mut this.machine.data_race {
1027 GlobalDataRaceHandler::None => {}
1028 GlobalDataRaceHandler::Vclocks(data_race) =>
1029 data_race.thread_terminated(&this.machine.threads),
1030 GlobalDataRaceHandler::Genmc(genmc_ctx) => {
1031 genmc_ctx.handle_thread_finish(&this.machine.threads)
1034 }
1035 }
1036
1037 let unblock_reason = BlockReason::Join(gone_thread);
1039 let threads = &this.machine.threads.threads;
1040 let joining_threads = threads
1041 .iter_enumerated()
1042 .filter(|(_, thread)| thread.state.is_blocked_on(&unblock_reason))
1043 .map(|(id, _)| id)
1044 .collect::<Vec<_>>();
1045 for thread in joining_threads {
1046 this.unblock_thread(thread, unblock_reason)?;
1047 }
1048
1049 interp_ok(())
1050 }
1051
1052 #[inline]
1055 fn block_thread(
1056 &mut self,
1057 reason: BlockReason,
1058 deadline: Option<Deadline>,
1059 callback: DynUnblockCallback<'tcx>,
1060 ) {
1061 let this = self.eval_context_mut();
1062 if deadline.is_some() && this.machine.data_race.as_genmc_ref().is_some() {
1063 panic!("Unimplemented: Timeouts not yet supported in GenMC mode.");
1064 }
1065 if matches!(deadline, Some(Deadline::RealTime(_))) && !this.machine.communicate() {
1066 panic!("cannot have `RealTime` timeout with isolation");
1067 }
1068 this.machine.threads.block_thread(reason, deadline, callback);
1069 }
1070
1071 fn unblock_thread(&mut self, thread: ThreadId, reason: BlockReason) -> InterpResult<'tcx> {
1074 let this = self.eval_context_mut();
1075 let old_state =
1076 mem::replace(&mut this.machine.threads.threads[thread].state, ThreadState::Enabled);
1077 let callback = match old_state {
1078 ThreadState::Blocked { reason: actual_reason, callback, .. } => {
1079 assert_eq!(
1080 reason, actual_reason,
1081 "unblock_thread: thread was blocked for the wrong reason"
1082 );
1083 callback
1084 }
1085 _ => panic!("unblock_thread: thread was not blocked"),
1086 };
1087 let old_thread = this.machine.threads.set_active_thread_id(thread);
1089 callback.call(this, UnblockKind::Ready)?;
1090 this.machine.threads.set_active_thread_id(old_thread);
1091 interp_ok(())
1092 }
1093
1094 #[inline]
1095 fn detach_thread(
1096 &mut self,
1097 thread_id: ThreadId,
1098 allow_terminated_joined: bool,
1099 ) -> InterpResult<'tcx> {
1100 let this = self.eval_context_mut();
1101 this.machine.threads.detach_thread(thread_id, allow_terminated_joined)
1102 }
1103
1104 fn join_thread(
1108 &mut self,
1109 joined_thread_id: ThreadId,
1110 success_retval: Scalar,
1111 return_dest: &MPlaceTy<'tcx>,
1112 ) -> InterpResult<'tcx> {
1113 let this = self.eval_context_mut();
1114 let thread_mgr = &mut this.machine.threads;
1115 if thread_mgr.threads[joined_thread_id].join_status == ThreadJoinStatus::Detached {
1116 throw_ub_format!("trying to join a detached thread");
1118 }
1119
1120 fn after_join<'tcx>(
1121 this: &mut InterpCx<'tcx, MiriMachine<'tcx>>,
1122 joined_thread_id: ThreadId,
1123 success_retval: Scalar,
1124 return_dest: &MPlaceTy<'tcx>,
1125 ) -> InterpResult<'tcx> {
1126 let threads = &this.machine.threads;
1127 match &mut this.machine.data_race {
1128 GlobalDataRaceHandler::None => {}
1129 GlobalDataRaceHandler::Vclocks(data_race) =>
1130 data_race.thread_joined(threads, joined_thread_id),
1131 GlobalDataRaceHandler::Genmc(genmc_ctx) =>
1132 genmc_ctx.handle_thread_join(threads.active_thread, joined_thread_id)?,
1133 }
1134 this.write_scalar(success_retval, return_dest)?;
1135 interp_ok(())
1136 }
1137
1138 thread_mgr.threads[joined_thread_id].join_status = ThreadJoinStatus::Joined;
1141 if !thread_mgr.threads[joined_thread_id].state.is_terminated() {
1142 trace!(
1143 "{:?} blocked on {:?} when trying to join",
1144 thread_mgr.active_thread, joined_thread_id
1145 );
1146 if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
1147 genmc_ctx.handle_thread_join(thread_mgr.active_thread, joined_thread_id)?;
1148 }
1149
1150 let dest = return_dest.clone();
1153 thread_mgr.block_thread(
1154 BlockReason::Join(joined_thread_id),
1155 None,
1156 callback!(
1157 @capture<'tcx> {
1158 joined_thread_id: ThreadId,
1159 dest: MPlaceTy<'tcx>,
1160 success_retval: Scalar,
1161 }
1162 |this, unblock: UnblockKind| {
1163 assert_eq!(unblock, UnblockKind::Ready);
1164 after_join(this, joined_thread_id, success_retval, &dest)
1165 }
1166 ),
1167 );
1168 } else {
1169 after_join(this, joined_thread_id, success_retval, return_dest)?;
1171 }
1172 interp_ok(())
1173 }
1174
1175 fn join_thread_exclusive(
1180 &mut self,
1181 joined_thread_id: ThreadId,
1182 success_retval: Scalar,
1183 return_dest: &MPlaceTy<'tcx>,
1184 ) -> InterpResult<'tcx> {
1185 let this = self.eval_context_mut();
1186 let threads = &this.machine.threads.threads;
1187 if threads[joined_thread_id].join_status == ThreadJoinStatus::Joined {
1188 throw_ub_format!("trying to join an already joined thread");
1189 }
1190
1191 if joined_thread_id == this.machine.threads.active_thread {
1192 throw_ub_format!("trying to join itself");
1193 }
1194
1195 assert!(
1197 threads.iter().all(|thread| {
1198 !thread.state.is_blocked_on(&BlockReason::Join(joined_thread_id))
1199 }),
1200 "this thread already has threads waiting for its termination"
1201 );
1202
1203 this.join_thread(joined_thread_id, success_retval, return_dest)
1204 }
1205
1206 #[inline]
1207 fn active_thread(&self) -> ThreadId {
1208 let this = self.eval_context_ref();
1209 this.machine.threads.active_thread()
1210 }
1211
1212 #[inline]
1213 fn active_thread_mut(&mut self) -> &mut Thread<'tcx> {
1214 let this = self.eval_context_mut();
1215 this.machine.threads.active_thread_mut()
1216 }
1217
1218 #[inline]
1219 fn active_thread_ref(&self) -> &Thread<'tcx> {
1220 let this = self.eval_context_ref();
1221 this.machine.threads.active_thread_ref()
1222 }
1223
1224 #[inline]
1225 fn get_total_thread_count(&self) -> usize {
1226 let this = self.eval_context_ref();
1227 this.machine.threads.get_total_thread_count()
1228 }
1229
1230 #[inline]
1231 fn have_all_terminated(&self) -> bool {
1232 let this = self.eval_context_ref();
1233 this.machine.threads.have_all_terminated()
1234 }
1235
1236 #[inline]
1237 fn enable_thread(&mut self, thread_id: ThreadId) {
1238 let this = self.eval_context_mut();
1239 this.machine.threads.enable_thread(thread_id);
1240 }
1241
1242 #[inline]
1243 fn active_thread_stack<'a>(&'a self) -> &'a [Frame<'tcx, Provenance, FrameExtra<'tcx>>] {
1244 let this = self.eval_context_ref();
1245 this.machine.threads.active_thread_stack()
1246 }
1247
1248 #[inline]
1249 fn active_thread_stack_mut<'a>(
1250 &'a mut self,
1251 ) -> &'a mut Vec<Frame<'tcx, Provenance, FrameExtra<'tcx>>> {
1252 let this = self.eval_context_mut();
1253 this.machine.threads.active_thread_stack_mut()
1254 }
1255
1256 #[inline]
1258 fn set_thread_name(&mut self, thread: ThreadId, new_thread_name: Vec<u8>) {
1259 self.eval_context_mut().machine.threads.set_thread_name(thread, new_thread_name);
1260 }
1261
1262 #[inline]
1263 fn get_thread_name<'c>(&'c self, thread: ThreadId) -> Option<&'c [u8]>
1264 where
1265 'tcx: 'c,
1266 {
1267 self.eval_context_ref().machine.threads.get_thread_name(thread)
1268 }
1269
1270 #[inline]
1271 fn yield_active_thread(&mut self) {
1272 self.eval_context_mut().machine.threads.yield_active_thread();
1273 }
1274
1275 #[inline]
1276 fn maybe_preempt_active_thread(&mut self) {
1277 let this = self.eval_context_mut();
1278 if !this.machine.threads.fixed_scheduling
1279 && this.machine.rng.get_mut().random_bool(this.machine.preemption_rate)
1280 {
1281 this.yield_active_thread();
1282 }
1283 }
1284
1285 fn run_threads(&mut self) -> InterpResult<'tcx, !> {
1288 let this = self.eval_context_mut();
1289 loop {
1290 if CTRL_C_RECEIVED.load(Relaxed) {
1291 this.machine.handle_abnormal_termination();
1292 throw_machine_stop!(TerminationInfo::Interrupted);
1293 }
1294 match this.schedule()? {
1295 SchedulingAction::ExecuteStep => {
1296 this.miri_step()?;
1297 }
1298 SchedulingAction::SleepAndWaitForIo(duration) => {
1299 if this.machine.communicate() {
1300 this.poll_and_unblock(duration)?;
1305 } else {
1306 let duration = duration.expect(
1307 "Infinite sleep should not be triggered when isolation is enabled",
1308 );
1309 this.machine.monotonic_clock.sleep(duration);
1310 }
1311 }
1312 }
1313 }
1314 }
1315}