1use std::any::Any;
2use std::cell::RefCell;
3use std::collections::VecDeque;
4use std::collections::hash_map::Entry;
5use std::default::Default;
6use std::ops::Not;
7use std::rc::Rc;
8use std::{fmt, iter};
9
10use rustc_abi::Size;
11use rustc_data_structures::fx::FxHashMap;
12
13use super::vector_clock::VClock;
14use crate::*;
15
16#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
18pub enum AccessKind {
19 Read,
20 Write,
21 Dealloc,
22}
23
24impl fmt::Display for AccessKind {
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 match self {
27 AccessKind::Read => write!(f, "read"),
28 AccessKind::Write => write!(f, "write"),
29 AccessKind::Dealloc => write!(f, "deallocation"),
30 }
31 }
32}
33
34pub trait SyncObj: Any {
36 fn on_access<'tcx>(&self, _access_kind: AccessKind) -> InterpResult<'tcx> {
38 interp_ok(())
39 }
40
41 fn delete_on_write(&self) -> bool {
44 false
45 }
46}
47
48impl dyn SyncObj {
49 #[inline(always)]
50 pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
51 let x: &dyn Any = self;
52 x.downcast_ref()
53 }
54}
55
56impl fmt::Debug for dyn SyncObj {
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 f.debug_struct("SyncObj").finish_non_exhaustive()
59 }
60}
61
62#[derive(Default, Debug)]
64struct Mutex {
65 owner: Option<ThreadId>,
67 lock_count: usize,
69 queue: VecDeque<ThreadId>,
71 clock: VClock,
73}
74
75#[derive(Default, Clone, Debug)]
76pub struct MutexRef(Rc<RefCell<Mutex>>);
77
78impl MutexRef {
79 pub fn new() -> Self {
80 Self(Default::default())
81 }
82
83 pub fn owner(&self) -> Option<ThreadId> {
85 self.0.borrow().owner
86 }
87
88 pub fn queue_is_empty(&self) -> bool {
89 self.0.borrow().queue.is_empty()
90 }
91}
92
93impl VisitProvenance for MutexRef {
94 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
96}
97
98#[derive(Default, Debug)]
100struct RwLock {
101 writer: Option<ThreadId>,
103 readers: FxHashMap<ThreadId, usize>,
106 writer_queue: VecDeque<ThreadId>,
108 reader_queue: VecDeque<ThreadId>,
110 clock_unlocked: VClock,
119 clock_current_readers: VClock,
130}
131
132impl RwLock {
133 #[inline]
134 fn is_locked(&self) -> bool {
136 trace!(
137 "rwlock_is_locked: writer is {:?} and there are {} reader threads (some of which could hold multiple read locks)",
138 self.writer,
139 self.readers.len(),
140 );
141 self.writer.is_some() || self.readers.is_empty().not()
142 }
143
144 #[inline]
146 fn is_write_locked(&self) -> bool {
147 trace!("rwlock_is_write_locked: writer is {:?}", self.writer);
148 self.writer.is_some()
149 }
150}
151
152#[derive(Default, Clone, Debug)]
153pub struct RwLockRef(Rc<RefCell<RwLock>>);
154
155impl RwLockRef {
156 pub fn new() -> Self {
157 Self(Default::default())
158 }
159
160 pub fn is_locked(&self) -> bool {
161 self.0.borrow().is_locked()
162 }
163
164 pub fn is_write_locked(&self) -> bool {
165 self.0.borrow().is_write_locked()
166 }
167
168 pub fn queue_is_empty(&self) -> bool {
169 let inner = self.0.borrow();
170 inner.reader_queue.is_empty() && inner.writer_queue.is_empty()
171 }
172}
173
174impl VisitProvenance for RwLockRef {
175 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
177}
178
179#[derive(Default, Debug)]
181struct Condvar {
182 waiters: VecDeque<ThreadId>,
183 clock: VClock,
189}
190
191#[derive(Default, Clone, Debug)]
192pub struct CondvarRef(Rc<RefCell<Condvar>>);
193
194impl CondvarRef {
195 pub fn new() -> Self {
196 Self(Default::default())
197 }
198
199 pub fn queue_is_empty(&self) -> bool {
200 self.0.borrow().waiters.is_empty()
201 }
202}
203
204impl VisitProvenance for CondvarRef {
205 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
207}
208
209#[derive(Default, Debug)]
211struct Futex {
212 waiters: Vec<FutexWaiter>,
213 clock: VClock,
219}
220
221#[derive(Default, Clone, Debug)]
222pub struct FutexRef(Rc<RefCell<Futex>>);
223
224impl FutexRef {
225 pub fn new() -> Self {
226 Self(Default::default())
227 }
228
229 pub fn waiters(&self) -> usize {
230 self.0.borrow().waiters.len()
231 }
232}
233
234impl VisitProvenance for FutexRef {
235 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
237}
238
239#[derive(Debug)]
241struct FutexWaiter {
242 thread: ThreadId,
244 bitset: u32,
246}
247
248impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
250pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
251 fn condvar_reacquire_mutex(
252 &mut self,
253 mutex_ref: MutexRef,
254 retval: Scalar,
255 dest: MPlaceTy<'tcx>,
256 ) -> InterpResult<'tcx> {
257 let this = self.eval_context_mut();
258 if let Some(owner) = mutex_ref.owner() {
259 assert_ne!(owner, this.active_thread());
260 this.mutex_enqueue_and_block(mutex_ref, Some((retval, dest)));
261 } else {
262 this.mutex_lock(&mutex_ref)?;
264 this.write_scalar(retval, &dest)?;
266 }
267 interp_ok(())
268 }
269}
270
271impl<'tcx> AllocExtra<'tcx> {
272 fn get_sync<T: 'static>(&self, offset: Size) -> Option<&T> {
273 self.sync_objs.get(&offset).and_then(|s| s.downcast_ref::<T>())
274 }
275}
276
277impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
282pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
283 fn get_sync_or_init<'a, T: SyncObj>(
288 &'a mut self,
289 ptr: Pointer,
290 new: impl FnOnce(&'a mut MiriMachine<'tcx>) -> T,
291 ) -> Option<&'a T>
292 where
293 'tcx: 'a,
294 {
295 let this = self.eval_context_mut();
296 if !this.ptr_try_get_alloc_id(ptr, 0).ok().is_some_and(|(alloc_id, offset, ..)| {
297 let info = this.get_alloc_info(alloc_id);
298 info.kind == AllocKind::LiveData && info.mutbl.is_mut() && offset < info.size
299 }) {
300 return None;
301 }
302 let (alloc, offset, _) = this.ptr_get_alloc_id(ptr, 0).unwrap();
304 let (alloc_extra, machine) = this.get_alloc_extra_mut(alloc).unwrap();
305 if alloc_extra.get_sync::<T>(offset).is_none() {
307 let new = new(machine);
308 alloc_extra.sync_objs.insert(offset, Box::new(new));
309 }
310 Some(alloc_extra.get_sync::<T>(offset).unwrap())
311 }
312
313 fn get_immovable_sync_with_static_init<'a, T: SyncObj>(
330 &'a mut self,
331 obj: &MPlaceTy<'tcx>,
332 init_offset: Size,
333 uninit_val: u8,
334 init_val: u8,
335 new_meta_obj: impl FnOnce(&mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, T>,
336 ) -> InterpResult<'tcx, &'a T>
337 where
338 'tcx: 'a,
339 {
340 assert!(init_val != uninit_val);
341 let this = self.eval_context_mut();
342 this.check_ptr_access(
343 obj.ptr(),
344 obj.layout.size,
345 CheckInAllocMsg::Dereferenceable("pointer"),
346 )?;
347 assert!(init_offset < obj.layout.size); let init_field = obj.offset(init_offset, this.machine.layouts.u8, this)?;
349
350 let (alloc, offset, _) = this.ptr_get_alloc_id(init_field.ptr(), 0)?;
351 let (alloc_extra, _machine) = this.get_alloc_extra_mut(alloc)?;
352 if alloc_extra.get_sync::<T>(offset).is_some() {
354 let (alloc_extra, _machine) = this.get_alloc_extra_mut(alloc).unwrap();
355 return interp_ok(alloc_extra.get_sync::<T>(offset).unwrap());
356 }
357
358 let meta_obj = new_meta_obj(this)?;
360 let (old_init, success) = this.atomic_compare_exchange(
361 &init_field,
362 &ImmTy::from_scalar(Scalar::from_u8(uninit_val), this.machine.layouts.u8),
363 Scalar::from_u8(init_val),
364 AtomicRwOrd::Relaxed,
365 AtomicReadOrd::Relaxed,
366 false,
367 )?;
368 if !success {
369 assert_eq!(
371 old_init.to_u8()?,
372 init_val,
373 "`new_meta_obj` should have ensured that this CAS succeeds"
374 );
375 }
376
377 let (alloc_extra, _machine) = this.get_alloc_extra_mut(alloc).unwrap();
378 assert!(meta_obj.delete_on_write());
379 alloc_extra.sync_objs.insert(offset, Box::new(meta_obj));
380 interp_ok(alloc_extra.get_sync::<T>(offset).unwrap())
381 }
382
383 fn init_immovable_sync<'a, T: SyncObj>(
386 &'a mut self,
387 obj: &MPlaceTy<'tcx>,
388 init_offset: Size,
389 init_val: u8,
390 new_meta_obj: T,
391 ) -> InterpResult<'tcx, Option<&'a T>>
392 where
393 'tcx: 'a,
394 {
395 let this = self.eval_context_mut();
396 this.check_ptr_access(
397 obj.ptr(),
398 obj.layout.size,
399 CheckInAllocMsg::Dereferenceable("pointer"),
400 )?;
401 assert!(init_offset < obj.layout.size); let init_field = obj.offset(init_offset, this.machine.layouts.u8, this)?;
403
404 this.write_bytes_ptr(obj.ptr(), iter::repeat_n(0, obj.layout.size.bytes_usize()))?;
406 this.write_scalar(Scalar::from_u8(init_val), &init_field)?;
407
408 let (alloc, offset, _) = this.ptr_get_alloc_id(init_field.ptr(), 0)?;
410 let (alloc_extra, _machine) = this.get_alloc_extra_mut(alloc).unwrap();
411 assert!(new_meta_obj.delete_on_write());
412 alloc_extra.sync_objs.insert(offset, Box::new(new_meta_obj));
413 interp_ok(Some(alloc_extra.get_sync::<T>(offset).unwrap()))
414 }
415
416 fn mutex_lock(&mut self, mutex_ref: &MutexRef) -> InterpResult<'tcx> {
418 let this = self.eval_context_mut();
419 let thread = this.active_thread();
420 let mut mutex = mutex_ref.0.borrow_mut();
421 if let Some(current_owner) = mutex.owner {
422 assert_eq!(thread, current_owner, "mutex already locked by another thread");
423 assert!(
424 mutex.lock_count > 0,
425 "invariant violation: lock_count == 0 iff the thread is unlocked"
426 );
427 } else {
428 mutex.owner = Some(thread);
429 }
430 mutex.lock_count = mutex.lock_count.strict_add(1);
431 this.acquire_clock(&mutex.clock)?;
432 interp_ok(())
433 }
434
435 fn mutex_unlock(&mut self, mutex_ref: &MutexRef) -> InterpResult<'tcx, Option<usize>> {
440 let this = self.eval_context_mut();
441 let mut mutex = mutex_ref.0.borrow_mut();
442 interp_ok(if let Some(current_owner) = mutex.owner {
443 if current_owner != this.machine.threads.active_thread() {
445 return interp_ok(None);
447 }
448 let old_lock_count = mutex.lock_count;
449 mutex.lock_count = old_lock_count.strict_sub(1);
450 if mutex.lock_count == 0 {
451 mutex.owner = None;
452 this.release_clock(|clock| mutex.clock.clone_from(clock))?;
456 let thread_id = mutex.queue.pop_front();
457 drop(mutex);
460 if let Some(thread_id) = thread_id {
461 this.unblock_thread(thread_id, BlockReason::Mutex)?;
462 }
463 }
464 Some(old_lock_count)
465 } else {
466 None
468 })
469 }
470
471 #[inline]
476 fn mutex_enqueue_and_block(
477 &mut self,
478 mutex_ref: MutexRef,
479 retval_dest: Option<(Scalar, MPlaceTy<'tcx>)>,
480 ) {
481 let this = self.eval_context_mut();
482 let thread = this.active_thread();
483 let mut mutex = mutex_ref.0.borrow_mut();
484 mutex.queue.push_back(thread);
485 assert!(mutex.owner.is_some(), "queuing on unlocked mutex");
486 drop(mutex);
487 this.block_thread(
488 BlockReason::Mutex,
489 None,
490 callback!(
491 @capture<'tcx> {
492 mutex_ref: MutexRef,
493 retval_dest: Option<(Scalar, MPlaceTy<'tcx>)>,
494 }
495 |this, unblock: UnblockKind| {
496 assert_eq!(unblock, UnblockKind::Ready);
497
498 assert!(mutex_ref.owner().is_none());
499 this.mutex_lock(&mutex_ref)?;
500
501 if let Some((retval, dest)) = retval_dest {
502 this.write_scalar(retval, &dest)?;
503 }
504
505 interp_ok(())
506 }
507 ),
508 );
509 }
510
511 fn rwlock_reader_lock(&mut self, rwlock_ref: &RwLockRef) -> InterpResult<'tcx> {
514 let this = self.eval_context_mut();
515 let thread = this.active_thread();
516 trace!("rwlock_reader_lock: now also held (one more time) by {:?}", thread);
517 let mut rwlock = rwlock_ref.0.borrow_mut();
518 assert!(!rwlock.is_write_locked(), "the lock is write locked");
519 let count = rwlock.readers.entry(thread).or_insert(0);
520 *count = count.strict_add(1);
521 this.acquire_clock(&rwlock.clock_unlocked)?;
522 interp_ok(())
523 }
524
525 fn rwlock_reader_unlock(&mut self, rwlock_ref: &RwLockRef) -> InterpResult<'tcx, bool> {
528 let this = self.eval_context_mut();
529 let thread = this.active_thread();
530 let mut rwlock = rwlock_ref.0.borrow_mut();
531 match rwlock.readers.entry(thread) {
532 Entry::Occupied(mut entry) => {
533 let count = entry.get_mut();
534 assert!(*count > 0, "rwlock locked with count == 0");
535 *count -= 1;
536 if *count == 0 {
537 trace!("rwlock_reader_unlock: no longer held by {:?}", thread);
538 entry.remove();
539 } else {
540 trace!("rwlock_reader_unlock: held one less time by {:?}", thread);
541 }
542 }
543 Entry::Vacant(_) => return interp_ok(false), }
545 this.release_clock(|clock| rwlock.clock_current_readers.join(clock))?;
547
548 if rwlock.is_locked().not() {
550 let rwlock_ref = &mut *rwlock;
554 rwlock_ref.clock_unlocked.clone_from(&rwlock_ref.clock_current_readers);
555 if let Some(writer) = rwlock_ref.writer_queue.pop_front() {
557 drop(rwlock); this.unblock_thread(writer, BlockReason::RwLock)?;
559 }
560 }
561 interp_ok(true)
562 }
563
564 #[inline]
567 fn rwlock_enqueue_and_block_reader(
568 &mut self,
569 rwlock_ref: RwLockRef,
570 retval: Scalar,
571 dest: MPlaceTy<'tcx>,
572 ) {
573 let this = self.eval_context_mut();
574 let thread = this.active_thread();
575 let mut rwlock = rwlock_ref.0.borrow_mut();
576 rwlock.reader_queue.push_back(thread);
577 assert!(rwlock.is_write_locked(), "read-queueing on not write locked rwlock");
578 drop(rwlock);
579 this.block_thread(
580 BlockReason::RwLock,
581 None,
582 callback!(
583 @capture<'tcx> {
584 rwlock_ref: RwLockRef,
585 retval: Scalar,
586 dest: MPlaceTy<'tcx>,
587 }
588 |this, unblock: UnblockKind| {
589 assert_eq!(unblock, UnblockKind::Ready);
590 this.rwlock_reader_lock(&rwlock_ref)?;
591 this.write_scalar(retval, &dest)?;
592 interp_ok(())
593 }
594 ),
595 );
596 }
597
598 #[inline]
600 fn rwlock_writer_lock(&mut self, rwlock_ref: &RwLockRef) -> InterpResult<'tcx> {
601 let this = self.eval_context_mut();
602 let thread = this.active_thread();
603 trace!("rwlock_writer_lock: now held by {:?}", thread);
604
605 let mut rwlock = rwlock_ref.0.borrow_mut();
606 assert!(!rwlock.is_locked(), "the rwlock is already locked");
607 rwlock.writer = Some(thread);
608 this.acquire_clock(&rwlock.clock_unlocked)?;
609 interp_ok(())
610 }
611
612 #[inline]
615 fn rwlock_writer_unlock(&mut self, rwlock_ref: &RwLockRef) -> InterpResult<'tcx, bool> {
616 let this = self.eval_context_mut();
617 let thread = this.active_thread();
618 let mut rwlock = rwlock_ref.0.borrow_mut();
619 interp_ok(if let Some(current_writer) = rwlock.writer {
620 if current_writer != thread {
621 return interp_ok(false);
623 }
624 rwlock.writer = None;
625 trace!("rwlock_writer_unlock: unlocked by {:?}", thread);
626 this.release_clock(|clock| rwlock.clock_unlocked.clone_from(clock))?;
628
629 if let Some(writer) = rwlock.writer_queue.pop_front() {
635 drop(rwlock); this.unblock_thread(writer, BlockReason::RwLock)?;
637 } else {
638 let readers = std::mem::take(&mut rwlock.reader_queue);
640 drop(rwlock); for reader in readers {
642 this.unblock_thread(reader, BlockReason::RwLock)?;
643 }
644 }
645 true
646 } else {
647 false
648 })
649 }
650
651 #[inline]
654 fn rwlock_enqueue_and_block_writer(
655 &mut self,
656 rwlock_ref: RwLockRef,
657 retval: Scalar,
658 dest: MPlaceTy<'tcx>,
659 ) {
660 let this = self.eval_context_mut();
661 let thread = this.active_thread();
662 let mut rwlock = rwlock_ref.0.borrow_mut();
663 rwlock.writer_queue.push_back(thread);
664 assert!(rwlock.is_locked(), "write-queueing on unlocked rwlock");
665 drop(rwlock);
666 this.block_thread(
667 BlockReason::RwLock,
668 None,
669 callback!(
670 @capture<'tcx> {
671 rwlock_ref: RwLockRef,
672 retval: Scalar,
673 dest: MPlaceTy<'tcx>,
674 }
675 |this, unblock: UnblockKind| {
676 assert_eq!(unblock, UnblockKind::Ready);
677 this.rwlock_writer_lock(&rwlock_ref)?;
678 this.write_scalar(retval, &dest)?;
679 interp_ok(())
680 }
681 ),
682 );
683 }
684
685 fn condvar_wait(
689 &mut self,
690 condvar_ref: CondvarRef,
691 mutex_ref: MutexRef,
692 deadline: Option<Deadline>,
693 retval_succ: Scalar,
694 retval_timeout: Scalar,
695 dest: MPlaceTy<'tcx>,
696 ) -> InterpResult<'tcx> {
697 let this = self.eval_context_mut();
698 if let Some(old_locked_count) = this.mutex_unlock(&mutex_ref)? {
699 if old_locked_count != 1 {
700 throw_unsup_format!(
701 "awaiting a condvar on a mutex acquired multiple times is not supported"
702 );
703 }
704 } else {
705 throw_ub_format!(
706 "awaiting a condvar on a mutex that is unlocked or owned by a different thread"
707 );
708 }
709 let thread = this.active_thread();
710
711 condvar_ref.0.borrow_mut().waiters.push_back(thread);
712 this.block_thread(
713 BlockReason::Condvar,
714 deadline,
715 callback!(
716 @capture<'tcx> {
717 condvar_ref: CondvarRef,
718 mutex_ref: MutexRef,
719 retval_succ: Scalar,
720 retval_timeout: Scalar,
721 dest: MPlaceTy<'tcx>,
722 }
723 |this, unblock: UnblockKind| {
724 match unblock {
725 UnblockKind::Ready => {
726 this.acquire_clock(
728 &condvar_ref.0.borrow().clock,
729 )?;
730 this.condvar_reacquire_mutex(mutex_ref, retval_succ, dest)
733 }
734 UnblockKind::TimedOut => {
735 let thread = this.active_thread();
737 let waiters = &mut condvar_ref.0.borrow_mut().waiters;
738 waiters.retain(|waiter| *waiter != thread);
739 this.condvar_reacquire_mutex(mutex_ref, retval_timeout, dest)
741 }
742 }
743 }
744 ),
745 );
746 interp_ok(())
747 }
748
749 fn condvar_signal(&mut self, condvar_ref: &CondvarRef) -> InterpResult<'tcx, bool> {
752 let this = self.eval_context_mut();
753 let mut condvar = condvar_ref.0.borrow_mut();
754
755 this.release_clock(|clock| condvar.clock.clone_from(clock))?;
757 let Some(waiter) = condvar.waiters.pop_front() else {
758 return interp_ok(false);
759 };
760 drop(condvar);
761 this.unblock_thread(waiter, BlockReason::Condvar)?;
762 interp_ok(true)
763 }
764
765 fn futex_wait(
768 &mut self,
769 futex_ref: FutexRef,
770 bitset: u32,
771 deadline: Option<Deadline>,
772 callback: DynUnblockCallback<'tcx>,
773 ) {
774 let this = self.eval_context_mut();
775 let thread = this.active_thread();
776 let mut futex = futex_ref.0.borrow_mut();
777 let waiters = &mut futex.waiters;
778 assert!(waiters.iter().all(|waiter| waiter.thread != thread), "thread is already waiting");
779 waiters.push(FutexWaiter { thread, bitset });
780 drop(futex);
781
782 this.block_thread(
783 BlockReason::Futex,
784 deadline,
785 callback!(
786 @capture<'tcx> {
787 futex_ref: FutexRef,
788 callback: DynUnblockCallback<'tcx>,
789 }
790 |this, unblock: UnblockKind| {
791 match unblock {
792 UnblockKind::Ready => {
793 let futex = futex_ref.0.borrow();
794 this.acquire_clock(&futex.clock)?;
796 },
797 UnblockKind::TimedOut => {
798 let thread = this.active_thread();
800 let mut futex = futex_ref.0.borrow_mut();
801 futex.waiters.retain(|waiter| waiter.thread != thread);
802 },
803 }
804
805 callback.call(this, unblock)
806 }
807 ),
808 );
809 }
810
811 fn futex_wake(
814 &mut self,
815 futex_ref: &FutexRef,
816 bitset: u32,
817 count: usize,
818 ) -> InterpResult<'tcx, usize> {
819 let this = self.eval_context_mut();
820 let mut futex = futex_ref.0.borrow_mut();
821
822 this.release_clock(|clock| futex.clock.clone_from(clock))?;
824
825 let waiters: Vec<_> =
829 futex.waiters.extract_if(.., |w| w.bitset & bitset != 0).take(count).collect();
830 drop(futex);
831
832 let woken = waiters.len();
833 for waiter in waiters {
834 this.unblock_thread(waiter.thread, BlockReason::Futex)?;
835 }
836
837 interp_ok(woken)
838 }
839}