1use rustc_abi::Size;
2use rustc_target::spec::Os;
3
4use crate::concurrency::sync::{AccessKind, SyncObj};
5use crate::*;
6
7fn bytewise_equal<'tcx>(
12 ecx: &MiriInterpCx<'tcx>,
13 left: &MPlaceTy<'tcx>,
14 right: &MPlaceTy<'tcx>,
15 prefix: Option<u64>,
16) -> InterpResult<'tcx, bool> {
17 let size = left.layout.size;
18 assert_eq!(size, right.layout.size);
19 let cmp_size = prefix.map(Size::from_bytes).unwrap_or(size);
20
21 let left_bytes = ecx.read_bytes_ptr_strip_provenance(left.ptr(), cmp_size)?;
22 let right_bytes = ecx.read_bytes_ptr_strip_provenance(right.ptr(), cmp_size)?;
23
24 interp_ok(left_bytes == right_bytes)
25}
26
27const PTHREAD_UNINIT: u8 = 0;
29const PTHREAD_INIT: u8 = 1;
30
31#[inline]
36fn mutexattr_kind_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
37 interp_ok(match &ecx.tcx.sess.target.os {
38 Os::Linux
39 | Os::Illumos
40 | Os::Solaris
41 | Os::MacOs
42 | Os::FreeBsd
43 | Os::Android
44 | Os::NetBsd => 0,
45 os => throw_unsup_format!("`pthread_mutexattr` is not supported on {os}"),
46 })
47}
48
49fn mutexattr_get_kind<'tcx>(
50 ecx: &MiriInterpCx<'tcx>,
51 attr_ptr: &OpTy<'tcx>,
52) -> InterpResult<'tcx, i32> {
53 ecx.deref_pointer_and_read(
54 attr_ptr,
55 mutexattr_kind_offset(ecx)?,
56 ecx.libc_ty_layout("pthread_mutexattr_t"),
57 ecx.machine.layouts.i32,
58 )?
59 .to_i32()
60}
61
62fn mutexattr_set_kind<'tcx>(
63 ecx: &mut MiriInterpCx<'tcx>,
64 attr_ptr: &OpTy<'tcx>,
65 kind: i32,
66) -> InterpResult<'tcx, ()> {
67 ecx.deref_pointer_and_write(
68 attr_ptr,
69 mutexattr_kind_offset(ecx)?,
70 Scalar::from_i32(kind),
71 ecx.libc_ty_layout("pthread_mutexattr_t"),
72 ecx.machine.layouts.i32,
73 )
74}
75
76const PTHREAD_MUTEX_KIND_UNCHANGED: i32 = 0x8000000;
81
82fn mutexattr_translate_kind<'tcx>(
84 ecx: &MiriInterpCx<'tcx>,
85 kind: i32,
86) -> InterpResult<'tcx, MutexKind> {
87 interp_ok(if kind == (ecx.eval_libc_i32("PTHREAD_MUTEX_NORMAL")) {
88 MutexKind::Normal
89 } else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") {
90 MutexKind::ErrorCheck
91 } else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") {
92 MutexKind::Recursive
93 } else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")
94 || kind == PTHREAD_MUTEX_KIND_UNCHANGED
95 {
96 MutexKind::Default
99 } else {
100 throw_unsup_format!("unsupported type of mutex: {kind}");
101 })
102}
103
104#[derive(Debug, Clone, Copy)]
110enum MutexKind {
111 Normal,
112 Default,
113 Recursive,
114 ErrorCheck,
115}
116
117#[derive(Debug, Clone)]
118struct PthreadMutex {
119 mutex_ref: MutexRef,
120 kind: MutexKind,
121}
122
123impl SyncObj for PthreadMutex {
124 fn on_access<'tcx>(&self, access_kind: AccessKind) -> InterpResult<'tcx> {
125 if !self.mutex_ref.queue_is_empty() {
126 throw_ub_format!(
127 "{access_kind} of `pthread_mutex_t` is forbidden while the queue is non-empty"
128 );
129 }
130 interp_ok(())
131 }
132
133 fn delete_on_write(&self) -> bool {
134 true
135 }
136}
137
138fn mutex_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size> {
142 let offset = match &ecx.tcx.sess.target.os {
143 Os::Linux | Os::Illumos | Os::Solaris | Os::FreeBsd | Os::Android => 0,
144 Os::MacOs | Os::NetBsd => 4,
146 os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"),
147 };
148 let offset = Size::from_bytes(offset);
149
150 if !ecx.machine.pthread_mutex_sanity.replace(true) {
153 let check_static_initializer = |name| {
154 let static_initializer = ecx.eval_path(&["libc", name]);
155 let init_field =
156 static_initializer.offset(offset, ecx.machine.layouts.u8, ecx).unwrap();
157 let init = ecx.read_scalar(&init_field).unwrap().to_u8().unwrap();
158 assert_eq!(
159 init, PTHREAD_UNINIT,
160 "{name} is incompatible with our initialization logic"
161 );
162 };
163
164 check_static_initializer("PTHREAD_MUTEX_INITIALIZER");
165 match &ecx.tcx.sess.target.os {
167 Os::Linux => {
168 check_static_initializer("PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP");
169 check_static_initializer("PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP");
170 check_static_initializer("PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP");
171 }
172 Os::Illumos | Os::Solaris | Os::MacOs | Os::FreeBsd | Os::Android | Os::NetBsd => {
173 }
175 os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"),
176 }
177 }
178
179 interp_ok(offset)
180}
181
182fn mutex_create<'tcx>(
184 ecx: &mut MiriInterpCx<'tcx>,
185 mutex_ptr: &OpTy<'tcx>,
186 kind: MutexKind,
187) -> InterpResult<'tcx, PthreadMutex> {
188 let mutex = ecx.deref_pointer_as(mutex_ptr, ecx.libc_ty_layout("pthread_mutex_t"))?;
189 let data = PthreadMutex { mutex_ref: MutexRef::new(), kind };
190 ecx.init_immovable_sync(&mutex, mutex_init_offset(ecx)?, PTHREAD_INIT, data.clone())?;
191 interp_ok(data)
192}
193
194fn mutex_get_data<'tcx, 'a>(
197 ecx: &'a mut MiriInterpCx<'tcx>,
198 mutex_ptr: &OpTy<'tcx>,
199) -> InterpResult<'tcx, &'a PthreadMutex>
200where
201 'tcx: 'a,
202{
203 let mutex = ecx.deref_pointer_as(mutex_ptr, ecx.libc_ty_layout("pthread_mutex_t"))?;
204 ecx.get_immovable_sync_with_static_init(
205 &mutex,
206 mutex_init_offset(ecx)?,
207 PTHREAD_UNINIT,
208 PTHREAD_INIT,
209 |ecx| {
210 let kind = mutex_kind_from_static_initializer(ecx, &mutex)?;
211 interp_ok(PthreadMutex { mutex_ref: MutexRef::new(), kind })
212 },
213 )
214}
215
216fn mutex_kind_from_static_initializer<'tcx>(
218 ecx: &MiriInterpCx<'tcx>,
219 mutex: &MPlaceTy<'tcx>,
220) -> InterpResult<'tcx, MutexKind> {
221 let prefix = match &ecx.tcx.sess.target.os {
222 Os::Android => Some(4),
225 _ => None,
226 };
227 let is_initializer = |name| bytewise_equal(ecx, mutex, &ecx.eval_path(&["libc", name]), prefix);
228
229 if is_initializer("PTHREAD_MUTEX_INITIALIZER")? {
233 return interp_ok(MutexKind::Default);
234 }
235 match &ecx.tcx.sess.target.os {
237 Os::Linux =>
238 if is_initializer("PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP")? {
239 return interp_ok(MutexKind::Recursive);
240 } else if is_initializer("PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP")? {
241 return interp_ok(MutexKind::ErrorCheck);
242 },
243 _ => {}
244 }
245 throw_ub_format!(
246 "`pthread_mutex_t` was not properly initialized at this location, or it got overwritten"
247 );
248}
249
250#[derive(Debug, Clone)]
255struct PthreadRwLock {
256 rwlock_ref: RwLockRef,
257}
258
259impl SyncObj for PthreadRwLock {
260 fn on_access<'tcx>(&self, access_kind: AccessKind) -> InterpResult<'tcx> {
261 if !self.rwlock_ref.queue_is_empty() {
262 throw_ub_format!(
263 "{access_kind} of `pthread_rwlock_t` is forbidden while the queue is non-empty"
264 );
265 }
266 interp_ok(())
267 }
268
269 fn delete_on_write(&self) -> bool {
270 true
271 }
272}
273
274fn rwlock_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size> {
275 let offset = match &ecx.tcx.sess.target.os {
276 Os::Linux | Os::Illumos | Os::Solaris | Os::FreeBsd | Os::Android => 0,
277 Os::MacOs => 4,
279 os => throw_unsup_format!("`pthread_rwlock` is not supported on {os}"),
280 };
281 let offset = Size::from_bytes(offset);
282
283 if !ecx.machine.pthread_rwlock_sanity.replace(true) {
286 let static_initializer = ecx.eval_path(&["libc", "PTHREAD_RWLOCK_INITIALIZER"]);
287 let init_field = static_initializer.offset(offset, ecx.machine.layouts.u8, ecx).unwrap();
288 let init = ecx.read_scalar(&init_field).unwrap().to_u8().unwrap();
289 assert_eq!(
290 init, PTHREAD_UNINIT,
291 "PTHREAD_RWLOCK_INITIALIZER is incompatible with our initialization logic"
292 );
293 }
294
295 interp_ok(offset)
296}
297
298fn rwlock_get_data<'tcx, 'a>(
299 ecx: &'a mut MiriInterpCx<'tcx>,
300 rwlock_ptr: &OpTy<'tcx>,
301) -> InterpResult<'tcx, &'a PthreadRwLock>
302where
303 'tcx: 'a,
304{
305 let rwlock = ecx.deref_pointer_as(rwlock_ptr, ecx.libc_ty_layout("pthread_rwlock_t"))?;
306 ecx.get_immovable_sync_with_static_init(
307 &rwlock,
308 rwlock_init_offset(ecx)?,
309 PTHREAD_UNINIT,
310 PTHREAD_INIT,
311 |ecx| {
312 let prefix = match &ecx.tcx.sess.target.os {
313 Os::Android => Some(4),
316 _ => None,
317 };
318 if !bytewise_equal(
319 ecx,
320 &rwlock,
321 &ecx.eval_path(&["libc", "PTHREAD_RWLOCK_INITIALIZER"]),
322 prefix,
323 )? {
324 throw_ub_format!(
325 "`pthread_rwlock_t` was not properly initialized at this location, or it got overwritten"
326 );
327 }
328 interp_ok(PthreadRwLock { rwlock_ref: RwLockRef::new() })
329 },
330 )
331}
332
333#[inline]
338fn condattr_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
339 interp_ok(match &ecx.tcx.sess.target.os {
340 Os::Linux | Os::Illumos | Os::Solaris | Os::FreeBsd | Os::Android => 0,
341 os => throw_unsup_format!("`pthread_condattr` clock field is not supported on {os}"),
343 })
344}
345
346fn condattr_get_clock_id<'tcx>(
347 ecx: &MiriInterpCx<'tcx>,
348 attr_ptr: &OpTy<'tcx>,
349) -> InterpResult<'tcx, Scalar> {
350 ecx.deref_pointer_and_read(
351 attr_ptr,
352 condattr_clock_offset(ecx)?,
353 ecx.libc_ty_layout("pthread_condattr_t"),
354 ecx.machine.layouts.i32,
355 )
356}
357
358fn condattr_set_clock_id<'tcx>(
359 ecx: &mut MiriInterpCx<'tcx>,
360 attr_ptr: &OpTy<'tcx>,
361 clock_id: i32,
362) -> InterpResult<'tcx, ()> {
363 ecx.deref_pointer_and_write(
364 attr_ptr,
365 condattr_clock_offset(ecx)?,
366 Scalar::from_i32(clock_id),
367 ecx.libc_ty_layout("pthread_condattr_t"),
368 ecx.machine.layouts.i32,
369 )
370}
371
372fn cond_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size> {
377 let offset = match &ecx.tcx.sess.target.os {
378 Os::Linux | Os::Illumos | Os::Solaris | Os::FreeBsd | Os::Android => 0,
379 Os::MacOs => 4,
381 os => throw_unsup_format!("`pthread_cond` is not supported on {os}"),
382 };
383 let offset = Size::from_bytes(offset);
384
385 if !ecx.machine.pthread_condvar_sanity.replace(true) {
388 let static_initializer = ecx.eval_path(&["libc", "PTHREAD_COND_INITIALIZER"]);
389 let init_field = static_initializer.offset(offset, ecx.machine.layouts.u8, ecx).unwrap();
390 let init = ecx.read_scalar(&init_field).unwrap().to_u8().unwrap();
391 assert_eq!(
392 init, PTHREAD_UNINIT,
393 "PTHREAD_COND_INITIALIZER is incompatible with our initialization logic"
394 );
395 }
396
397 interp_ok(offset)
398}
399
400#[derive(Debug, Clone)]
401struct PthreadCondvar {
402 condvar_ref: CondvarRef,
403 clock: TimeoutClock,
404}
405
406impl SyncObj for PthreadCondvar {
407 fn on_access<'tcx>(&self, access_kind: AccessKind) -> InterpResult<'tcx> {
408 if !self.condvar_ref.queue_is_empty() {
409 throw_ub_format!(
410 "{access_kind} of `pthread_cond_t` is forbidden while the queue is non-empty"
411 );
412 }
413 interp_ok(())
414 }
415
416 fn delete_on_write(&self) -> bool {
417 true
418 }
419}
420
421fn cond_create<'tcx>(
422 ecx: &mut MiriInterpCx<'tcx>,
423 cond_ptr: &OpTy<'tcx>,
424 clock: TimeoutClock,
425) -> InterpResult<'tcx, PthreadCondvar> {
426 let cond = ecx.deref_pointer_as(cond_ptr, ecx.libc_ty_layout("pthread_cond_t"))?;
427 let data = PthreadCondvar { condvar_ref: CondvarRef::new(), clock };
428 ecx.init_immovable_sync(&cond, cond_init_offset(ecx)?, PTHREAD_INIT, data.clone())?;
429 interp_ok(data)
430}
431
432fn cond_get_data<'tcx, 'a>(
433 ecx: &'a mut MiriInterpCx<'tcx>,
434 cond_ptr: &OpTy<'tcx>,
435) -> InterpResult<'tcx, &'a PthreadCondvar>
436where
437 'tcx: 'a,
438{
439 let cond = ecx.deref_pointer_as(cond_ptr, ecx.libc_ty_layout("pthread_cond_t"))?;
440 ecx.get_immovable_sync_with_static_init(
441 &cond,
442 cond_init_offset(ecx)?,
443 PTHREAD_UNINIT,
444 PTHREAD_INIT,
445 |ecx| {
446 let prefix = match &ecx.tcx.sess.target.os {
447 Os::Android => Some(4),
450 _ => None,
451 };
452 if !bytewise_equal(
453 ecx,
454 &cond,
455 &ecx.eval_path(&["libc", "PTHREAD_COND_INITIALIZER"]),
456 prefix,
457 )? {
458 throw_ub_format!(
459 "`pthread_cond_t` was not properly initialized at this location, or it got overwritten"
460 );
461 }
462 interp_ok(PthreadCondvar {
464 condvar_ref: CondvarRef::new(),
465 clock: TimeoutClock::RealTime,
466 })
467 },
468 )
469}
470
471impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
472pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
473 fn pthread_mutexattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
474 let this = self.eval_context_mut();
475
476 mutexattr_set_kind(this, attr_op, PTHREAD_MUTEX_KIND_UNCHANGED)?;
477
478 interp_ok(())
479 }
480
481 fn pthread_mutexattr_settype(
482 &mut self,
483 attr_op: &OpTy<'tcx>,
484 kind_op: &OpTy<'tcx>,
485 ) -> InterpResult<'tcx, Scalar> {
486 let this = self.eval_context_mut();
487
488 let kind = this.read_scalar(kind_op)?.to_i32()?;
489 if kind == this.eval_libc_i32("PTHREAD_MUTEX_NORMAL")
490 || kind == this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")
491 || kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK")
492 || kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE")
493 {
494 assert_ne!(kind, PTHREAD_MUTEX_KIND_UNCHANGED);
496 mutexattr_set_kind(this, attr_op, kind)?;
497 } else {
498 let einval = this.eval_libc_i32("EINVAL");
499 return interp_ok(Scalar::from_i32(einval));
500 }
501
502 interp_ok(Scalar::from_i32(0))
503 }
504
505 fn pthread_mutexattr_destroy(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
506 let this = self.eval_context_mut();
507
508 mutexattr_get_kind(this, attr_op)?;
510
511 this.write_uninit(
524 &this.deref_pointer_as(attr_op, this.libc_ty_layout("pthread_mutexattr_t"))?,
525 )?;
526
527 interp_ok(())
528 }
529
530 fn pthread_mutex_init(
531 &mut self,
532 mutex_op: &OpTy<'tcx>,
533 attr_op: &OpTy<'tcx>,
534 ) -> InterpResult<'tcx, ()> {
535 let this = self.eval_context_mut();
536
537 let attr = this.read_pointer(attr_op)?;
538 let kind = if this.ptr_is_null(attr)? {
539 MutexKind::Default
540 } else {
541 mutexattr_translate_kind(this, mutexattr_get_kind(this, attr_op)?)?
542 };
543
544 mutex_create(this, mutex_op, kind)?;
545
546 interp_ok(())
547 }
548
549 fn pthread_mutex_lock(
550 &mut self,
551 mutex_op: &OpTy<'tcx>,
552 dest: &MPlaceTy<'tcx>,
553 ) -> InterpResult<'tcx> {
554 let this = self.eval_context_mut();
555
556 let mutex = mutex_get_data(this, mutex_op)?.clone();
557
558 let ret = if let Some(owner_thread) = mutex.mutex_ref.owner() {
559 if owner_thread != this.active_thread() {
560 this.mutex_enqueue_and_block(
561 mutex.mutex_ref,
562 Some((Scalar::from_i32(0), dest.clone())),
563 );
564 return interp_ok(());
565 } else {
566 match mutex.kind {
568 MutexKind::Default =>
569 throw_ub_format!(
570 "trying to acquire default mutex already locked by the current thread"
571 ),
572 MutexKind::Normal => throw_machine_stop!(TerminationInfo::LocalDeadlock),
573 MutexKind::ErrorCheck => this.eval_libc_i32("EDEADLK"),
574 MutexKind::Recursive => {
575 this.mutex_lock(&mutex.mutex_ref)?;
576 0
577 }
578 }
579 }
580 } else {
581 this.mutex_lock(&mutex.mutex_ref)?;
583 0
584 };
585 this.write_scalar(Scalar::from_i32(ret), dest)?;
586 interp_ok(())
587 }
588
589 fn pthread_mutex_trylock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
590 let this = self.eval_context_mut();
591
592 let mutex = mutex_get_data(this, mutex_op)?.clone();
593
594 interp_ok(Scalar::from_i32(if let Some(owner_thread) = mutex.mutex_ref.owner() {
595 if owner_thread != this.active_thread() {
596 this.eval_libc_i32("EBUSY")
597 } else {
598 match mutex.kind {
599 MutexKind::Default | MutexKind::Normal | MutexKind::ErrorCheck =>
600 this.eval_libc_i32("EBUSY"),
601 MutexKind::Recursive => {
602 this.mutex_lock(&mutex.mutex_ref)?;
603 0
604 }
605 }
606 }
607 } else {
608 this.mutex_lock(&mutex.mutex_ref)?;
610 0
611 }))
612 }
613
614 fn pthread_mutex_unlock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
615 let this = self.eval_context_mut();
616
617 let mutex = mutex_get_data(this, mutex_op)?.clone();
618
619 if let Some(_old_locked_count) = this.mutex_unlock(&mutex.mutex_ref)? {
620 interp_ok(Scalar::from_i32(0))
622 } else {
623 match mutex.kind {
627 MutexKind::Default =>
628 throw_ub_format!(
629 "unlocked a default mutex that was not locked by the current thread"
630 ),
631 MutexKind::Normal =>
632 throw_ub_format!(
633 "unlocked a PTHREAD_MUTEX_NORMAL mutex that was not locked by the current thread"
634 ),
635 MutexKind::ErrorCheck | MutexKind::Recursive =>
636 interp_ok(Scalar::from_i32(this.eval_libc_i32("EPERM"))),
637 }
638 }
639 }
640
641 fn pthread_mutex_destroy(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
642 let this = self.eval_context_mut();
643
644 let mutex = mutex_get_data(this, mutex_op)?.clone();
647
648 if mutex.mutex_ref.owner().is_some() {
649 throw_ub_format!("destroyed a locked mutex");
650 }
651
652 let mutex_place =
655 this.deref_pointer_as(mutex_op, this.libc_ty_layout("pthread_mutex_t"))?;
656 this.write_uninit(&mutex_place)?;
657
658 interp_ok(())
659 }
660
661 fn pthread_rwlock_rdlock(
662 &mut self,
663 rwlock_op: &OpTy<'tcx>,
664 dest: &MPlaceTy<'tcx>,
665 ) -> InterpResult<'tcx> {
666 let this = self.eval_context_mut();
667
668 let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
669
670 if rwlock.rwlock_ref.is_write_locked() {
671 this.rwlock_enqueue_and_block_reader(
672 rwlock.rwlock_ref,
673 Scalar::from_i32(0),
674 dest.clone(),
675 );
676 } else {
677 this.rwlock_reader_lock(&rwlock.rwlock_ref)?;
678 this.write_null(dest)?;
679 }
680
681 interp_ok(())
682 }
683
684 fn pthread_rwlock_tryrdlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
685 let this = self.eval_context_mut();
686
687 let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
688
689 if rwlock.rwlock_ref.is_write_locked() {
690 interp_ok(Scalar::from_i32(this.eval_libc_i32("EBUSY")))
691 } else {
692 this.rwlock_reader_lock(&rwlock.rwlock_ref)?;
693 interp_ok(Scalar::from_i32(0))
694 }
695 }
696
697 fn pthread_rwlock_wrlock(
698 &mut self,
699 rwlock_op: &OpTy<'tcx>,
700 dest: &MPlaceTy<'tcx>,
701 ) -> InterpResult<'tcx> {
702 let this = self.eval_context_mut();
703
704 let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
705
706 if rwlock.rwlock_ref.is_locked() {
707 this.rwlock_enqueue_and_block_writer(
720 rwlock.rwlock_ref,
721 Scalar::from_i32(0),
722 dest.clone(),
723 );
724 } else {
725 this.rwlock_writer_lock(&rwlock.rwlock_ref)?;
726 this.write_null(dest)?;
727 }
728
729 interp_ok(())
730 }
731
732 fn pthread_rwlock_trywrlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
733 let this = self.eval_context_mut();
734
735 let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
736
737 if rwlock.rwlock_ref.is_locked() {
738 interp_ok(Scalar::from_i32(this.eval_libc_i32("EBUSY")))
739 } else {
740 this.rwlock_writer_lock(&rwlock.rwlock_ref)?;
741 interp_ok(Scalar::from_i32(0))
742 }
743 }
744
745 fn pthread_rwlock_unlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
746 let this = self.eval_context_mut();
747
748 let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
749
750 if this.rwlock_reader_unlock(&rwlock.rwlock_ref)?
751 || this.rwlock_writer_unlock(&rwlock.rwlock_ref)?
752 {
753 interp_ok(())
754 } else {
755 throw_ub_format!("unlocked an rwlock that was not locked by the active thread");
756 }
757 }
758
759 fn pthread_rwlock_destroy(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
760 let this = self.eval_context_mut();
761
762 let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
765
766 if rwlock.rwlock_ref.is_locked() {
767 throw_ub_format!("destroyed a locked rwlock");
768 }
769
770 let rwlock_place =
773 this.deref_pointer_as(rwlock_op, this.libc_ty_layout("pthread_rwlock_t"))?;
774 this.write_uninit(&rwlock_place)?;
775
776 interp_ok(())
777 }
778
779 fn pthread_condattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
780 let this = self.eval_context_mut();
781
782 if this.tcx.sess.target.os != Os::MacOs {
784 let default_clock_id = this.eval_libc_i32("CLOCK_REALTIME");
788 condattr_set_clock_id(this, attr_op, default_clock_id)?;
789 }
790
791 interp_ok(())
792 }
793
794 fn pthread_condattr_setclock(
795 &mut self,
796 attr_op: &OpTy<'tcx>,
797 clock_id_op: &OpTy<'tcx>,
798 ) -> InterpResult<'tcx, Scalar> {
799 let this = self.eval_context_mut();
800
801 let clock_id = this.read_scalar(clock_id_op)?;
802 if this.parse_clockid(clock_id).is_some() {
803 condattr_set_clock_id(this, attr_op, clock_id.to_i32()?)?;
804 } else {
805 let einval = this.eval_libc_i32("EINVAL");
806 return interp_ok(Scalar::from_i32(einval));
807 }
808
809 interp_ok(Scalar::from_i32(0))
810 }
811
812 fn pthread_condattr_getclock(
813 &mut self,
814 attr_op: &OpTy<'tcx>,
815 clk_id_op: &OpTy<'tcx>,
816 ) -> InterpResult<'tcx, ()> {
817 let this = self.eval_context_mut();
818
819 let clock_id = condattr_get_clock_id(this, attr_op)?;
820 this.write_scalar(
821 clock_id,
822 &this.deref_pointer_as(clk_id_op, this.libc_ty_layout("clockid_t"))?,
823 )?;
824
825 interp_ok(())
826 }
827
828 fn pthread_condattr_destroy(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
829 let this = self.eval_context_mut();
830
831 if this.tcx.sess.target.os != Os::MacOs {
834 condattr_get_clock_id(this, attr_op)?;
835 }
836
837 this.write_uninit(
840 &this.deref_pointer_as(attr_op, this.libc_ty_layout("pthread_condattr_t"))?,
841 )?;
842
843 interp_ok(())
844 }
845
846 fn pthread_cond_init(
847 &mut self,
848 cond_op: &OpTy<'tcx>,
849 attr_op: &OpTy<'tcx>,
850 ) -> InterpResult<'tcx, ()> {
851 let this = self.eval_context_mut();
852
853 let attr = this.read_pointer(attr_op)?;
854 let clock_id = if this.ptr_is_null(attr)? || this.tcx.sess.target.os == Os::MacOs {
856 this.eval_libc("CLOCK_REALTIME")
857 } else {
858 condattr_get_clock_id(this, attr_op)?
859 };
860 let Some(clock) = this.parse_clockid(clock_id) else {
861 throw_ub_format!("pthread_cond_init: invalid attributes (unsupported clock)")
863 };
864
865 cond_create(this, cond_op, clock)?;
866
867 interp_ok(())
868 }
869
870 fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
871 let this = self.eval_context_mut();
872 let condvar = cond_get_data(this, cond_op)?.condvar_ref.clone();
873 this.condvar_signal(&condvar)?;
874 interp_ok(())
875 }
876
877 fn pthread_cond_broadcast(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
878 let this = self.eval_context_mut();
879 let condvar = cond_get_data(this, cond_op)?.condvar_ref.clone();
880 while this.condvar_signal(&condvar)? {}
881 interp_ok(())
882 }
883
884 fn pthread_cond_wait(
885 &mut self,
886 cond_op: &OpTy<'tcx>,
887 mutex_op: &OpTy<'tcx>,
888 dest: &MPlaceTy<'tcx>,
889 ) -> InterpResult<'tcx> {
890 let this = self.eval_context_mut();
891
892 let data = cond_get_data(this, cond_op)?.clone();
893 let mutex_ref = mutex_get_data(this, mutex_op)?.mutex_ref.clone();
894
895 this.condvar_wait(
896 data.condvar_ref,
897 mutex_ref,
898 None, Scalar::from_i32(0),
900 Scalar::from_i32(0), dest.clone(),
902 )?;
903
904 interp_ok(())
905 }
906
907 fn pthread_cond_timedwait(
908 &mut self,
909 cond_op: &OpTy<'tcx>,
910 mutex_op: &OpTy<'tcx>,
911 timeout_op: &OpTy<'tcx>,
912 dest: &MPlaceTy<'tcx>,
913 macos_relative_np: bool,
914 ) -> InterpResult<'tcx> {
915 let this = self.eval_context_mut();
916
917 let data = cond_get_data(this, cond_op)?.clone();
918 let mutex_ref = mutex_get_data(this, mutex_op)?.mutex_ref.clone();
919
920 let Some(duration) = this
922 .read_timespec(&this.deref_pointer_as(timeout_op, this.libc_ty_layout("timespec"))?)?
923 else {
924 let einval = this.eval_libc("EINVAL");
925 this.write_scalar(einval, dest)?;
926 return interp_ok(());
927 };
928
929 let (clock, style) = if macos_relative_np {
930 (TimeoutClock::Monotonic, TimeoutStyle::Relative)
933 } else {
934 if data.clock == TimeoutClock::RealTime {
935 this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?;
936 }
937
938 (data.clock, TimeoutStyle::Absolute)
939 };
940
941 this.condvar_wait(
942 data.condvar_ref,
943 mutex_ref,
944 Some(this.machine.timeout(clock, style, duration)),
945 Scalar::from_i32(0),
946 this.eval_libc("ETIMEDOUT"), dest.clone(),
948 )?;
949
950 interp_ok(())
951 }
952
953 fn pthread_cond_destroy(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
954 let this = self.eval_context_mut();
955
956 let condvar = &cond_get_data(this, cond_op)?.condvar_ref;
959 if !condvar.queue_is_empty() {
960 throw_ub_format!("destroying an awaited conditional variable");
961 }
962
963 let cond_place = this.deref_pointer_as(cond_op, this.libc_ty_layout("pthread_cond_t"))?;
966 this.write_uninit(&cond_place)?;
967
968 interp_ok(())
969 }
970}