1use std::cell::RefCell;
2use std::collections::VecDeque;
3use std::collections::hash_map::Entry;
4use std::default::Default;
5use std::ops::Not;
6use std::rc::Rc;
7
8use rustc_data_structures::fx::FxHashMap;
9
10use super::vector_clock::VClock;
11use crate::*;
12
13#[derive(Default, Debug)]
15struct Mutex {
16 owner: Option<ThreadId>,
18 lock_count: usize,
20 queue: VecDeque<ThreadId>,
22 clock: VClock,
24}
25
26#[derive(Default, Clone, Debug)]
27pub struct MutexRef(Rc<RefCell<Mutex>>);
28
29impl MutexRef {
30 pub fn new() -> Self {
31 Self(Default::default())
32 }
33
34 pub fn owner(&self) -> Option<ThreadId> {
36 self.0.borrow().owner
37 }
38
39 pub fn queue_is_empty(&self) -> bool {
40 self.0.borrow().queue.is_empty()
41 }
42}
43
44impl VisitProvenance for MutexRef {
45 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
47}
48
49#[derive(Default, Debug)]
51struct RwLock {
52 writer: Option<ThreadId>,
54 readers: FxHashMap<ThreadId, usize>,
57 writer_queue: VecDeque<ThreadId>,
59 reader_queue: VecDeque<ThreadId>,
61 clock_unlocked: VClock,
70 clock_current_readers: VClock,
81}
82
83impl RwLock {
84 #[inline]
85 fn is_locked(&self) -> bool {
87 trace!(
88 "rwlock_is_locked: writer is {:?} and there are {} reader threads (some of which could hold multiple read locks)",
89 self.writer,
90 self.readers.len(),
91 );
92 self.writer.is_some() || self.readers.is_empty().not()
93 }
94
95 #[inline]
97 fn is_write_locked(&self) -> bool {
98 trace!("rwlock_is_write_locked: writer is {:?}", self.writer);
99 self.writer.is_some()
100 }
101}
102
103#[derive(Default, Clone, Debug)]
104pub struct RwLockRef(Rc<RefCell<RwLock>>);
105
106impl RwLockRef {
107 pub fn new() -> Self {
108 Self(Default::default())
109 }
110
111 pub fn is_locked(&self) -> bool {
112 self.0.borrow().is_locked()
113 }
114
115 pub fn is_write_locked(&self) -> bool {
116 self.0.borrow().is_write_locked()
117 }
118
119 pub fn queue_is_empty(&self) -> bool {
120 let inner = self.0.borrow();
121 inner.reader_queue.is_empty() && inner.writer_queue.is_empty()
122 }
123}
124
125impl VisitProvenance for RwLockRef {
126 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
128}
129
130#[derive(Default, Debug)]
132struct Condvar {
133 waiters: VecDeque<ThreadId>,
134 clock: VClock,
140}
141
142#[derive(Default, Clone, Debug)]
143pub struct CondvarRef(Rc<RefCell<Condvar>>);
144
145impl CondvarRef {
146 pub fn new() -> Self {
147 Self(Default::default())
148 }
149
150 pub fn queue_is_empty(&self) -> bool {
151 self.0.borrow().waiters.is_empty()
152 }
153}
154
155impl VisitProvenance for CondvarRef {
156 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
158}
159
160#[derive(Default, Debug)]
162struct Semaphore {
163 waiters: VecDeque<ThreadId>,
164 value: u64,
165 clock: VClock,
167}
168
169#[derive(Default, Clone, Debug)]
170pub struct SemaphoreRef(Rc<RefCell<Semaphore>>);
171
172impl SemaphoreRef {
173 pub fn new(value: u64) -> Self {
174 Self(Rc::new(RefCell::new(Semaphore { value, ..Default::default() })))
175 }
176
177 pub fn value(&self) -> u64 {
178 self.0.borrow().value
179 }
180
181 pub fn queue_is_empty(&self) -> bool {
182 self.0.borrow().waiters.is_empty()
183 }
184}
185
186impl VisitProvenance for SemaphoreRef {
187 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
189}
190
191#[derive(Default, Debug)]
193struct Futex {
194 waiters: Vec<FutexWaiter>,
195 clock: VClock,
201}
202
203#[derive(Default, Clone, Debug)]
204pub struct FutexRef(Rc<RefCell<Futex>>);
205
206impl FutexRef {
207 pub fn new() -> Self {
208 Self(Default::default())
209 }
210
211 pub fn waiters(&self) -> usize {
212 self.0.borrow().waiters.len()
213 }
214}
215
216impl VisitProvenance for FutexRef {
217 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
219}
220
221#[derive(Debug)]
223struct FutexWaiter {
224 thread: ThreadId,
226 bitset: u32,
228}
229
230impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
232pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
233 fn condvar_reacquire_mutex(
234 &mut self,
235 mutex_ref: MutexRef,
236 retval: Scalar,
237 dest: MPlaceTy<'tcx>,
238 ) -> InterpResult<'tcx> {
239 let ecx = self.eval_context_mut();
240 if let Some(owner) = mutex_ref.owner() {
241 assert_ne!(owner, ecx.active_thread());
242 ecx.mutex_enqueue_and_block(mutex_ref, Some((retval, dest)));
243 } else {
244 ecx.mutex_lock(&mutex_ref)?;
246 ecx.write_scalar(retval, &dest)?;
248 }
249 interp_ok(())
250 }
251}
252
253impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
258pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
259 fn mutex_lock(&self, mutex_ref: &MutexRef) -> InterpResult<'tcx> {
261 let ecx = self.eval_context_ref();
262 let thread = ecx.active_thread();
263 let mut mutex = mutex_ref.0.borrow_mut();
264 if let Some(current_owner) = mutex.owner {
265 assert_eq!(thread, current_owner, "mutex already locked by another thread");
266 assert!(
267 mutex.lock_count > 0,
268 "invariant violation: lock_count == 0 iff the thread is unlocked"
269 );
270 } else {
271 mutex.owner = Some(thread);
272 }
273 mutex.lock_count = mutex.lock_count.strict_add(1);
274 ecx.acquire_clock(&mutex.clock)?;
275 interp_ok(())
276 }
277
278 fn mutex_unlock(&mut self, mutex_ref: &MutexRef) -> InterpResult<'tcx, Option<usize>> {
283 let ecx = self.eval_context_mut();
284 let mut mutex = mutex_ref.0.borrow_mut();
285 interp_ok(if let Some(current_owner) = mutex.owner {
286 if current_owner != ecx.machine.threads.active_thread() {
288 return interp_ok(None);
290 }
291 let old_lock_count = mutex.lock_count;
292 mutex.lock_count = old_lock_count.strict_sub(1);
293 if mutex.lock_count == 0 {
294 mutex.owner = None;
295 ecx.release_clock(|clock| mutex.clock.clone_from(clock))?;
299 let thread_id = mutex.queue.pop_front();
300 drop(mutex);
303 if let Some(thread_id) = thread_id {
304 ecx.unblock_thread(thread_id, BlockReason::Mutex)?;
305 }
306 }
307 Some(old_lock_count)
308 } else {
309 None
311 })
312 }
313
314 #[inline]
319 fn mutex_enqueue_and_block(
320 &mut self,
321 mutex_ref: MutexRef,
322 retval_dest: Option<(Scalar, MPlaceTy<'tcx>)>,
323 ) {
324 let ecx = self.eval_context_mut();
325 let thread = ecx.active_thread();
326 let mut mutex = mutex_ref.0.borrow_mut();
327 mutex.queue.push_back(thread);
328 assert!(mutex.owner.is_some(), "queuing on unlocked mutex");
329 drop(mutex);
330 ecx.block_thread(
331 BlockReason::Mutex,
332 None,
333 callback!(
334 @capture<'tcx> {
335 mutex_ref: MutexRef,
336 retval_dest: Option<(Scalar, MPlaceTy<'tcx>)>,
337 }
338 |ecx, unblock: UnblockKind| {
339 assert_eq!(unblock, UnblockKind::Ready);
340
341 assert!(mutex_ref.owner().is_none());
342 ecx.mutex_lock(&mutex_ref)?;
343
344 if let Some((retval, dest)) = retval_dest {
345 ecx.write_scalar(retval, &dest)?;
346 }
347
348 interp_ok(())
349 }
350 ),
351 );
352 }
353
354 fn rwlock_reader_lock(&self, rwlock_ref: &RwLockRef) -> InterpResult<'tcx> {
357 let ecx = self.eval_context_ref();
358 let thread = ecx.active_thread();
359 trace!("rwlock_reader_lock: now also held (one more time) by {:?}", thread);
360 let mut rwlock = rwlock_ref.0.borrow_mut();
361 assert!(!rwlock.is_write_locked(), "the lock is write locked");
362 let count = rwlock.readers.entry(thread).or_insert(0);
363 *count = count.strict_add(1);
364 ecx.acquire_clock(&rwlock.clock_unlocked)?;
365 interp_ok(())
366 }
367
368 fn rwlock_reader_unlock(&mut self, rwlock_ref: &RwLockRef) -> InterpResult<'tcx, bool> {
371 let ecx = self.eval_context_mut();
372 let thread = ecx.active_thread();
373 let mut rwlock = rwlock_ref.0.borrow_mut();
374 match rwlock.readers.entry(thread) {
375 Entry::Occupied(mut entry) => {
376 let count = entry.get_mut();
377 assert!(*count > 0, "rwlock locked with count == 0");
378 *count -= 1;
379 if *count == 0 {
380 trace!("rwlock_reader_unlock: no longer held by {:?}", thread);
381 entry.remove();
382 } else {
383 trace!("rwlock_reader_unlock: held one less time by {:?}", thread);
384 }
385 }
386 Entry::Vacant(_) => return interp_ok(false), }
388 ecx.release_clock(|clock| rwlock.clock_current_readers.join(clock))?;
390
391 if rwlock.is_locked().not() {
393 let rwlock_ref = &mut *rwlock;
397 rwlock_ref.clock_unlocked.clone_from(&rwlock_ref.clock_current_readers);
398 if let Some(writer) = rwlock_ref.writer_queue.pop_front() {
400 drop(rwlock); ecx.unblock_thread(writer, BlockReason::RwLock)?;
402 }
403 }
404 interp_ok(true)
405 }
406
407 #[inline]
410 fn rwlock_enqueue_and_block_reader(
411 &mut self,
412 rwlock_ref: RwLockRef,
413 retval: Scalar,
414 dest: MPlaceTy<'tcx>,
415 ) {
416 let ecx = self.eval_context_mut();
417 let thread = ecx.active_thread();
418 let mut rwlock = rwlock_ref.0.borrow_mut();
419 rwlock.reader_queue.push_back(thread);
420 assert!(rwlock.is_write_locked(), "read-queueing on not write locked rwlock");
421 drop(rwlock);
422 ecx.block_thread(
423 BlockReason::RwLock,
424 None,
425 callback!(
426 @capture<'tcx> {
427 rwlock_ref: RwLockRef,
428 retval: Scalar,
429 dest: MPlaceTy<'tcx>,
430 }
431 |ecx, unblock: UnblockKind| {
432 assert_eq!(unblock, UnblockKind::Ready);
433 ecx.rwlock_reader_lock(&rwlock_ref)?;
434 ecx.write_scalar(retval, &dest)?;
435 interp_ok(())
436 }
437 ),
438 );
439 }
440
441 #[inline]
443 fn rwlock_writer_lock(&self, rwlock_ref: &RwLockRef) -> InterpResult<'tcx> {
444 let ecx = self.eval_context_ref();
445 let thread = ecx.active_thread();
446 trace!("rwlock_writer_lock: now held by {:?}", thread);
447
448 let mut rwlock = rwlock_ref.0.borrow_mut();
449 assert!(!rwlock.is_locked(), "the rwlock is already locked");
450 rwlock.writer = Some(thread);
451 ecx.acquire_clock(&rwlock.clock_unlocked)?;
452 interp_ok(())
453 }
454
455 #[inline]
458 fn rwlock_writer_unlock(&mut self, rwlock_ref: &RwLockRef) -> InterpResult<'tcx, bool> {
459 let ecx = self.eval_context_mut();
460 let thread = ecx.active_thread();
461 let mut rwlock = rwlock_ref.0.borrow_mut();
462 interp_ok(if let Some(current_writer) = rwlock.writer {
463 if current_writer != thread {
464 return interp_ok(false);
466 }
467 rwlock.writer = None;
468 trace!("rwlock_writer_unlock: unlocked by {:?}", thread);
469 ecx.release_clock(|clock| rwlock.clock_unlocked.clone_from(clock))?;
471
472 if let Some(writer) = rwlock.writer_queue.pop_front() {
478 drop(rwlock); ecx.unblock_thread(writer, BlockReason::RwLock)?;
480 } else {
481 let readers = std::mem::take(&mut rwlock.reader_queue);
483 drop(rwlock); for reader in readers {
485 ecx.unblock_thread(reader, BlockReason::RwLock)?;
486 }
487 }
488 true
489 } else {
490 false
491 })
492 }
493
494 #[inline]
497 fn rwlock_enqueue_and_block_writer(
498 &mut self,
499 rwlock_ref: RwLockRef,
500 retval: Scalar,
501 dest: MPlaceTy<'tcx>,
502 ) {
503 let ecx = self.eval_context_mut();
504 let thread = ecx.active_thread();
505 let mut rwlock = rwlock_ref.0.borrow_mut();
506 rwlock.writer_queue.push_back(thread);
507 assert!(rwlock.is_locked(), "write-queueing on unlocked rwlock");
508 drop(rwlock);
509 ecx.block_thread(
510 BlockReason::RwLock,
511 None,
512 callback!(
513 @capture<'tcx> {
514 rwlock_ref: RwLockRef,
515 retval: Scalar,
516 dest: MPlaceTy<'tcx>,
517 }
518 |ecx, unblock: UnblockKind| {
519 assert_eq!(unblock, UnblockKind::Ready);
520 ecx.rwlock_writer_lock(&rwlock_ref)?;
521 ecx.write_scalar(retval, &dest)?;
522 interp_ok(())
523 }
524 ),
525 );
526 }
527
528 fn condvar_wait(
532 &mut self,
533 condvar_ref: CondvarRef,
534 mutex_ref: MutexRef,
535 deadline: Option<Deadline>,
536 retval_succ: Scalar,
537 retval_timeout: Scalar,
538 dest: MPlaceTy<'tcx>,
539 ) -> InterpResult<'tcx> {
540 let ecx = self.eval_context_mut();
541 if let Some(old_locked_count) = ecx.mutex_unlock(&mutex_ref)? {
542 if old_locked_count != 1 {
543 throw_unsup_format!(
544 "awaiting a condvar on a mutex acquired multiple times is not supported"
545 );
546 }
547 } else {
548 throw_ub_format!(
549 "awaiting a condvar on a mutex that is unlocked or owned by a different thread"
550 );
551 }
552 let thread = ecx.active_thread();
553
554 condvar_ref.0.borrow_mut().waiters.push_back(thread);
555 ecx.block_thread(
556 BlockReason::Condvar,
557 deadline,
558 callback!(
559 @capture<'tcx> {
560 condvar_ref: CondvarRef,
561 mutex_ref: MutexRef,
562 retval_succ: Scalar,
563 retval_timeout: Scalar,
564 dest: MPlaceTy<'tcx>,
565 }
566 |ecx, unblock: UnblockKind| {
567 match unblock {
568 UnblockKind::Ready => {
569 ecx.acquire_clock(
571 &condvar_ref.0.borrow().clock,
572 )?;
573 ecx.condvar_reacquire_mutex(mutex_ref, retval_succ, dest)
576 }
577 UnblockKind::TimedOut => {
578 let thread = ecx.active_thread();
580 let waiters = &mut condvar_ref.0.borrow_mut().waiters;
581 waiters.retain(|waiter| *waiter != thread);
582 ecx.condvar_reacquire_mutex(mutex_ref, retval_timeout, dest)
584 }
585 }
586 }
587 ),
588 );
589 interp_ok(())
590 }
591
592 fn condvar_signal(&mut self, condvar_ref: &CondvarRef) -> InterpResult<'tcx, bool> {
595 let ecx = self.eval_context_mut();
596 let mut condvar = condvar_ref.0.borrow_mut();
597
598 ecx.release_clock(|clock| condvar.clock.clone_from(clock))?;
600 let Some(waiter) = condvar.waiters.pop_front() else {
601 return interp_ok(false);
602 };
603 drop(condvar);
604 ecx.unblock_thread(waiter, BlockReason::Condvar)?;
605 interp_ok(true)
606 }
607
608 fn futex_wait(
611 &mut self,
612 futex_ref: FutexRef,
613 bitset: u32,
614 deadline: Option<Deadline>,
615 callback: DynUnblockCallback<'tcx>,
616 ) {
617 let ecx = self.eval_context_mut();
618 let thread = ecx.active_thread();
619 let mut futex = futex_ref.0.borrow_mut();
620 let waiters = &mut futex.waiters;
621 assert!(waiters.iter().all(|waiter| waiter.thread != thread), "thread is already waiting");
622 waiters.push(FutexWaiter { thread, bitset });
623 drop(futex);
624
625 ecx.block_thread(
626 BlockReason::Futex,
627 deadline,
628 callback!(
629 @capture<'tcx> {
630 futex_ref: FutexRef,
631 callback: DynUnblockCallback<'tcx>,
632 }
633 |ecx, unblock: UnblockKind| {
634 match unblock {
635 UnblockKind::Ready => {
636 let futex = futex_ref.0.borrow();
637 ecx.acquire_clock(&futex.clock)?;
639 },
640 UnblockKind::TimedOut => {
641 let thread = ecx.active_thread();
643 let mut futex = futex_ref.0.borrow_mut();
644 futex.waiters.retain(|waiter| waiter.thread != thread);
645 },
646 }
647
648 callback.call(ecx, unblock)
649 }
650 ),
651 );
652 }
653
654 fn futex_wake(
657 &mut self,
658 futex_ref: &FutexRef,
659 bitset: u32,
660 count: usize,
661 ) -> InterpResult<'tcx, usize> {
662 let ecx = self.eval_context_mut();
663 let mut futex = futex_ref.0.borrow_mut();
664
665 ecx.release_clock(|clock| futex.clock.clone_from(clock))?;
667
668 let wake_threads: Vec<_> =
672 futex.waiters.extract_if(.., |w| w.bitset & bitset != 0).take(count).collect();
673 drop(futex);
674
675 let woken = wake_threads.len();
676 for waiter in wake_threads {
677 ecx.unblock_thread(waiter.thread, BlockReason::Futex)?;
678 }
679
680 interp_ok(woken)
681 }
682
683 fn semaphore_decrement(
689 &mut self,
690 semaphore_ref: SemaphoreRef,
691 deadline: Option<Deadline>,
692 callback: DynUnblockCallback<'tcx>,
693 ) -> InterpResult<'tcx> {
694 let ecx = self.eval_context_mut();
695
696 let mut semaphore = semaphore_ref.0.borrow_mut();
697 if let Some(new_value) = semaphore.value.checked_sub(1) {
698 semaphore.value = new_value;
699 ecx.acquire_clock(&semaphore.clock)?;
700 drop(semaphore);
701 return callback.call(ecx, UnblockKind::Ready);
702 }
703
704 semaphore.waiters.push_back(ecx.active_thread());
706 drop(semaphore);
707
708 ecx.block_thread(
709 BlockReason::Semaphore,
710 deadline.clone(),
711 callback!(
712 @capture<'tcx> {
713 semaphore_ref: SemaphoreRef,
714 deadline: Option<Deadline>,
715 callback: DynUnblockCallback<'tcx>,
716 }
717 |ecx, unblock: UnblockKind| {
718 match unblock {
719 UnblockKind::TimedOut => {
720 let thread = ecx.active_thread();
722 let mut semaphore = semaphore_ref.0.borrow_mut();
723 semaphore.waiters.retain(|&waiter| waiter != thread);
724 return callback.call(ecx, UnblockKind::TimedOut);
726 }
727 UnblockKind::Ready => {
728 ecx.semaphore_decrement(semaphore_ref, deadline, callback)
731 }
732 }
733 }
734 ),
735 );
736 interp_ok(())
737 }
738
739 fn semaphore_increment(&mut self, semaphore_ref: &SemaphoreRef) -> InterpResult<'tcx, bool> {
742 let ecx = self.eval_context_mut();
743
744 let mut semaphore = semaphore_ref.0.borrow_mut();
745 let wake_thread =
747 (semaphore.value == 0).then_some(()).and_then(|()| semaphore.waiters.pop_front());
748 let Some(new_value) = semaphore.value.checked_add(1) else {
750 throw_unsup_format!("the counter of a semaphore overflowed")
751 };
752 semaphore.value = new_value;
753 ecx.release_clock(|clock| semaphore.clock.clone_from(clock))?;
755 drop(semaphore);
756
757 if let Some(thread) = wake_thread {
758 ecx.unblock_thread(thread, BlockReason::Semaphore)?;
759 }
760
761 interp_ok(wake_thread.is_some())
762 }
763}