Skip to main content

miri/concurrency/
thread.rs

1//! Implements threads.
2
3use std::mem;
4use std::task::Poll;
5use std::time::{Duration, SystemTime};
6
7use rand::RngExt;
8use rustc_abi::ExternAbi;
9use rustc_data_structures::either::Either;
10use rustc_data_structures::fx::FxHashMap;
11use rustc_hir::def_id::DefId;
12use rustc_index::{Idx, IndexVec};
13use rustc_middle::mir::Mutability;
14use rustc_middle::ty::layout::TyAndLayout;
15use rustc_span::{DUMMY_SP, Span};
16use rustc_target::spec::Os;
17
18use crate::concurrency::GlobalDataRaceHandler;
19use crate::shims::tls;
20use crate::*;
21
22/// The argument type for the "unblock" callback, indicating why the thread got unblocked.
23#[derive(Clone, Copy, Debug, PartialEq)]
24pub enum UnblockKind {
25    /// Operation completed successfully, thread continues normal execution.
26    Ready,
27    /// The operation did not complete within its specified duration.
28    TimedOut,
29}
30
31/// Type alias for unblock callbacks, i.e. machine callbacks invoked when
32/// a thread gets unblocked.
33pub type DynUnblockCallback<'tcx> = DynMachineCallback<'tcx, UnblockKind>;
34
35/// A thread identifier.
36#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
37pub struct ThreadId(u32);
38
39impl ThreadId {
40    pub fn to_u32(self) -> u32 {
41        self.0
42    }
43
44    /// Create a new thread id from a `u32` without checking if this thread exists.
45    pub fn new_unchecked(id: u32) -> Self {
46        Self(id)
47    }
48
49    pub const MAIN_THREAD: ThreadId = ThreadId(0);
50}
51
52impl Idx for ThreadId {
53    fn new(idx: usize) -> Self {
54        ThreadId(u32::try_from(idx).unwrap())
55    }
56
57    fn index(self) -> usize {
58        usize::try_from(self.0).unwrap()
59    }
60}
61
62impl From<ThreadId> for u64 {
63    fn from(t: ThreadId) -> Self {
64        t.0.into()
65    }
66}
67
68/// Keeps track of what the thread is blocked on.
69#[derive(Debug, Copy, Clone, PartialEq, Eq)]
70pub enum BlockReason {
71    /// The thread tried to join the specified thread and is blocked until that
72    /// thread terminates.
73    Join(ThreadId),
74    /// Waiting for time to pass.
75    Sleep,
76    /// Blocked on a mutex.
77    Mutex,
78    /// Blocked on a condition variable.
79    Condvar,
80    /// Blocked on a reader-writer lock.
81    RwLock,
82    /// Blocked on a semaphore.
83    Semaphore,
84    /// Blocked on a Futex variable.
85    Futex,
86    /// Blocked on an InitOnce.
87    InitOnce,
88    /// Blocked until a file description readiness is satisfied (e.g. epoll).
89    Readiness,
90    /// Blocked on eventfd.
91    Eventfd,
92    /// Blocked on virtual socket.
93    VirtualSocket,
94    /// Blocked on an IO operation.
95    IO,
96    /// Thread is paused indefinitely.
97    Pause,
98    /// Blocked for any reason related to GenMC, such as `assume` statements (GenMC mode only).
99    /// Will be implicitly unblocked when GenMC schedules this thread again.
100    Genmc,
101}
102
103/// The state of a thread.
104enum ThreadState<'tcx> {
105    /// The thread is enabled and can be executed.
106    Enabled,
107    /// The thread is blocked on something.
108    Blocked { reason: BlockReason, deadline: Option<Deadline>, callback: DynUnblockCallback<'tcx> },
109    /// The thread has terminated its execution. We do not delete terminated
110    /// threads (FIXME: why?).
111    Terminated,
112}
113
114impl<'tcx> std::fmt::Debug for ThreadState<'tcx> {
115    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116        match self {
117            Self::Enabled => write!(f, "Enabled"),
118            Self::Blocked { reason, deadline, .. } =>
119                f.debug_struct("Blocked")
120                    .field("reason", reason)
121                    .field("deadline", deadline)
122                    .finish(),
123            Self::Terminated => write!(f, "Terminated"),
124        }
125    }
126}
127
128impl<'tcx> ThreadState<'tcx> {
129    fn is_enabled(&self) -> bool {
130        matches!(self, ThreadState::Enabled)
131    }
132
133    fn is_terminated(&self) -> bool {
134        matches!(self, ThreadState::Terminated)
135    }
136
137    fn is_blocked_on(&self, reason: BlockReason) -> bool {
138        matches!(self, ThreadState::Blocked { reason: actual_reason, .. } if *actual_reason == reason)
139    }
140}
141
142/// The join status of a thread.
143#[derive(Debug, Copy, Clone, PartialEq, Eq)]
144enum ThreadJoinStatus {
145    /// The thread can be joined.
146    Joinable,
147    /// A thread is detached if its join handle was destroyed and no other
148    /// thread can join it.
149    Detached,
150    /// The thread was already joined by some thread and cannot be joined again.
151    Joined,
152}
153
154/// A thread.
155pub struct Thread<'tcx> {
156    state: ThreadState<'tcx>,
157
158    /// Name of the thread.
159    thread_name: Option<Vec<u8>>,
160
161    /// The virtual call stack.
162    stack: Vec<Frame<'tcx, Provenance, FrameExtra<'tcx>>>,
163
164    /// A span that explains where the thread (or more specifically, its current root
165    /// frame) "comes from".
166    pub(crate) origin_span: Span,
167
168    /// The function to call when the stack ran empty, to figure out what to do next.
169    /// Conceptually, this is the interpreter implementation of the things that happen 'after' the
170    /// Rust language entry point for this thread returns (usually implemented by the C or OS runtime).
171    /// (`None` is an error, it means the callback has not been set up yet or is actively running.)
172    pub(crate) on_stack_empty: Option<StackEmptyCallback<'tcx>>,
173
174    /// The index of the topmost user-relevant frame in `stack`. This field must contain
175    /// the value produced by `get_top_user_relevant_frame`.
176    /// This field is a cache to reduce how often we call that method. The cache is manually
177    /// maintained inside `MiriMachine::after_stack_push` and `MiriMachine::after_stack_pop`.
178    top_user_relevant_frame: Option<usize>,
179
180    /// The join status.
181    join_status: ThreadJoinStatus,
182
183    /// Stack of active unwind payloads for the current thread. Used for storing
184    /// the argument of the call to `miri_start_unwind` (the payload) when unwinding.
185    /// This is pointer-sized, and matches the `Payload` type in `src/libpanic_unwind/miri.rs`.
186    ///
187    /// In real unwinding, the payload gets passed as an argument to the landing pad,
188    /// which then forwards it to 'Resume'. However this argument is implicit in MIR,
189    /// so we have to store it out-of-band. When there are multiple active unwinds,
190    /// the innermost one is always caught first, so we can store them as a stack.
191    pub(crate) unwind_payloads: Vec<ImmTy<'tcx>>,
192
193    /// Last OS error location in memory. It is a 32-bit integer.
194    pub(crate) last_error: Option<MPlaceTy<'tcx>>,
195}
196
197pub type StackEmptyCallback<'tcx> =
198    Box<dyn FnMut(&mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, Poll<()>> + 'tcx>;
199
200impl<'tcx> Thread<'tcx> {
201    /// Get the name of the current thread if it was set.
202    fn thread_name(&self) -> Option<&[u8]> {
203        self.thread_name.as_deref()
204    }
205
206    /// Return whether this thread is enabled or not.
207    pub fn is_enabled(&self) -> bool {
208        self.state.is_enabled()
209    }
210
211    pub fn is_terminated(&self) -> bool {
212        self.state.is_terminated()
213    }
214
215    pub fn is_blocked_on(&self, reason: BlockReason) -> bool {
216        self.state.is_blocked_on(reason)
217    }
218
219    /// Get the name of the current thread for display purposes; will include thread ID if not set.
220    fn thread_display_name(&self, id: ThreadId) -> String {
221        if let Some(ref thread_name) = self.thread_name {
222            String::from_utf8_lossy(thread_name).into_owned()
223        } else {
224            format!("unnamed-{}", id.index())
225        }
226    }
227
228    /// Return the top user-relevant frame, if there is one. `skip` indicates how many top frames
229    /// should be skipped.
230    /// Note that the choice to return `None` here when there is no user-relevant frame is part of
231    /// justifying the optimization that only pushes of user-relevant frames require updating the
232    /// `top_user_relevant_frame` field.
233    fn compute_top_user_relevant_frame(&self, skip: usize) -> Option<usize> {
234        // We are search for the frame with maximum relevance.
235        let mut best = None;
236        for (idx, frame) in self.stack.iter().enumerate().rev().skip(skip) {
237            let relevance = frame.extra.user_relevance;
238            if relevance == u8::MAX {
239                // We can short-circuit this search.
240                return Some(idx);
241            }
242            if best.is_none_or(|(_best_idx, best_relevance)| best_relevance < relevance) {
243                // The previous best frame has strictly worse relevance, so despite us being lower
244                // in the stack, we win.
245                best = Some((idx, relevance));
246            }
247        }
248        best.map(|(idx, _relevance)| idx)
249    }
250
251    /// Re-compute the top user-relevant frame from scratch. `skip` indicates how many top frames
252    /// should be skipped.
253    pub fn recompute_top_user_relevant_frame(&mut self, skip: usize) {
254        self.top_user_relevant_frame = self.compute_top_user_relevant_frame(skip);
255    }
256
257    /// Set the top user-relevant frame to the given value. Must be equal to what
258    /// `get_top_user_relevant_frame` would return!
259    pub fn set_top_user_relevant_frame(&mut self, frame_idx: usize) {
260        debug_assert_eq!(Some(frame_idx), self.compute_top_user_relevant_frame(0));
261        self.top_user_relevant_frame = Some(frame_idx);
262    }
263
264    /// Returns the topmost frame that is considered user-relevant, or the
265    /// top of the stack if there is no such frame, or `None` if the stack is empty.
266    pub fn top_user_relevant_frame(&self) -> Option<usize> {
267        // This can be called upon creation of an allocation. We create allocations while setting up
268        // parts of the Rust runtime when we do not have any stack frames yet, so we need to handle
269        // empty stacks.
270        self.top_user_relevant_frame.or_else(|| self.stack.len().checked_sub(1))
271    }
272
273    pub fn current_user_relevance(&self) -> u8 {
274        self.top_user_relevant_frame()
275            .map(|frame_idx| self.stack[frame_idx].extra.user_relevance)
276            .unwrap_or(0)
277    }
278
279    pub fn current_user_relevant_span(&self) -> Span {
280        debug_assert_eq!(self.top_user_relevant_frame, self.compute_top_user_relevant_frame(0));
281        self.top_user_relevant_frame()
282            .map(|frame_idx| self.stack[frame_idx].current_span())
283            .unwrap_or(rustc_span::DUMMY_SP)
284    }
285}
286
287impl<'tcx> std::fmt::Debug for Thread<'tcx> {
288    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
289        write!(
290            f,
291            "{}({:?}, {:?})",
292            String::from_utf8_lossy(self.thread_name().unwrap_or(b"<unnamed>")),
293            self.state,
294            self.join_status
295        )
296    }
297}
298
299impl<'tcx> Thread<'tcx> {
300    fn new(name: Option<&str>, on_stack_empty: Option<StackEmptyCallback<'tcx>>) -> Self {
301        Self {
302            state: ThreadState::Enabled,
303            thread_name: name.map(|name| Vec::from(name.as_bytes())),
304            stack: Vec::new(),
305            origin_span: DUMMY_SP,
306            top_user_relevant_frame: None,
307            join_status: ThreadJoinStatus::Joinable,
308            unwind_payloads: Vec::new(),
309            last_error: None,
310            on_stack_empty,
311        }
312    }
313}
314
315impl VisitProvenance for Thread<'_> {
316    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
317        let Thread {
318            unwind_payloads: panic_payload,
319            last_error,
320            stack,
321            origin_span: _,
322            top_user_relevant_frame: _,
323            state: _,
324            thread_name: _,
325            join_status: _,
326            on_stack_empty: _, // we assume the closure captures no GC-relevant state
327        } = self;
328
329        for payload in panic_payload {
330            payload.visit_provenance(visit);
331        }
332        last_error.visit_provenance(visit);
333        for frame in stack {
334            frame.visit_provenance(visit)
335        }
336    }
337}
338
339impl VisitProvenance for Frame<'_, Provenance, FrameExtra<'_>> {
340    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
341        let return_place = self.return_place();
342        let locals = self.locals();
343        let Frame {
344            extra,
345            // There are some private fields we cannot access; they contain no tags.
346            ..
347        } = self;
348
349        // Return place.
350        return_place.visit_provenance(visit);
351        // Locals.
352        for local in locals.iter() {
353            // We only need the provenance so it's good for this to not be a real read.
354            match local.as_mplace_or_imm_ghost() {
355                None => {}
356                Some(Either::Left((ptr, meta))) => {
357                    ptr.visit_provenance(visit);
358                    meta.visit_provenance(visit);
359                }
360                Some(Either::Right(imm)) => {
361                    imm.visit_provenance(visit);
362                }
363            }
364        }
365
366        extra.visit_provenance(visit);
367    }
368}
369
370/// An error signaling that the requested thread doesn't exist or has terminated.
371#[derive(Debug, Copy, Clone)]
372pub enum ThreadLookupError {
373    /// No thread with this ID exists.
374    InvalidId,
375    /// The thread exists but has already terminated.
376    Terminated(ThreadId),
377}
378
379/// A set of threads.
380#[derive(Debug)]
381pub struct ThreadManager<'tcx> {
382    /// Identifier of the currently active thread.
383    active_thread: ThreadId,
384    /// Threads used in the program.
385    ///
386    /// Note that this vector also contains terminated threads.
387    threads: IndexVec<ThreadId, Thread<'tcx>>,
388    /// A mapping from a thread-local static to the thread specific allocation.
389    thread_local_allocs: FxHashMap<(DefId, ThreadId), StrictPointer>,
390    /// A flag that indicates that we should change the active thread.
391    /// Completely ignored in GenMC mode.
392    pub(super) yield_active_thread: bool,
393    /// A flag that indicates that we should do round robin scheduling of threads else randomized scheduling is used.
394    fixed_scheduling: bool,
395}
396
397impl VisitProvenance for ThreadManager<'_> {
398    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
399        let ThreadManager {
400            threads,
401            thread_local_allocs,
402            active_thread: _,
403            yield_active_thread: _,
404            fixed_scheduling: _,
405        } = self;
406
407        for thread in threads {
408            thread.visit_provenance(visit);
409        }
410        for ptr in thread_local_allocs.values() {
411            ptr.visit_provenance(visit);
412        }
413    }
414}
415
416impl<'tcx> ThreadManager<'tcx> {
417    pub(crate) fn new(config: &MiriConfig) -> Self {
418        let mut threads = IndexVec::new();
419        // Create the main thread and add it to the list of threads.
420        threads.push(Thread::new(Some("main"), None));
421        Self {
422            active_thread: ThreadId::MAIN_THREAD,
423            threads,
424            thread_local_allocs: Default::default(),
425            yield_active_thread: false,
426            fixed_scheduling: config.fixed_scheduling,
427        }
428    }
429
430    pub(crate) fn init(
431        ecx: &mut MiriInterpCx<'tcx>,
432        on_main_stack_empty: StackEmptyCallback<'tcx>,
433    ) {
434        ecx.machine.threads.threads[ThreadId::MAIN_THREAD].on_stack_empty =
435            Some(on_main_stack_empty);
436        if ecx.tcx.sess.target.os != Os::Windows {
437            // The main thread can *not* be joined on except on windows.
438            ecx.machine.threads.threads[ThreadId::MAIN_THREAD].join_status =
439                ThreadJoinStatus::Detached;
440        }
441    }
442
443    /// Returns the `ThreadId` for the given raw thread id.
444    /// Returns `Err(ThreadNotFound::InvalidId)` if the id is out of range, or
445    /// `Err(ThreadNotFound::Terminated(id))` if the thread exists but has terminated.
446    pub fn thread_id_try_from(&self, id: impl TryInto<u32>) -> Result<ThreadId, ThreadLookupError> {
447        if let Ok(id) = id.try_into()
448            && usize::try_from(id).is_ok_and(|id| id < self.threads.len())
449        {
450            let thread_id = ThreadId(id);
451            if self.threads[thread_id].state.is_terminated() {
452                Err(ThreadLookupError::Terminated(thread_id))
453            } else {
454                Ok(thread_id)
455            }
456        } else {
457            Err(ThreadLookupError::InvalidId)
458        }
459    }
460
461    /// Check if we have an allocation for the given thread local static for the
462    /// active thread.
463    fn get_thread_local_alloc_id(&self, def_id: DefId) -> Option<StrictPointer> {
464        self.thread_local_allocs.get(&(def_id, self.active_thread)).cloned()
465    }
466
467    /// Set the pointer for the allocation of the given thread local
468    /// static for the active thread.
469    ///
470    /// Panics if a thread local is initialized twice for the same thread.
471    fn set_thread_local_alloc(&mut self, def_id: DefId, ptr: StrictPointer) {
472        self.thread_local_allocs.try_insert((def_id, self.active_thread), ptr).unwrap();
473    }
474
475    /// Borrow the stack of the active thread.
476    pub fn active_thread_stack(&self) -> &[Frame<'tcx, Provenance, FrameExtra<'tcx>>] {
477        &self.threads[self.active_thread].stack
478    }
479
480    /// Mutably borrow the stack of the active thread.
481    pub fn active_thread_stack_mut(
482        &mut self,
483    ) -> &mut Vec<Frame<'tcx, Provenance, FrameExtra<'tcx>>> {
484        &mut self.threads[self.active_thread].stack
485    }
486
487    pub(super) fn all_threads(&self) -> impl Iterator<Item = (ThreadId, &Thread<'tcx>)> {
488        self.threads.iter_enumerated()
489    }
490
491    pub fn all_blocked_stacks(
492        &self,
493    ) -> impl Iterator<Item = (ThreadId, &[Frame<'tcx, Provenance, FrameExtra<'tcx>>])> {
494        self.threads
495            .iter_enumerated()
496            .filter(|(_id, t)| matches!(t.state, ThreadState::Blocked { .. }))
497            .map(|(id, t)| (id, &t.stack[..]))
498    }
499
500    /// Create a new thread and returns its id.
501    fn create_thread(&mut self, on_stack_empty: StackEmptyCallback<'tcx>) -> ThreadId {
502        let new_thread_id = ThreadId::new(self.threads.len());
503        self.threads.push(Thread::new(None, Some(on_stack_empty)));
504        new_thread_id
505    }
506
507    /// Set an active thread and return the id of the thread that was active before.
508    pub(super) fn set_active_thread(&mut self, id: ThreadId) -> ThreadId {
509        assert!(id.index() < self.threads.len());
510        info!(
511            "---------- Now executing on thread `{}` (previous: `{}`) ----------------------------------------",
512            self.get_thread_display_name(id),
513            self.get_thread_display_name(self.active_thread)
514        );
515        std::mem::replace(&mut self.active_thread, id)
516    }
517
518    /// Get the id of the currently active thread.
519    pub fn active_thread(&self) -> ThreadId {
520        self.active_thread
521    }
522
523    /// Get the total number of threads that were ever spawn by this program.
524    pub fn get_total_thread_count(&self) -> usize {
525        self.threads.len()
526    }
527
528    /// Get the total of threads that are currently live, i.e., not yet terminated.
529    /// (They might be blocked.)
530    pub fn get_live_thread_count(&self) -> usize {
531        self.threads.iter().filter(|t| !t.state.is_terminated()).count()
532    }
533
534    /// Has the given thread terminated?
535    fn has_terminated(&self, thread_id: ThreadId) -> bool {
536        self.threads[thread_id].state.is_terminated()
537    }
538
539    /// Have all threads terminated?
540    fn have_all_terminated(&self) -> bool {
541        self.threads.iter().all(|thread| thread.state.is_terminated())
542    }
543
544    /// Enable the thread for execution. The thread must be terminated.
545    fn enable_thread(&mut self, thread_id: ThreadId) {
546        assert!(self.has_terminated(thread_id));
547        self.threads[thread_id].state = ThreadState::Enabled;
548    }
549
550    /// Get a mutable borrow of the currently active thread.
551    pub fn active_thread_mut(&mut self) -> &mut Thread<'tcx> {
552        &mut self.threads[self.active_thread]
553    }
554
555    /// Get a shared borrow of the currently active thread.
556    pub fn active_thread_ref(&self) -> &Thread<'tcx> {
557        &self.threads[self.active_thread]
558    }
559
560    pub fn thread_ref(&self, thread_id: ThreadId) -> &Thread<'tcx> {
561        &self.threads[thread_id]
562    }
563
564    /// Mark the thread as detached, which means that no other thread will try
565    /// to join it and the thread is responsible for cleaning up.
566    ///
567    /// `allow_terminated_joined` allows detaching joined threads that have already terminated.
568    /// This matches Windows's behavior for `CloseHandle`.
569    ///
570    /// See <https://docs.microsoft.com/en-us/windows/win32/procthread/thread-handles-and-identifiers>:
571    /// > The handle is valid until closed, even after the thread it represents has been terminated.
572    fn detach_thread(&mut self, id: ThreadId, allow_terminated_joined: bool) -> InterpResult<'tcx> {
573        // NOTE: In GenMC mode, we treat detached threads like regular threads that are never joined, so there is no special handling required here.
574        trace!("detaching {:?}", id);
575
576        let is_ub = if allow_terminated_joined && self.threads[id].state.is_terminated() {
577            // "Detached" in particular means "not yet joined". Redundant detaching is still UB.
578            self.threads[id].join_status == ThreadJoinStatus::Detached
579        } else {
580            self.threads[id].join_status != ThreadJoinStatus::Joinable
581        };
582        if is_ub {
583            throw_ub_format!("trying to detach thread that was already detached or joined");
584        }
585
586        self.threads[id].join_status = ThreadJoinStatus::Detached;
587        interp_ok(())
588    }
589
590    /// Set the name of the given thread.
591    pub fn set_thread_name(&mut self, thread: ThreadId, new_thread_name: Vec<u8>) {
592        self.threads[thread].thread_name = Some(new_thread_name);
593    }
594
595    /// Get the name of the given thread.
596    pub fn get_thread_name(&self, thread: ThreadId) -> Option<&[u8]> {
597        self.threads[thread].thread_name()
598    }
599
600    pub fn get_thread_display_name(&self, thread: ThreadId) -> String {
601        self.threads[thread].thread_display_name(thread)
602    }
603
604    /// Put the thread into the blocked state.
605    fn block_thread(
606        &mut self,
607        reason: BlockReason,
608        deadline: Option<Deadline>,
609        callback: DynUnblockCallback<'tcx>,
610    ) {
611        let state = &mut self.threads[self.active_thread].state;
612        assert!(state.is_enabled());
613        *state = ThreadState::Blocked { reason, deadline, callback }
614    }
615
616    pub fn fixed_scheduling(&self) -> bool {
617        self.fixed_scheduling
618    }
619}
620
621// Public interface to thread management.
622impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
623pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
624    #[inline]
625    fn thread_id_try_from(&self, id: impl TryInto<u32>) -> Result<ThreadId, ThreadLookupError> {
626        self.eval_context_ref().machine.threads.thread_id_try_from(id)
627    }
628
629    /// Get a thread-specific allocation id for the given thread-local static.
630    /// If needed, allocate a new one.
631    fn get_or_create_thread_local_alloc(
632        &mut self,
633        def_id: DefId,
634    ) -> InterpResult<'tcx, StrictPointer> {
635        let ecx = self.eval_context_mut();
636        let tcx = ecx.tcx;
637        if let Some(old_alloc) = ecx.machine.threads.get_thread_local_alloc_id(def_id) {
638            // We already have a thread-specific allocation id for this
639            // thread-local static.
640            interp_ok(old_alloc)
641        } else {
642            // We need to allocate a thread-specific allocation id for this
643            // thread-local static.
644            // First, we compute the initial value for this static.
645            if tcx.is_foreign_item(def_id) {
646                throw_unsup_format!("foreign thread-local statics are not supported");
647            }
648            let params = ecx.machine.get_default_alloc_params();
649            let alloc = ecx.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?;
650            // We make a full copy of this allocation.
651            let mut alloc = alloc.inner().adjust_from_tcx(
652                &ecx.tcx,
653                |bytes, align| {
654                    interp_ok(MiriAllocBytes::from_bytes(
655                        std::borrow::Cow::Borrowed(bytes),
656                        align,
657                        params,
658                    ))
659                },
660                |ptr| ecx.global_root_pointer(ptr),
661            )?;
662            // This allocation will be deallocated when the thread dies, so it is not in read-only memory.
663            alloc.mutability = Mutability::Mut;
664            // Create a fresh allocation with this content.
665            let ptr = ecx.insert_allocation(alloc, MiriMemoryKind::Tls.into())?;
666            ecx.machine.threads.set_thread_local_alloc(def_id, ptr);
667            interp_ok(ptr)
668        }
669    }
670
671    /// Start a regular (non-main) thread.
672    #[inline]
673    fn start_regular_thread(
674        &mut self,
675        thread: Option<MPlaceTy<'tcx>>,
676        start_routine: Pointer,
677        start_abi: ExternAbi,
678        func_arg: ImmTy<'tcx>,
679        ret_layout: TyAndLayout<'tcx>,
680    ) -> InterpResult<'tcx, ThreadId> {
681        let ecx = self.eval_context_mut();
682
683        // Create the new thread
684        let current_span = ecx.machine.current_user_relevant_span();
685        let new_thread_id = ecx.machine.threads.create_thread({
686            let mut state = tls::TlsDtorsState::new_for_thread_finish();
687            Box::new(move |m| state.on_stack_empty(m))
688        });
689        match &mut ecx.machine.data_race {
690            GlobalDataRaceHandler::None => {}
691            GlobalDataRaceHandler::Vclocks(data_race) =>
692                data_race.thread_created(&ecx.machine.threads, new_thread_id, current_span),
693            GlobalDataRaceHandler::Genmc(genmc_ctx) =>
694                genmc_ctx.handle_thread_create(
695                    &ecx.machine.threads,
696                    start_routine,
697                    &func_arg,
698                    new_thread_id,
699                )?,
700        }
701        // Write the current thread-id, switch to the next thread later
702        // to treat this write operation as occurring on the current thread.
703        if let Some(thread_info_place) = thread {
704            ecx.write_scalar(
705                Scalar::from_uint(new_thread_id.to_u32(), thread_info_place.layout.size),
706                &thread_info_place,
707            )?;
708        }
709
710        // Finally switch to new thread so that we can push the first stackframe.
711        // After this all accesses will be treated as occurring in the new thread.
712        let old_thread_id = ecx.machine.threads.set_active_thread(new_thread_id);
713
714        // The child inherits its parent's cpu affinity.
715        // Skips this if `machine.thread_cpu_affinity` is not initialized.
716        if let Some(thread_cpu_affinity) = &mut ecx.machine.thread_cpu_affinity
717            && let Some(cpuset) = thread_cpu_affinity.get(&old_thread_id).cloned()
718        {
719            thread_cpu_affinity.insert(new_thread_id, cpuset);
720        }
721
722        // Perform the function pointer load in the new thread frame.
723        let instance = ecx.get_ptr_fn(start_routine)?.as_instance()?;
724
725        // Note: the returned value is currently ignored (see the FIXME in
726        // pthread_join in shims/unix/thread.rs) because the Rust standard library does not use
727        // it.
728        let ret_place = ecx.allocate(ret_layout, MiriMemoryKind::Machine.into())?;
729
730        ecx.call_thread_root_function(
731            instance,
732            start_abi,
733            &[func_arg],
734            Some(&ret_place),
735            current_span,
736        )?;
737
738        // Restore the old active thread frame.
739        ecx.machine.threads.set_active_thread(old_thread_id);
740
741        interp_ok(new_thread_id)
742    }
743
744    /// Replace the active thread with an empty one, and a new on-empty handler.
745    /// The memory of the old stack is leaked.
746    /// Used to implement `exit`.
747    fn replace_active_thread_stack(&mut self, on_stack_empty: StackEmptyCallback<'tcx>) {
748        let ecx = self.eval_context_mut();
749        let thread = ecx.active_thread_mut();
750        let span = thread.current_user_relevant_span();
751
752        thread.stack.clear();
753        thread.origin_span = span;
754        thread.top_user_relevant_frame = None;
755        thread.on_stack_empty = Some(on_stack_empty);
756    }
757
758    /// Handles thread termination of the active thread: wakes up threads joining on this one,
759    /// and frees the thread's thread-local statics. `for_exit` indicates whether we are
760    /// about to exit the program; that changes some details.
761    ///
762    /// This is called by the eval loop when a thread's on_stack_empty returns `Ready`.
763    fn terminate_active_thread(&mut self, for_exit: bool) -> InterpResult<'tcx> {
764        let ecx = self.eval_context_mut();
765
766        // Mark thread as terminated.
767        let thread = ecx.active_thread_mut();
768        thread.state = ThreadState::Terminated;
769        assert!(
770            for_exit || thread.stack.is_empty(),
771            "only threads with an empty stack can be terminated"
772        );
773
774        // Deallocate TLS.
775        let gone_thread = ecx.active_thread();
776        {
777            let mut free_tls_statics = Vec::new();
778            ecx.machine.threads.thread_local_allocs.retain(|&(_def_id, thread), &mut ptr| {
779                if thread != gone_thread {
780                    // A different thread, keep this static around.
781                    return true;
782                }
783                // Delete this static from the map and from memory.
784                // We cannot free directly here as we cannot use `?` in this context.
785                free_tls_statics.push(ptr);
786                false
787            });
788            // Now free the TLS statics.
789            for ptr in free_tls_statics {
790                if for_exit {
791                    // Leak the thread-local statics rather than freeing them.
792                    if let Some(alloc) = ptr.provenance.get_alloc_id() {
793                        trace!("Thread-local static leaked and stored as static root: {:?}", alloc);
794                        ecx.machine.static_roots.push(alloc);
795                    }
796                } else {
797                    ecx.deallocate_ptr(ptr.into(), None, MiriMemoryKind::Tls.into())?;
798                }
799            }
800        }
801
802        match &mut ecx.machine.data_race {
803            GlobalDataRaceHandler::None => {}
804            GlobalDataRaceHandler::Vclocks(data_race) =>
805                data_race.thread_terminated(&ecx.machine.threads),
806            GlobalDataRaceHandler::Genmc(genmc_ctx) => {
807                // Inform GenMC that the thread finished.
808                // This needs to happen once all accesses to the thread are done, including freeing any TLS statics.
809                genmc_ctx.handle_thread_finish(&ecx.machine.threads)
810            }
811        }
812
813        // Unblock joining threads.
814        let unblock_reason = BlockReason::Join(gone_thread);
815        let threads = &ecx.machine.threads.threads;
816        let joining_threads = threads
817            .iter_enumerated()
818            .filter(|(_, thread)| thread.state.is_blocked_on(unblock_reason))
819            .map(|(id, _)| id)
820            .collect::<Vec<_>>();
821        for thread in joining_threads {
822            ecx.unblock_thread(thread, unblock_reason)?;
823        }
824
825        interp_ok(())
826    }
827
828    /// Block the current thread, with an optional timeout.
829    /// The callback will be invoked when the thread gets unblocked.
830    #[inline]
831    fn block_thread(
832        &mut self,
833        reason: BlockReason,
834        deadline: Option<Deadline>,
835        callback: DynUnblockCallback<'tcx>,
836    ) {
837        let ecx = self.eval_context_mut();
838        if deadline.is_some() && ecx.machine.data_race.as_genmc_ref().is_some() {
839            panic!("Unimplemented: Timeouts not yet supported in GenMC mode.");
840        }
841        if matches!(deadline, Some(Deadline::RealTime(_))) && !ecx.machine.communicate() {
842            panic!("cannot have `RealTime` timeout with isolation");
843        }
844        ecx.machine.threads.block_thread(reason, deadline, callback);
845    }
846
847    /// Put the blocked thread into the enabled state.
848    /// Sanity-checks that the thread previously was blocked for the right reason.
849    fn unblock_thread(&mut self, thread: ThreadId, reason: BlockReason) -> InterpResult<'tcx> {
850        let ecx = self.eval_context_mut();
851        let old_state =
852            mem::replace(&mut ecx.machine.threads.threads[thread].state, ThreadState::Enabled);
853        let callback = match old_state {
854            ThreadState::Blocked { reason: actual_reason, callback, .. } => {
855                assert_eq!(
856                    reason, actual_reason,
857                    "unblock_thread: thread was blocked for the wrong reason"
858                );
859                callback
860            }
861            _ => panic!("unblock_thread: thread was not blocked"),
862        };
863        // The callback must be executed in the previously blocked thread.
864        let old_thread = ecx.machine.threads.set_active_thread(thread);
865        callback.call(ecx, UnblockKind::Ready)?;
866        ecx.machine.threads.set_active_thread(old_thread);
867        interp_ok(())
868    }
869
870    /// Find all threads with expired timeouts, unblock them and execute their timeout callbacks.
871    ///
872    /// This method returns the minimum duration until the next thread deadline.
873    /// If all ready threads have no deadline set, [`None`] is returned.
874    fn unblock_expired_deadlines(&mut self) -> InterpResult<'tcx, Option<Duration>> {
875        let ecx = self.eval_context_mut();
876        let communicate = ecx.machine.communicate();
877
878        let mut min_wait_time = Option::<Duration>::None;
879        let mut callbacks = Vec::new();
880
881        for (id, thread) in ecx.machine.threads.threads.iter_enumerated_mut() {
882            match &thread.state {
883                ThreadState::Blocked { deadline: Some(deadline), .. } => {
884                    let wait_time = match deadline {
885                        Deadline::Monotonic(instant) =>
886                            instant.duration_since(ecx.machine.monotonic_clock.now()),
887                        Deadline::RealTime(time) => {
888                            assert!(communicate, "cannot have `RealTime` timeout with isolation");
889                            time.duration_since(SystemTime::now()).unwrap_or(Duration::ZERO)
890                        }
891                    };
892
893                    if wait_time.is_zero() {
894                        // The timeout expired for this thread.
895                        let old_state = mem::replace(&mut thread.state, ThreadState::Enabled);
896                        let ThreadState::Blocked { callback, .. } = old_state else {
897                            unreachable!()
898                        };
899                        // Add callback to list to be run after this loop because of borrow-checking.
900                        callbacks.push((id, callback));
901                    } else {
902                        // Update `min_wait_time` to contain the smallest duration until
903                        // the next timeout expires.
904                        min_wait_time = Some(wait_time.min(min_wait_time.unwrap_or(Duration::MAX)));
905                    }
906                }
907                _ => {}
908            }
909        }
910
911        for (thread, callback) in callbacks {
912            // This back-and-forth with `set_active_thread` is here because of two
913            // design decisions:
914            // 1. Make the caller and not the callback responsible for changing
915            //    thread.
916            // 2. Make the scheduler the only place that can change the active
917            //    thread.
918            let old_thread = ecx.machine.threads.set_active_thread(thread);
919            callback.call(ecx, UnblockKind::TimedOut)?;
920            ecx.machine.threads.set_active_thread(old_thread);
921        }
922
923        interp_ok(min_wait_time)
924    }
925
926    #[inline]
927    fn detach_thread(
928        &mut self,
929        thread_id: ThreadId,
930        allow_terminated_joined: bool,
931    ) -> InterpResult<'tcx> {
932        let ecx = self.eval_context_mut();
933        ecx.machine.threads.detach_thread(thread_id, allow_terminated_joined)
934    }
935
936    /// Mark that the active thread tries to join the thread with `joined_thread_id`.
937    ///
938    /// When the join is successful (immediately, or as soon as the joined thread finishes), `success_retval` will be written to `return_dest`.
939    fn join_thread(
940        &mut self,
941        joined_thread_id: ThreadId,
942        success_retval: Scalar,
943        return_dest: &MPlaceTy<'tcx>,
944    ) -> InterpResult<'tcx> {
945        let ecx = self.eval_context_mut();
946        let thread_mgr = &mut ecx.machine.threads;
947        if thread_mgr.threads[joined_thread_id].join_status == ThreadJoinStatus::Detached {
948            // On Windows this corresponds to joining on a closed handle.
949            throw_ub_format!("trying to join a detached thread");
950        }
951
952        fn after_join<'tcx>(
953            ecx: &mut MiriInterpCx<'tcx>,
954            joined_thread_id: ThreadId,
955            success_retval: Scalar,
956            return_dest: &MPlaceTy<'tcx>,
957        ) -> InterpResult<'tcx> {
958            let threads = &ecx.machine.threads;
959            match &mut ecx.machine.data_race {
960                GlobalDataRaceHandler::None => {}
961                GlobalDataRaceHandler::Vclocks(data_race) =>
962                    data_race.thread_joined(threads, joined_thread_id),
963                GlobalDataRaceHandler::Genmc(genmc_ctx) =>
964                    genmc_ctx.handle_thread_join(threads.active_thread, joined_thread_id)?,
965            }
966            ecx.write_scalar(success_retval, return_dest)?;
967            interp_ok(())
968        }
969
970        // Mark the joined thread as being joined so that we detect if other
971        // threads try to join it.
972        thread_mgr.threads[joined_thread_id].join_status = ThreadJoinStatus::Joined;
973        if !thread_mgr.threads[joined_thread_id].state.is_terminated() {
974            trace!(
975                "{:?} blocked on {:?} when trying to join",
976                thread_mgr.active_thread, joined_thread_id
977            );
978            if let Some(genmc_ctx) = ecx.machine.data_race.as_genmc_ref() {
979                genmc_ctx.handle_thread_join(thread_mgr.active_thread, joined_thread_id)?;
980            }
981
982            // The joined thread is still running, we need to wait for it.
983            // Once we get unblocked, perform the appropriate synchronization and write the return value.
984            let dest = return_dest.clone();
985            thread_mgr.block_thread(
986                BlockReason::Join(joined_thread_id),
987                None,
988                callback!(
989                    @capture<'tcx> {
990                        joined_thread_id: ThreadId,
991                        dest: MPlaceTy<'tcx>,
992                        success_retval: Scalar,
993                    }
994                    |ecx, unblock: UnblockKind| {
995                        assert_eq!(unblock, UnblockKind::Ready);
996                        after_join(ecx, joined_thread_id, success_retval, &dest)
997                    }
998                ),
999            );
1000        } else {
1001            // The thread has already terminated - establish happens-before and write the return value.
1002            after_join(ecx, joined_thread_id, success_retval, return_dest)?;
1003        }
1004        interp_ok(())
1005    }
1006
1007    /// Mark that the active thread tries to exclusively join the thread with `joined_thread_id`.
1008    /// If the thread is already joined by another thread, it will throw UB.
1009    ///
1010    /// When the join is successful (immediately, or as soon as the joined thread finishes), `success_retval` will be written to `return_dest`.
1011    fn join_thread_exclusive(
1012        &mut self,
1013        joined_thread_id: ThreadId,
1014        success_retval: Scalar,
1015        return_dest: &MPlaceTy<'tcx>,
1016    ) -> InterpResult<'tcx> {
1017        let ecx = self.eval_context_mut();
1018        let threads = &ecx.machine.threads.threads;
1019        if threads[joined_thread_id].join_status == ThreadJoinStatus::Joined {
1020            throw_ub_format!("trying to join an already joined thread");
1021        }
1022
1023        if joined_thread_id == ecx.machine.threads.active_thread {
1024            throw_ub_format!("trying to join itself");
1025        }
1026
1027        // Sanity check `join_status`.
1028        assert!(
1029            threads
1030                .iter()
1031                .all(|thread| { !thread.state.is_blocked_on(BlockReason::Join(joined_thread_id)) }),
1032            "this thread already has threads waiting for its termination"
1033        );
1034
1035        ecx.join_thread(joined_thread_id, success_retval, return_dest)
1036    }
1037
1038    #[inline]
1039    fn active_thread(&self) -> ThreadId {
1040        let ecx = self.eval_context_ref();
1041        ecx.machine.threads.active_thread()
1042    }
1043
1044    #[inline]
1045    fn active_thread_mut(&mut self) -> &mut Thread<'tcx> {
1046        let ecx = self.eval_context_mut();
1047        ecx.machine.threads.active_thread_mut()
1048    }
1049
1050    #[inline]
1051    fn active_thread_ref(&self) -> &Thread<'tcx> {
1052        let ecx = self.eval_context_ref();
1053        ecx.machine.threads.active_thread_ref()
1054    }
1055
1056    #[inline]
1057    fn get_total_thread_count(&self) -> usize {
1058        let ecx = self.eval_context_ref();
1059        ecx.machine.threads.get_total_thread_count()
1060    }
1061
1062    #[inline]
1063    fn have_all_terminated(&self) -> bool {
1064        let ecx = self.eval_context_ref();
1065        ecx.machine.threads.have_all_terminated()
1066    }
1067
1068    #[inline]
1069    fn enable_thread(&mut self, thread_id: ThreadId) {
1070        let ecx = self.eval_context_mut();
1071        ecx.machine.threads.enable_thread(thread_id);
1072    }
1073
1074    #[inline]
1075    fn active_thread_stack<'a>(&'a self) -> &'a [Frame<'tcx, Provenance, FrameExtra<'tcx>>] {
1076        let ecx = self.eval_context_ref();
1077        ecx.machine.threads.active_thread_stack()
1078    }
1079
1080    #[inline]
1081    fn active_thread_stack_mut<'a>(
1082        &'a mut self,
1083    ) -> &'a mut Vec<Frame<'tcx, Provenance, FrameExtra<'tcx>>> {
1084        let ecx = self.eval_context_mut();
1085        ecx.machine.threads.active_thread_stack_mut()
1086    }
1087
1088    /// Set the name of the current thread. The buffer must not include the null terminator.
1089    #[inline]
1090    fn set_thread_name(&mut self, thread: ThreadId, new_thread_name: Vec<u8>) {
1091        self.eval_context_mut().machine.threads.set_thread_name(thread, new_thread_name);
1092    }
1093
1094    #[inline]
1095    fn get_thread_name<'c>(&'c self, thread: ThreadId) -> Option<&'c [u8]>
1096    where
1097        'tcx: 'c,
1098    {
1099        self.eval_context_ref().machine.threads.get_thread_name(thread)
1100    }
1101
1102    #[inline]
1103    fn yield_active_thread(&mut self) {
1104        // We do not yield immediately, as swapping out the current stack while executing a MIR statement
1105        // could lead to all sorts of confusion.
1106        // We should only switch stacks between steps.
1107        self.eval_context_mut().machine.threads.yield_active_thread = true;
1108    }
1109
1110    #[inline]
1111    fn maybe_preempt_active_thread(&mut self) {
1112        let ecx = self.eval_context_mut();
1113        if !ecx.machine.threads.fixed_scheduling
1114            && ecx.machine.rng.get_mut().random_bool(ecx.machine.preemption_rate)
1115        {
1116            ecx.yield_active_thread();
1117        }
1118    }
1119}