@@ -288,15 +288,12 @@ fn release_cond_mutex<'mir, 'tcx: 'mir>(
288
288
active_thread : ThreadId ,
289
289
mutex : MutexId ,
290
290
) -> InterpResult < ' tcx > {
291
- if let Some ( ( old_owner_thread , old_locked_count) ) = ecx. mutex_unlock ( mutex) ? {
291
+ if let Some ( old_locked_count) = ecx. mutex_unlock ( mutex, active_thread ) ? {
292
292
if old_locked_count != 1 {
293
293
throw_unsup_format ! ( "awaiting on a lock acquired multiple times is not supported" ) ;
294
294
}
295
- if old_owner_thread != active_thread {
296
- throw_ub_format ! ( "awaiting on a mutex owned by a different thread" ) ;
297
- }
298
295
} else {
299
- throw_ub_format ! ( "awaiting on unlocked mutex" ) ;
296
+ throw_ub_format ! ( "awaiting on unlocked or owned by a different thread mutex" ) ;
300
297
}
301
298
ecx. block_thread ( active_thread) ?;
302
299
Ok ( ( ) )
@@ -321,7 +318,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
321
318
let this = self . eval_context_mut ( ) ;
322
319
323
320
let kind = this. read_scalar ( kind_op) ?. not_undef ( ) ?;
324
- if kind == this. eval_libc ( "PTHREAD_MUTEX_NORMAL" ) ?
321
+ if kind == this. eval_libc ( "PTHREAD_MUTEX_DEFAULT" ) ?
322
+ || kind == this. eval_libc ( "PTHREAD_MUTEX_NORMAL" ) ?
325
323
|| kind == this. eval_libc ( "PTHREAD_MUTEX_ERRORCHECK" ) ?
326
324
|| kind == this. eval_libc ( "PTHREAD_MUTEX_RECURSIVE" ) ?
327
325
{
@@ -380,6 +378,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
380
378
Ok ( 0 )
381
379
} else {
382
380
// Trying to acquire the same mutex again.
381
+ if kind == this. eval_libc ( "PTHREAD_MUTEX_DEFAULT" ) ? {
382
+ // FIXME: Sometimes this is actually a Deadlock.
383
+ // https://github.com/rust-lang/miri/issues/1419
384
+ throw_ub_format ! (
385
+ "trying to acquire already locked PTHREAD_MUTEX_DEFAULT (see #1419)"
386
+ ) ;
387
+ }
383
388
if kind == this. eval_libc ( "PTHREAD_MUTEX_NORMAL" ) ? {
384
389
throw_machine_stop ! ( TerminationInfo :: Deadlock ) ;
385
390
} else if kind == this. eval_libc ( "PTHREAD_MUTEX_ERRORCHECK" ) ? {
@@ -388,7 +393,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
388
393
this. mutex_lock ( id, active_thread) ;
389
394
Ok ( 0 )
390
395
} else {
391
- throw_ub_format ! ( "called pthread_mutex_lock on an unsupported type of mutex" ) ;
396
+ throw_unsup_format ! (
397
+ "called pthread_mutex_lock on an unsupported type of mutex"
398
+ ) ;
392
399
}
393
400
}
394
401
} else {
@@ -410,15 +417,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
410
417
if owner_thread != active_thread {
411
418
this. eval_libc_i32 ( "EBUSY" )
412
419
} else {
413
- if kind == this. eval_libc ( "PTHREAD_MUTEX_NORMAL" ) ?
420
+ if kind == this. eval_libc ( "PTHREAD_MUTEX_DEFAULT" ) ?
421
+ || kind == this. eval_libc ( "PTHREAD_MUTEX_NORMAL" ) ?
414
422
|| kind == this. eval_libc ( "PTHREAD_MUTEX_ERRORCHECK" ) ?
415
423
{
416
424
this. eval_libc_i32 ( "EBUSY" )
417
425
} else if kind == this. eval_libc ( "PTHREAD_MUTEX_RECURSIVE" ) ? {
418
426
this. mutex_lock ( id, active_thread) ;
419
427
Ok ( 0 )
420
428
} else {
421
- throw_ub_format ! (
429
+ throw_unsup_format ! (
422
430
"called pthread_mutex_trylock on an unsupported type of mutex"
423
431
) ;
424
432
}
@@ -435,21 +443,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
435
443
436
444
let kind = mutex_get_kind ( this, mutex_op) ?. not_undef ( ) ?;
437
445
let id = mutex_get_or_create_id ( this, mutex_op) ?;
446
+ let active_thread = this. get_active_thread ( ) ?;
438
447
439
- if let Some ( ( old_owner_thread, _old_locked_count) ) = this. mutex_unlock ( id) ? {
440
- if old_owner_thread != this. get_active_thread ( ) ? {
441
- throw_ub_format ! ( "called pthread_mutex_unlock on a mutex owned by another thread" ) ;
442
- }
448
+ if let Some ( _old_locked_count) = this. mutex_unlock ( id, active_thread) ? {
449
+ // The mutex was locked by the current thread.
443
450
Ok ( 0 )
444
451
} else {
445
- if kind == this. eval_libc ( "PTHREAD_MUTEX_NORMAL" ) ? {
446
- throw_ub_format ! ( "unlocked a PTHREAD_MUTEX_NORMAL mutex that was not locked" ) ;
452
+ // The mutex was locked by another thread or not locked at all. See
453
+ // the “Unlock When Not Owner” column in
454
+ // https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_unlock.html.
455
+ if kind == this. eval_libc ( "PTHREAD_MUTEX_DEFAULT" ) ? {
456
+ throw_ub_format ! (
457
+ "unlocked a PTHREAD_MUTEX_DEFAULT mutex that was not locked by the current thread"
458
+ ) ;
459
+ } else if kind == this. eval_libc ( "PTHREAD_MUTEX_NORMAL" ) ? {
460
+ throw_ub_format ! (
461
+ "unlocked a PTHREAD_MUTEX_NORMAL mutex that was not locked by the current thread"
462
+ ) ;
447
463
} else if kind == this. eval_libc ( "PTHREAD_MUTEX_ERRORCHECK" ) ? {
448
464
this. eval_libc_i32 ( "EPERM" )
449
465
} else if kind == this. eval_libc ( "PTHREAD_MUTEX_RECURSIVE" ) ? {
450
466
this. eval_libc_i32 ( "EPERM" )
451
467
} else {
452
- throw_ub_format ! ( "called pthread_mutex_unlock on an unsupported type of mutex" ) ;
468
+ throw_unsup_format ! ( "called pthread_mutex_unlock on an unsupported type of mutex" ) ;
453
469
}
454
470
}
455
471
}
@@ -505,6 +521,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
505
521
let active_thread = this. get_active_thread ( ) ?;
506
522
507
523
if this. rwlock_is_locked ( id) {
524
+ // Note: this will deadlock if the lock is already locked by this
525
+ // thread in any way.
526
+ //
527
+ // Relevant documentation:
528
+ // https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_wrlock.html
529
+ // An in depth discussion on this topic:
530
+ // https://github.com/rust-lang/rust/issues/53127
531
+ //
532
+ // FIXME: Detect and report the deadlock proactively. (We currently
533
+ // report the deadlock only when no thread can continue execution,
534
+ // but we could detect that this lock is already locked and report
535
+ // an error.)
508
536
this. rwlock_enqueue_and_block_writer ( id, active_thread) ?;
509
537
} else {
510
538
this. rwlock_writer_lock ( id, active_thread) ;
@@ -719,19 +747,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
719
747
let clock_id = cond_get_clock_id ( this, cond_op) ?. to_i32 ( ) ?;
720
748
let duration = {
721
749
let tp = this. deref_operand ( abstime_op) ?;
722
- let mut offset = Size :: from_bytes ( 0 ) ;
723
- let layout = this. libc_ty_layout ( "time_t" ) ?;
724
- let seconds_place = tp. offset ( offset, MemPlaceMeta :: None , layout, this) ?;
750
+ let seconds_place = this. mplace_field ( tp, 0 ) ?;
725
751
let seconds = this. read_scalar ( seconds_place. into ( ) ) ?;
726
- offset += layout. size ;
727
- let layout = this. libc_ty_layout ( "c_long" ) ?;
728
- let nanoseconds_place = tp. offset ( offset, MemPlaceMeta :: None , layout, this) ?;
752
+ let nanoseconds_place = this. mplace_field ( tp, 1 ) ?;
729
753
let nanoseconds = this. read_scalar ( nanoseconds_place. into ( ) ) ?;
730
- let ( seconds, nanoseconds) = if this. pointer_size ( ) . bytes ( ) == 8 {
731
- ( seconds. to_u64 ( ) ?, nanoseconds. to_u64 ( ) ?. try_into ( ) . unwrap ( ) )
732
- } else {
733
- ( seconds. to_u32 ( ) ?. into ( ) , nanoseconds. to_u32 ( ) ?)
734
- } ;
754
+ let ( seconds, nanoseconds) = (
755
+ seconds. to_machine_usize ( this) ?,
756
+ nanoseconds. to_machine_usize ( this) ?. try_into ( ) . unwrap ( ) ,
757
+ ) ;
735
758
Duration :: new ( seconds, nanoseconds)
736
759
} ;
737
760
@@ -740,7 +763,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
740
763
} else if clock_id == this. eval_libc_i32 ( "CLOCK_MONOTONIC" ) ? {
741
764
Time :: Monotonic ( this. machine . time_anchor . checked_add ( duration) . unwrap ( ) )
742
765
} else {
743
- throw_unsup_format ! ( "Unsupported clock id." ) ;
766
+ throw_unsup_format ! ( "unsupported clock id: {}" , clock_id ) ;
744
767
} ;
745
768
746
769
// Register the timeout callback.
0 commit comments