1 //! Implements threads.
3 use std::cell::RefCell;
4 use std::collections::hash_map::Entry;
8 use rustc_data_structures::fx::FxHashMap;
9 use rustc_index::vec::{Idx, IndexVec};
10 use rustc_middle::mir;
15 /// A thread identifier.
16 #[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
17 pub struct ThreadId(usize);
19 impl Idx for ThreadId {
20 fn new(idx: usize) -> Self {
23 fn index(self) -> usize {
28 impl From<u64> for ThreadId {
29 fn from(id: u64) -> Self {
34 /// The state of a thread.
35 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
36 pub enum ThreadState {
37 /// The thread is enabled and can be executed.
39 /// The thread tried to join the specified thread and is blocked until that
40 /// thread terminates.
42 /// The thread has terminated its execution (we do not delete terminated
48 pub struct Thread<'mir, 'tcx> {
50 /// The virtual call stack.
51 stack: Vec<Frame<'mir, 'tcx, Tag, FrameData<'tcx>>>,
52 /// Is the thread detached?
54 /// A thread is detached if its join handle was destroyed and no other
55 /// thread can join it.
59 impl<'mir, 'tcx> Thread<'mir, 'tcx> {
60 /// Check if the thread terminated. If yes, change the state to terminated
61 /// and return `true`.
62 fn check_terminated(&mut self) -> bool {
63 if self.state == ThreadState::Enabled {
64 if self.stack.is_empty() {
65 self.state = ThreadState::Terminated;
73 impl<'mir, 'tcx> std::fmt::Debug for Thread<'mir, 'tcx> {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 write!(f, "{:?}", self.state)
79 impl<'mir, 'tcx> Default for Thread<'mir, 'tcx> {
80 fn default() -> Self {
81 Self { state: ThreadState::Enabled, stack: Vec::new(), detached: false }
87 pub struct ThreadSet<'mir, 'tcx> {
88 /// Identifier of the currently active thread.
89 active_thread: ThreadId,
90 /// Threads used in the program.
92 /// Note that this vector also contains terminated threads.
93 threads: IndexVec<ThreadId, Thread<'mir, 'tcx>>,
96 impl<'mir, 'tcx> Default for ThreadSet<'mir, 'tcx> {
97 fn default() -> Self {
98 let mut threads = IndexVec::new();
99 threads.push(Default::default());
101 active_thread: ThreadId::new(0),
107 impl<'mir, 'tcx: 'mir> ThreadSet<'mir, 'tcx> {
108 /// Borrow the stack of the active thread.
109 fn active_thread_stack(&self) -> &[Frame<'mir, 'tcx, Tag, FrameData<'tcx>>] {
110 &self.threads[self.active_thread].stack
112 /// Mutably borrow the stack of the active thread.
113 fn active_thread_stack_mut(&mut self) -> &mut Vec<Frame<'mir, 'tcx, Tag, FrameData<'tcx>>> {
114 &mut self.threads[self.active_thread].stack
116 /// Create a new thread and returns its id.
117 fn create_thread(&mut self) -> ThreadId {
118 let new_thread_id = ThreadId::new(self.threads.len());
119 self.threads.push(Default::default());
122 /// Set an active thread and return the id of the thread that was active before.
123 fn set_active_thread(&mut self, id: ThreadId) -> ThreadId {
124 let active_thread_id = self.active_thread;
125 self.active_thread = id;
126 assert!(self.active_thread.index() < self.threads.len());
129 /// Get the id of the currently active thread.
130 fn get_active_thread(&self) -> ThreadId {
133 /// Mark the thread as detached, which means that no other thread will try
134 /// to join it and the thread is responsible for cleaning up.
135 fn detach_thread(&mut self, id: ThreadId) {
136 self.threads[id].detached = true;
138 /// Mark that the active thread tries to join the thread with `joined_thread_id`.
139 fn join_thread(&mut self, joined_thread_id: ThreadId) {
140 assert!(!self.threads[joined_thread_id].detached, "Bug: trying to join a detached thread.");
141 assert_ne!(joined_thread_id, self.active_thread, "Bug: trying to join itself");
145 .all(|thread| thread.state != ThreadState::Blocked(joined_thread_id)),
146 "Bug: multiple threads try to join the same thread."
148 if self.threads[joined_thread_id].state != ThreadState::Terminated {
149 // The joined thread is still running, we need to wait for it.
150 self.threads[self.active_thread].state = ThreadState::Blocked(joined_thread_id);
152 "{:?} blocked on {:?} when trying to join",
158 /// Get ids of all threads ever allocated.
159 fn get_all_thread_ids(&mut self) -> Vec<ThreadId> {
160 (0..self.threads.len()).map(ThreadId::new).collect()
162 /// Decide which thread to run next.
164 /// Returns `false` if all threads terminated.
165 fn schedule(&mut self) -> InterpResult<'tcx, bool> {
166 if self.threads[self.active_thread].check_terminated() {
167 // Check if we need to unblock any threads.
168 for (i, thread) in self.threads.iter_enumerated_mut() {
169 if thread.state == ThreadState::Blocked(self.active_thread) {
170 trace!("unblocking {:?} because {:?} terminated", i, self.active_thread);
171 thread.state = ThreadState::Enabled;
175 if self.threads[self.active_thread].state == ThreadState::Enabled {
178 if let Some(enabled_thread) =
179 self.threads.iter().position(|thread| thread.state == ThreadState::Enabled)
181 self.active_thread = ThreadId::new(enabled_thread);
184 if self.threads.iter().all(|thread| thread.state == ThreadState::Terminated) {
187 throw_machine_stop!(TerminationInfo::Abort(Some(format!("execution deadlocked"))))
192 /// In Rust, a thread local variable is just a specially marked static. To
193 /// ensure a property that each memory allocation has a globally unique
194 /// allocation identifier, we create a fresh allocation id for each thread. This
195 /// data structure keeps the track of the created allocation identifiers and
196 /// their relation to the original static allocations.
197 #[derive(Clone, Debug, Default)]
198 pub struct ThreadLocalStorage {
199 /// A map from a thread local allocation identifier to the static from which
201 thread_local_origin: RefCell<FxHashMap<AllocId, AllocId>>,
202 /// A map from a thread local static and thread id to the unique thread
203 /// local allocation.
204 thread_local_allocations: RefCell<FxHashMap<(AllocId, ThreadId), AllocId>>,
205 /// The currently active thread.
206 active_thread: Option<ThreadId>,
209 impl ThreadLocalStorage {
210 /// For static allocation identifier `original_id` get a thread local
211 /// allocation identifier. If it is not allocated yet, allocate.
212 pub fn get_or_register_allocation(&self, tcx: ty::TyCtxt<'_>, original_id: AllocId) -> AllocId {
214 .thread_local_allocations
216 .entry((original_id, self.active_thread.unwrap()))
218 Entry::Occupied(entry) => *entry.get(),
219 Entry::Vacant(entry) => {
220 let fresh_id = tcx.alloc_map.lock().reserve();
221 entry.insert(fresh_id);
222 self.thread_local_origin.borrow_mut().insert(fresh_id, original_id);
224 "get_or_register_allocation(original_id={:?}) -> {:?}",
232 /// For thread local allocation identifier `alloc_id`, retrieve the original
233 /// static allocation identifier from which it was created.
234 pub fn resolve_allocation<'tcx>(
236 tcx: ty::TyCtxt<'tcx>,
238 ) -> Option<mir::interpret::GlobalAlloc<'tcx>> {
239 trace!("resolve_allocation(alloc_id: {:?})", alloc_id);
240 if let Some(original_id) = self.thread_local_origin.borrow().get(&alloc_id) {
241 trace!("resolve_allocation(alloc_id: {:?}) -> {:?}", alloc_id, original_id);
242 tcx.alloc_map.lock().get(*original_id)
244 tcx.alloc_map.lock().get(alloc_id)
247 /// Set which thread is currently active.
248 fn set_active_thread(&mut self, active_thread: ThreadId) {
249 self.active_thread = Some(active_thread);
253 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
254 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
255 fn create_thread(&mut self) -> InterpResult<'tcx, ThreadId> {
256 let this = self.eval_context_mut();
257 Ok(this.machine.threads.create_thread())
259 fn detach_thread(&mut self, thread_id: ThreadId) -> InterpResult<'tcx> {
260 let this = self.eval_context_mut();
261 this.machine.threads.detach_thread(thread_id);
264 fn join_thread(&mut self, joined_thread_id: ThreadId) -> InterpResult<'tcx> {
265 let this = self.eval_context_mut();
266 this.machine.threads.join_thread(joined_thread_id);
269 fn set_active_thread(&mut self, thread_id: ThreadId) -> InterpResult<'tcx, ThreadId> {
270 let this = self.eval_context_mut();
271 this.memory.extra.tls.set_active_thread(thread_id);
272 Ok(this.machine.threads.set_active_thread(thread_id))
274 fn get_active_thread(&self) -> InterpResult<'tcx, ThreadId> {
275 let this = self.eval_context_ref();
276 Ok(this.machine.threads.get_active_thread())
278 fn active_thread_stack(&self) -> &[Frame<'mir, 'tcx, Tag, FrameData<'tcx>>] {
279 let this = self.eval_context_ref();
280 this.machine.threads.active_thread_stack()
282 fn active_thread_stack_mut(&mut self) -> &mut Vec<Frame<'mir, 'tcx, Tag, FrameData<'tcx>>> {
283 let this = self.eval_context_mut();
284 this.machine.threads.active_thread_stack_mut()
286 fn get_all_thread_ids(&mut self) -> Vec<ThreadId> {
287 let this = self.eval_context_mut();
288 this.machine.threads.get_all_thread_ids()
290 /// Decide which thread to run next.
292 /// Returns `false` if all threads terminated.
293 fn schedule(&mut self) -> InterpResult<'tcx, bool> {
294 let this = self.eval_context_mut();
295 // Find the next thread to run.
296 if this.machine.threads.schedule()? {
297 let active_thread = this.machine.threads.get_active_thread();
298 this.memory.extra.tls.set_active_thread(active_thread);