]> git.lizzy.rs Git - rust.git/blob - src/thread.rs
Auto merge of #1362 - vakaras:add-sync-primitives-cr1, r=RalfJung
[rust.git] / src / thread.rs
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 use rustc_middle::{
15     middle::codegen_fn_attrs::CodegenFnAttrFlags,
16     mir,
17     ty::{self, Instance},
18 };
19
20 use crate::sync::SynchronizationState;
21 use crate::*;
22
23 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
24 pub enum SchedulingAction {
25     /// Execute step on the active thread.
26     ExecuteStep,
27     /// Execute a timeout callback.
28     ExecuteTimeoutCallback,
29     /// Execute destructors of the active thread.
30     ExecuteDtors,
31     /// Stop the program.
32     Stop,
33 }
34
35 /// Timeout callbacks can be created by synchronization primitives to tell the
36 /// scheduler that they should be called once some period of time passes.
37 type TimeoutCallback<'mir, 'tcx> =
38     Box<dyn FnOnce(&mut InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>) -> InterpResult<'tcx> + 'tcx>;
39
40 /// A thread identifier.
41 #[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
42 pub struct ThreadId(u32);
43
44 /// The main thread. When it terminates, the whole application terminates.
45 const MAIN_THREAD: ThreadId = ThreadId(0);
46
47 impl ThreadId {
48     pub fn to_u32(self) -> u32 {
49         self.0
50     }
51 }
52
53 impl Idx for ThreadId {
54     fn new(idx: usize) -> Self {
55         ThreadId(u32::try_from(idx).unwrap())
56     }
57
58     fn index(self) -> usize {
59         usize::try_from(self.0).unwrap()
60     }
61 }
62
63 impl TryFrom<u64> for ThreadId {
64     type Error = TryFromIntError;
65     fn try_from(id: u64) -> Result<Self, Self::Error> {
66         u32::try_from(id).map(|id_u32| Self(id_u32))
67     }
68 }
69
70 impl From<u32> for ThreadId {
71     fn from(id: u32) -> Self {
72         Self(id)
73     }
74 }
75
76 impl ThreadId {
77     pub fn to_u32_scalar<'tcx>(&self) -> Scalar<Tag> {
78         Scalar::from_u32(u32::try_from(self.0).unwrap())
79     }
80 }
81
82 /// The state of a thread.
83 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
84 pub enum ThreadState {
85     /// The thread is enabled and can be executed.
86     Enabled,
87     /// The thread tried to join the specified thread and is blocked until that
88     /// thread terminates.
89     BlockedOnJoin(ThreadId),
90     /// The thread is blocked on some synchronization primitive. It is the
91     /// responsibility of the synchronization primitives to track threads that
92     /// are blocked by them.
93     BlockedOnSync,
94     /// The thread has terminated its execution (we do not delete terminated
95     /// threads).
96     Terminated,
97 }
98
99 /// The join status of a thread.
100 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
101 enum ThreadJoinStatus {
102     /// The thread can be joined.
103     Joinable,
104     /// A thread is detached if its join handle was destroyed and no other
105     /// thread can join it.
106     Detached,
107     /// The thread was already joined by some thread and cannot be joined again.
108     Joined,
109 }
110
111 /// A thread.
112 pub struct Thread<'mir, 'tcx> {
113     state: ThreadState,
114     /// Name of the thread.
115     thread_name: Option<Vec<u8>>,
116     /// The virtual call stack.
117     stack: Vec<Frame<'mir, 'tcx, Tag, FrameData<'tcx>>>,
118     /// The join status.
119     join_status: ThreadJoinStatus,
120 }
121
122 impl<'mir, 'tcx> Thread<'mir, 'tcx> {
123     /// Check if the thread is done executing (no more stack frames). If yes,
124     /// change the state to terminated and return `true`.
125     fn check_terminated(&mut self) -> bool {
126         if self.state == ThreadState::Enabled {
127             if self.stack.is_empty() {
128                 self.state = ThreadState::Terminated;
129                 return true;
130             }
131         }
132         false
133     }
134
135     /// Get the name of the current thread, or `<unnamed>` if it was not set.
136     fn thread_name(&self) -> &[u8] {
137         if let Some(ref thread_name) = self.thread_name {
138             thread_name
139         } else {
140             b"<unnamed>"
141         }
142     }
143 }
144
145 impl<'mir, 'tcx> std::fmt::Debug for Thread<'mir, 'tcx> {
146     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147         write!(f, "{}({:?}, {:?})", String::from_utf8_lossy(self.thread_name()), self.state, self.join_status)
148     }
149 }
150
151 impl<'mir, 'tcx> Default for Thread<'mir, 'tcx> {
152     fn default() -> Self {
153         Self {
154             state: ThreadState::Enabled,
155             thread_name: None,
156             stack: Vec::new(),
157             join_status: ThreadJoinStatus::Joinable,
158         }
159     }
160 }
161
162 /// A specific moment in time.
163 #[derive(Debug)]
164 pub enum Time {
165     Monotonic(Instant),
166     RealTime(SystemTime),
167 }
168
169 impl Time {
170     /// How long do we have to wait from now until the specified time?
171     fn get_wait_time(&self) -> Duration {
172         match self {
173             Time::Monotonic(instant) => instant.saturating_duration_since(Instant::now()),
174             Time::RealTime(time) =>
175                 time.duration_since(SystemTime::now()).unwrap_or(Duration::new(0, 0)),
176         }
177     }
178 }
179
180 /// Callbacks are used to implement timeouts. For example, waiting on a
181 /// conditional variable with a timeout creates a callback that is called after
182 /// the specified time and unblocks the thread. If another thread signals on the
183 /// conditional variable, the signal handler deletes the callback.
184 struct TimeoutCallbackInfo<'mir, 'tcx> {
185     /// The callback should be called no earlier than this time.
186     call_time: Time,
187     /// The called function.
188     callback: TimeoutCallback<'mir, 'tcx>,
189 }
190
191 impl<'mir, 'tcx> std::fmt::Debug for TimeoutCallbackInfo<'mir, 'tcx> {
192     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193         write!(f, "TimeoutCallback({:?})", self.call_time)
194     }
195 }
196
197 /// A set of threads.
198 #[derive(Debug)]
199 pub struct ThreadManager<'mir, 'tcx> {
200     /// Identifier of the currently active thread.
201     active_thread: ThreadId,
202     /// Threads used in the program.
203     ///
204     /// Note that this vector also contains terminated threads.
205     threads: IndexVec<ThreadId, Thread<'mir, 'tcx>>,
206     /// This field is pub(crate) because the synchronization primitives
207     /// (`crate::sync`) need a way to access it.
208     pub(crate) sync: SynchronizationState,
209     /// A mapping from a thread-local static to an allocation id of a thread
210     /// specific allocation.
211     thread_local_alloc_ids: RefCell<FxHashMap<(DefId, ThreadId), AllocId>>,
212     /// A flag that indicates that we should change the active thread.
213     yield_active_thread: bool,
214     /// Callbacks that are called once the specified time passes.
215     timeout_callbacks: FxHashMap<ThreadId, TimeoutCallbackInfo<'mir, 'tcx>>,
216 }
217
218 impl<'mir, 'tcx> Default for ThreadManager<'mir, 'tcx> {
219     fn default() -> Self {
220         let mut threads = IndexVec::new();
221         // Create the main thread and add it to the list of threads.
222         let mut main_thread = Thread::default();
223         // The main thread can *not* be joined on.
224         main_thread.join_status = ThreadJoinStatus::Detached;
225         threads.push(main_thread);
226         Self {
227             active_thread: ThreadId::new(0),
228             threads: threads,
229             sync: SynchronizationState::default(),
230             thread_local_alloc_ids: Default::default(),
231             yield_active_thread: false,
232             timeout_callbacks: FxHashMap::default(),
233         }
234     }
235 }
236
237 impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
238     /// Check if we have an allocation for the given thread local static for the
239     /// active thread.
240     fn get_thread_local_alloc_id(&self, def_id: DefId) -> Option<AllocId> {
241         self.thread_local_alloc_ids.borrow().get(&(def_id, self.active_thread)).cloned()
242     }
243
244     /// Set the allocation id as the allocation id of the given thread local
245     /// static for the active thread.
246     ///
247     /// Panics if a thread local is initialized twice for the same thread.
248     fn set_thread_local_alloc_id(&self, def_id: DefId, new_alloc_id: AllocId) {
249         self.thread_local_alloc_ids
250             .borrow_mut()
251             .insert((def_id, self.active_thread), new_alloc_id)
252             .unwrap_none();
253     }
254
255     /// Borrow the stack of the active thread.
256     fn active_thread_stack(&self) -> &[Frame<'mir, 'tcx, Tag, FrameData<'tcx>>] {
257         &self.threads[self.active_thread].stack
258     }
259
260     /// Mutably borrow the stack of the active thread.
261     fn active_thread_stack_mut(&mut self) -> &mut Vec<Frame<'mir, 'tcx, Tag, FrameData<'tcx>>> {
262         &mut self.threads[self.active_thread].stack
263     }
264
265     /// Create a new thread and returns its id.
266     fn create_thread(&mut self) -> ThreadId {
267         let new_thread_id = ThreadId::new(self.threads.len());
268         self.threads.push(Default::default());
269         new_thread_id
270     }
271
272     /// Set an active thread and return the id of the thread that was active before.
273     fn set_active_thread_id(&mut self, id: ThreadId) -> ThreadId {
274         let active_thread_id = self.active_thread;
275         self.active_thread = id;
276         assert!(self.active_thread.index() < self.threads.len());
277         active_thread_id
278     }
279
280     /// Get the id of the currently active thread.
281     fn get_active_thread_id(&self) -> ThreadId {
282         self.active_thread
283     }
284
285     /// Get the total number of threads that were ever spawn by this program.
286     fn get_total_thread_count(&self) -> usize {
287         self.threads.len()
288     }
289
290     /// Has the given thread terminated?
291     fn has_terminated(&self, thread_id: ThreadId) -> bool {
292         self.threads[thread_id].state == ThreadState::Terminated
293     }
294
295     /// Enable the thread for execution. The thread must be terminated.
296     fn enable_thread(&mut self, thread_id: ThreadId) {
297         assert!(self.has_terminated(thread_id));
298         self.threads[thread_id].state = ThreadState::Enabled;
299     }
300
301     /// Get a mutable borrow of the currently active thread.
302     fn active_thread_mut(&mut self) -> &mut Thread<'mir, 'tcx> {
303         &mut self.threads[self.active_thread]
304     }
305
306     /// Get a shared borrow of the currently active thread.
307     fn active_thread_ref(&self) -> &Thread<'mir, 'tcx> {
308         &self.threads[self.active_thread]
309     }
310
311     /// Mark the thread as detached, which means that no other thread will try
312     /// to join it and the thread is responsible for cleaning up.
313     fn detach_thread(&mut self, id: ThreadId) -> InterpResult<'tcx> {
314         if self.threads[id].join_status != ThreadJoinStatus::Joinable {
315             throw_ub_format!("trying to detach thread that was already detached or joined");
316         }
317         self.threads[id].join_status = ThreadJoinStatus::Detached;
318         Ok(())
319     }
320
321     /// Mark that the active thread tries to join the thread with `joined_thread_id`.
322     fn join_thread(&mut self, joined_thread_id: ThreadId) -> InterpResult<'tcx> {
323         if self.threads[joined_thread_id].join_status != ThreadJoinStatus::Joinable {
324             throw_ub_format!("trying to join a detached or already joined thread");
325         }
326         if joined_thread_id == self.active_thread {
327             throw_ub_format!("trying to join itself");
328         }
329         assert!(
330             self.threads
331                 .iter()
332                 .all(|thread| thread.state != ThreadState::BlockedOnJoin(joined_thread_id)),
333             "a joinable thread already has threads waiting for its termination"
334         );
335         // Mark the joined thread as being joined so that we detect if other
336         // threads try to join it.
337         self.threads[joined_thread_id].join_status = ThreadJoinStatus::Joined;
338         if self.threads[joined_thread_id].state != ThreadState::Terminated {
339             // The joined thread is still running, we need to wait for it.
340             self.active_thread_mut().state = ThreadState::BlockedOnJoin(joined_thread_id);
341             trace!(
342                 "{:?} blocked on {:?} when trying to join",
343                 self.active_thread,
344                 joined_thread_id
345             );
346         }
347         Ok(())
348     }
349
350     /// Set the name of the active thread.
351     fn set_thread_name(&mut self, new_thread_name: Vec<u8>) {
352         self.active_thread_mut().thread_name = Some(new_thread_name);
353     }
354
355     /// Get the name of the active thread.
356     fn get_thread_name(&self) -> &[u8] {
357         self.active_thread_ref().thread_name()
358     }
359
360     /// Put the thread into the blocked state.
361     fn block_thread(&mut self, thread: ThreadId) {
362         let state = &mut self.threads[thread].state;
363         assert_eq!(*state, ThreadState::Enabled);
364         *state = ThreadState::BlockedOnSync;
365     }
366
367     /// Put the blocked thread into the enabled state.
368     fn unblock_thread(&mut self, thread: ThreadId) {
369         let state = &mut self.threads[thread].state;
370         assert_eq!(*state, ThreadState::BlockedOnSync);
371         *state = ThreadState::Enabled;
372     }
373
374     /// Change the active thread to some enabled thread.
375     fn yield_active_thread(&mut self) {
376         self.yield_active_thread = true;
377     }
378
379     /// Register the given `callback` to be called once the `call_time` passes.
380     ///
381     /// The callback will be called with `thread` being the active thread, and
382     /// the callback may not change the active thread.
383     fn register_timeout_callback(
384         &mut self,
385         thread: ThreadId,
386         call_time: Time,
387         callback: TimeoutCallback<'mir, 'tcx>,
388     ) {
389         self.timeout_callbacks
390             .insert(thread, TimeoutCallbackInfo { call_time, callback })
391             .unwrap_none();
392     }
393
394     /// Unregister the callback for the `thread`.
395     fn unregister_timeout_callback_if_exists(&mut self, thread: ThreadId) {
396         self.timeout_callbacks.remove(&thread);
397     }
398
399     /// Get a callback that is ready to be called.
400     fn get_ready_callback(&mut self) -> Option<(ThreadId, TimeoutCallback<'mir, 'tcx>)> {
401         // We iterate over all threads in the order of their indices because
402         // this allows us to have a deterministic scheduler.
403         for thread in self.threads.indices() {
404             match self.timeout_callbacks.entry(thread) {
405                 Entry::Occupied(entry) =>
406                     if entry.get().call_time.get_wait_time() == Duration::new(0, 0) {
407                         return Some((thread, entry.remove().callback));
408                     },
409                 Entry::Vacant(_) => {}
410             }
411         }
412         None
413     }
414
415     /// Decide which action to take next and on which thread.
416     ///
417     /// The currently implemented scheduling policy is the one that is commonly
418     /// used in stateless model checkers such as Loom: run the active thread as
419     /// long as we can and switch only when we have to (the active thread was
420     /// blocked, terminated, or has explicitly asked to be preempted).
421     fn schedule(&mut self) -> InterpResult<'tcx, SchedulingAction> {
422         // Check whether the thread has **just** terminated (`check_terminated`
423         // checks whether the thread has popped all its stack and if yes, sets
424         // the thread state to terminated).
425         if self.threads[self.active_thread].check_terminated() {
426             // Check if we need to unblock any threads.
427             for (i, thread) in self.threads.iter_enumerated_mut() {
428                 if thread.state == ThreadState::BlockedOnJoin(self.active_thread) {
429                     trace!("unblocking {:?} because {:?} terminated", i, self.active_thread);
430                     thread.state = ThreadState::Enabled;
431                 }
432             }
433             return Ok(SchedulingAction::ExecuteDtors);
434         }
435         if self.threads[MAIN_THREAD].state == ThreadState::Terminated {
436             // The main thread terminated; stop the program.
437             if self.threads.iter().any(|thread| thread.state != ThreadState::Terminated) {
438                 // FIXME: This check should be either configurable or just emit
439                 // a warning. For example, it seems normal for a program to
440                 // terminate without waiting for its detached threads to
441                 // terminate. However, this case is not trivial to support
442                 // because we also probably do not want to consider the memory
443                 // owned by these threads as leaked.
444                 throw_unsup_format!("the main thread terminated without waiting for other threads");
445             }
446             return Ok(SchedulingAction::Stop);
447         }
448         // At least for `pthread_cond_timedwait` we need to report timeout when
449         // the function is called already after the specified time even if a
450         // signal is received before the thread gets scheduled. Therefore, we
451         // need to schedule all timeout callbacks before we continue regular
452         // execution.
453         //
454         // Documentation:
455         // https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html#
456         let potential_sleep_time =
457             self.timeout_callbacks.values().map(|info| info.call_time.get_wait_time()).min();
458         if potential_sleep_time == Some(Duration::new(0, 0)) {
459             return Ok(SchedulingAction::ExecuteTimeoutCallback);
460         }
461         // No callbacks scheduled, pick a regular thread to execute.
462         if self.threads[self.active_thread].state == ThreadState::Enabled
463             && !self.yield_active_thread
464         {
465             // The currently active thread is still enabled, just continue with it.
466             return Ok(SchedulingAction::ExecuteStep);
467         }
468         // We need to pick a new thread for execution.
469         for (id, thread) in self.threads.iter_enumerated() {
470             if thread.state == ThreadState::Enabled {
471                 if !self.yield_active_thread || id != self.active_thread {
472                     self.active_thread = id;
473                     break;
474                 }
475             }
476         }
477         self.yield_active_thread = false;
478         if self.threads[self.active_thread].state == ThreadState::Enabled {
479             return Ok(SchedulingAction::ExecuteStep);
480         }
481         // We have not found a thread to execute.
482         if self.threads.iter().all(|thread| thread.state == ThreadState::Terminated) {
483             unreachable!("all threads terminated without the main thread terminating?!");
484         } else if let Some(sleep_time) = potential_sleep_time {
485             // All threads are currently blocked, but we have unexecuted
486             // timeout_callbacks, which may unblock some of the threads. Hence,
487             // sleep until the first callback.
488             std::thread::sleep(sleep_time);
489             Ok(SchedulingAction::ExecuteTimeoutCallback)
490         } else {
491             throw_machine_stop!(TerminationInfo::Deadlock);
492         }
493     }
494 }
495
496 // Public interface to thread management.
497 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
498 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
499     /// A workaround for thread-local statics until
500     /// https://github.com/rust-lang/rust/issues/70685 is fixed: change the
501     /// thread-local allocation id with a freshly generated allocation id for
502     /// the currently active thread.
503     fn remap_thread_local_alloc_ids(
504         &self,
505         val: &mut mir::interpret::ConstValue<'tcx>,
506     ) -> InterpResult<'tcx> {
507         let this = self.eval_context_ref();
508         match *val {
509             mir::interpret::ConstValue::Scalar(Scalar::Ptr(ref mut ptr)) => {
510                 let alloc_id = ptr.alloc_id;
511                 let alloc = this.tcx.get_global_alloc(alloc_id);
512                 let tcx = this.tcx;
513                 let is_thread_local = |def_id| {
514                     tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL)
515                 };
516                 match alloc {
517                     Some(GlobalAlloc::Static(def_id)) if is_thread_local(def_id) => {
518                         ptr.alloc_id = this.get_or_create_thread_local_alloc_id(def_id)?;
519                     }
520                     _ => {}
521                 }
522             }
523             _ => {
524                 // FIXME: Handling only `Scalar` seems to work for now, but at
525                 // least in principle thread-locals could be in any constant, so
526                 // we should also consider other cases. However, once
527                 // https://github.com/rust-lang/rust/issues/70685 gets fixed,
528                 // this code will have to be rewritten anyway.
529             }
530         }
531         Ok(())
532     }
533
534     /// Get a thread-specific allocation id for the given thread-local static.
535     /// If needed, allocate a new one.
536     ///
537     /// FIXME: This method should be replaced as soon as
538     /// https://github.com/rust-lang/rust/issues/70685 gets fixed.
539     fn get_or_create_thread_local_alloc_id(&self, def_id: DefId) -> InterpResult<'tcx, AllocId> {
540         let this = self.eval_context_ref();
541         let tcx = this.tcx;
542         if let Some(new_alloc_id) = this.machine.threads.get_thread_local_alloc_id(def_id) {
543             // We already have a thread-specific allocation id for this
544             // thread-local static.
545             Ok(new_alloc_id)
546         } else {
547             // We need to allocate a thread-specific allocation id for this
548             // thread-local static.
549             //
550             // At first, we invoke the `const_eval_raw` query and extract the
551             // allocation from it. Unfortunately, we have to duplicate the code
552             // from `Memory::get_global_alloc` that does this.
553             //
554             // Then we store the retrieved allocation back into the `alloc_map`
555             // to get a fresh allocation id, which we can use as a
556             // thread-specific allocation id for the thread-local static.
557             if tcx.is_foreign_item(def_id) {
558                 throw_unsup_format!("foreign thread-local statics are not supported");
559             }
560             // Invoke the `const_eval_raw` query.
561             let instance = Instance::mono(tcx.tcx, def_id);
562             let gid = GlobalId { instance, promoted: None };
563             let raw_const =
564                 tcx.const_eval_raw(ty::ParamEnv::reveal_all().and(gid)).map_err(|err| {
565                     // no need to report anything, the const_eval call takes care of that
566                     // for statics
567                     assert!(tcx.is_static(def_id));
568                     err
569                 })?;
570             let id = raw_const.alloc_id;
571             // Extract the allocation from the query result.
572             let allocation = tcx.global_alloc(id).unwrap_memory();
573             // Create a new allocation id for the same allocation in this hacky
574             // way. Internally, `alloc_map` deduplicates allocations, but this
575             // is fine because Miri will make a copy before a first mutable
576             // access.
577             let new_alloc_id = tcx.create_memory_alloc(allocation);
578             this.machine.threads.set_thread_local_alloc_id(def_id, new_alloc_id);
579             Ok(new_alloc_id)
580         }
581     }
582
583     #[inline]
584     fn create_thread(&mut self) -> InterpResult<'tcx, ThreadId> {
585         let this = self.eval_context_mut();
586         Ok(this.machine.threads.create_thread())
587     }
588
589     #[inline]
590     fn detach_thread(&mut self, thread_id: ThreadId) -> InterpResult<'tcx> {
591         let this = self.eval_context_mut();
592         this.machine.threads.detach_thread(thread_id)
593     }
594
595     #[inline]
596     fn join_thread(&mut self, joined_thread_id: ThreadId) -> InterpResult<'tcx> {
597         let this = self.eval_context_mut();
598         this.machine.threads.join_thread(joined_thread_id)
599     }
600
601     #[inline]
602     fn set_active_thread(&mut self, thread_id: ThreadId) -> InterpResult<'tcx, ThreadId> {
603         let this = self.eval_context_mut();
604         Ok(this.machine.threads.set_active_thread_id(thread_id))
605     }
606
607     #[inline]
608     fn get_active_thread(&self) -> InterpResult<'tcx, ThreadId> {
609         let this = self.eval_context_ref();
610         Ok(this.machine.threads.get_active_thread_id())
611     }
612
613     #[inline]
614     fn get_total_thread_count(&self) -> InterpResult<'tcx, usize> {
615         let this = self.eval_context_ref();
616         Ok(this.machine.threads.get_total_thread_count())
617     }
618
619     #[inline]
620     fn has_terminated(&self, thread_id: ThreadId) -> InterpResult<'tcx, bool> {
621         let this = self.eval_context_ref();
622         Ok(this.machine.threads.has_terminated(thread_id))
623     }
624
625     #[inline]
626     fn enable_thread(&mut self, thread_id: ThreadId) -> InterpResult<'tcx> {
627         let this = self.eval_context_mut();
628         this.machine.threads.enable_thread(thread_id);
629         Ok(())
630     }
631
632     #[inline]
633     fn active_thread_stack(&self) -> &[Frame<'mir, 'tcx, Tag, FrameData<'tcx>>] {
634         let this = self.eval_context_ref();
635         this.machine.threads.active_thread_stack()
636     }
637
638     #[inline]
639     fn active_thread_stack_mut(&mut self) -> &mut Vec<Frame<'mir, 'tcx, Tag, FrameData<'tcx>>> {
640         let this = self.eval_context_mut();
641         this.machine.threads.active_thread_stack_mut()
642     }
643
644     #[inline]
645     fn set_active_thread_name(&mut self, new_thread_name: Vec<u8>) -> InterpResult<'tcx, ()> {
646         let this = self.eval_context_mut();
647         Ok(this.machine.threads.set_thread_name(new_thread_name))
648     }
649
650     #[inline]
651     fn get_active_thread_name<'c>(&'c self) -> InterpResult<'tcx, &'c [u8]>
652     where
653         'mir: 'c,
654     {
655         let this = self.eval_context_ref();
656         Ok(this.machine.threads.get_thread_name())
657     }
658
659     #[inline]
660     fn block_thread(&mut self, thread: ThreadId) -> InterpResult<'tcx> {
661         let this = self.eval_context_mut();
662         Ok(this.machine.threads.block_thread(thread))
663     }
664
665     #[inline]
666     fn unblock_thread(&mut self, thread: ThreadId) -> InterpResult<'tcx> {
667         let this = self.eval_context_mut();
668         Ok(this.machine.threads.unblock_thread(thread))
669     }
670
671     #[inline]
672     fn yield_active_thread(&mut self) -> InterpResult<'tcx> {
673         let this = self.eval_context_mut();
674         this.machine.threads.yield_active_thread();
675         Ok(())
676     }
677
678     #[inline]
679     fn register_timeout_callback(
680         &mut self,
681         thread: ThreadId,
682         call_time: Time,
683         callback: TimeoutCallback<'mir, 'tcx>,
684     ) -> InterpResult<'tcx> {
685         let this = self.eval_context_mut();
686         this.machine.threads.register_timeout_callback(thread, call_time, callback);
687         Ok(())
688     }
689
690     #[inline]
691     fn unregister_timeout_callback_if_exists(&mut self, thread: ThreadId) -> InterpResult<'tcx> {
692         let this = self.eval_context_mut();
693         this.machine.threads.unregister_timeout_callback_if_exists(thread);
694         Ok(())
695     }
696
697     /// Execute a timeout callback on the callback's thread.
698     #[inline]
699     fn run_timeout_callback(&mut self) -> InterpResult<'tcx> {
700         let this = self.eval_context_mut();
701         let (thread, callback) =
702             this.machine.threads.get_ready_callback().expect("no callback found");
703         // This back-and-forth with `set_active_thread` is here because of two
704         // design decisions:
705         // 1. Make the caller and not the callback responsible for changing
706         //    thread.
707         // 2. Make the scheduler the only place that can change the active
708         //    thread.
709         let old_thread = this.set_active_thread(thread)?;
710         callback(this)?;
711         this.set_active_thread(old_thread)?;
712         Ok(())
713     }
714
715     /// Decide which action to take next and on which thread.
716     #[inline]
717     fn schedule(&mut self) -> InterpResult<'tcx, SchedulingAction> {
718         let this = self.eval_context_mut();
719         this.machine.threads.schedule()
720     }
721 }