Skip to content

Commit bd97074

Browse files
committed
Small changes.
1 parent babedc9 commit bd97074

File tree

6 files changed

+77
-42
lines changed

6 files changed

+77
-42
lines changed

src/shims/sync.rs

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -288,15 +288,12 @@ fn release_cond_mutex<'mir, 'tcx: 'mir>(
288288
active_thread: ThreadId,
289289
mutex: MutexId,
290290
) -> 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)? {
292292
if old_locked_count != 1 {
293293
throw_unsup_format!("awaiting on a lock acquired multiple times is not supported");
294294
}
295-
if old_owner_thread != active_thread {
296-
throw_ub_format!("awaiting on a mutex owned by a different thread");
297-
}
298295
} else {
299-
throw_ub_format!("awaiting on unlocked mutex");
296+
throw_ub_format!("awaiting on unlocked or owned by a different thread mutex");
300297
}
301298
ecx.block_thread(active_thread)?;
302299
Ok(())
@@ -321,7 +318,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
321318
let this = self.eval_context_mut();
322319

323320
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")?
325323
|| kind == this.eval_libc("PTHREAD_MUTEX_ERRORCHECK")?
326324
|| kind == this.eval_libc("PTHREAD_MUTEX_RECURSIVE")?
327325
{
@@ -380,6 +378,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
380378
Ok(0)
381379
} else {
382380
// 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+
}
383388
if kind == this.eval_libc("PTHREAD_MUTEX_NORMAL")? {
384389
throw_machine_stop!(TerminationInfo::Deadlock);
385390
} else if kind == this.eval_libc("PTHREAD_MUTEX_ERRORCHECK")? {
@@ -388,7 +393,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
388393
this.mutex_lock(id, active_thread);
389394
Ok(0)
390395
} 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+
);
392399
}
393400
}
394401
} else {
@@ -410,15 +417,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
410417
if owner_thread != active_thread {
411418
this.eval_libc_i32("EBUSY")
412419
} 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")?
414422
|| kind == this.eval_libc("PTHREAD_MUTEX_ERRORCHECK")?
415423
{
416424
this.eval_libc_i32("EBUSY")
417425
} else if kind == this.eval_libc("PTHREAD_MUTEX_RECURSIVE")? {
418426
this.mutex_lock(id, active_thread);
419427
Ok(0)
420428
} else {
421-
throw_ub_format!(
429+
throw_unsup_format!(
422430
"called pthread_mutex_trylock on an unsupported type of mutex"
423431
);
424432
}
@@ -435,21 +443,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
435443

436444
let kind = mutex_get_kind(this, mutex_op)?.not_undef()?;
437445
let id = mutex_get_or_create_id(this, mutex_op)?;
446+
let active_thread = this.get_active_thread()?;
438447

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.
443450
Ok(0)
444451
} 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+
);
447463
} else if kind == this.eval_libc("PTHREAD_MUTEX_ERRORCHECK")? {
448464
this.eval_libc_i32("EPERM")
449465
} else if kind == this.eval_libc("PTHREAD_MUTEX_RECURSIVE")? {
450466
this.eval_libc_i32("EPERM")
451467
} 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");
453469
}
454470
}
455471
}
@@ -505,6 +521,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
505521
let active_thread = this.get_active_thread()?;
506522

507523
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.)
508536
this.rwlock_enqueue_and_block_writer(id, active_thread)?;
509537
} else {
510538
this.rwlock_writer_lock(id, active_thread);
@@ -719,19 +747,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
719747
let clock_id = cond_get_clock_id(this, cond_op)?.to_i32()?;
720748
let duration = {
721749
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)?;
725751
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)?;
729753
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+
);
735758
Duration::new(seconds, nanoseconds)
736759
};
737760

@@ -740,7 +763,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
740763
} else if clock_id == this.eval_libc_i32("CLOCK_MONOTONIC")? {
741764
Time::Monotonic(this.machine.time_anchor.checked_add(duration).unwrap())
742765
} else {
743-
throw_unsup_format!("Unsupported clock id.");
766+
throw_unsup_format!("unsupported clock id: {}", clock_id);
744767
};
745768

746769
// Register the timeout callback.

src/sync.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,12 @@ macro_rules! declare_id {
3030
// We use 0 as a sentinel value (see the comment above) and,
3131
// therefore, need to shift by one when converting from an index
3232
// into a vector.
33-
$name(NonZeroU32::new(u32::try_from(idx).unwrap() + 1).unwrap())
33+
let shifted_idx = u32::try_from(idx).unwrap().checked_add(1).unwrap();
34+
$name(NonZeroU32::new(shifted_idx).unwrap())
3435
}
3536
fn index(self) -> usize {
3637
// See the comment in `Self::new`.
38+
// (This cannot underflow because self is NonZeroU32.)
3739
usize::try_from(self.0.get() - 1).unwrap()
3840
}
3941
}
@@ -150,11 +152,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
150152
///
151153
/// Note: It is the caller's responsibility to check that the thread that
152154
/// unlocked the lock actually is the same one, which owned it.
153-
fn mutex_unlock(&mut self, id: MutexId) -> InterpResult<'tcx, Option<(ThreadId, usize)>> {
155+
fn mutex_unlock(
156+
&mut self,
157+
id: MutexId,
158+
expected_owner: ThreadId,
159+
) -> InterpResult<'tcx, Option<usize>> {
154160
let this = self.eval_context_mut();
155161
let mutex = &mut this.machine.threads.sync.mutexes[id];
156162
if let Some(current_owner) = mutex.owner {
157163
// Mutex is locked.
164+
if current_owner != expected_owner {
165+
// Only the owner can unlock the mutex.
166+
return Ok(None);
167+
}
158168
let old_lock_count = mutex.lock_count;
159169
mutex.lock_count = old_lock_count
160170
.checked_sub(1)
@@ -168,7 +178,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
168178
this.unblock_thread(new_owner)?;
169179
}
170180
}
171-
Ok(Some((current_owner, old_lock_count)))
181+
Ok(Some(old_lock_count))
172182
} else {
173183
// Mutex is unlocked.
174184
Ok(None)

src/thread.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ impl<'mir, 'tcx> Default for Thread<'mir, 'tcx> {
159159
}
160160
}
161161

162+
/// A specific moment in time.
162163
#[derive(Debug)]
163164
pub enum Time {
164165
Monotonic(Instant),
@@ -449,9 +450,9 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
449450
//
450451
// Documentation:
451452
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html#
452-
if let Some(sleep_time) =
453-
self.timeout_callbacks.values().map(|info| info.call_time.get_wait_time()).min()
454-
{
453+
let potential_sleep_time =
454+
self.timeout_callbacks.values().map(|info| info.call_time.get_wait_time()).min();
455+
if let Some(sleep_time) = potential_sleep_time {
455456
if sleep_time == Duration::new(0, 0) {
456457
return Ok(SchedulingAction::ExecuteTimeoutCallback);
457458
}
@@ -479,9 +480,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
479480
// We have not found a thread to execute.
480481
if self.threads.iter().all(|thread| thread.state == ThreadState::Terminated) {
481482
unreachable!("all threads terminated without the main thread terminating?!");
482-
} else if let Some(sleep_time) =
483-
self.timeout_callbacks.values().map(|info| info.call_time.get_wait_time()).min()
484-
{
483+
} else if let Some(sleep_time) = potential_sleep_time {
485484
// All threads are currently blocked, but we have unexecuted
486485
// timeout_callbacks, which may unblock some of the threads. Hence,
487486
// sleep until the first callback.

tests/compile-fail/sync/libc_pthread_mutex_normal_deadlock.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ fn main() {
1111
let mut mutex: libc::pthread_mutex_t = std::mem::zeroed();
1212
assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mutexattr as *const _), 0);
1313
assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);
14-
libc::pthread_mutex_lock(&mut mutex as *mut _); //~ ERROR deadlock
14+
// FIXME: The error should be deadlock. See issue
15+
// https://github.com/rust-lang/miri/issues/1419.
16+
libc::pthread_mutex_lock(&mut mutex as *mut _); //~ ERROR Undefined Behavior
1517
}
1618
}

tests/compile-fail/sync/libc_pthread_mutex_wrong_owner.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ fn main() {
2424

2525
let lock_copy = lock.clone();
2626
thread::spawn(move || {
27-
assert_eq!(libc::pthread_mutex_unlock(lock_copy.0.get() as *mut _), 0); //~ ERROR: Undefined Behavior: called pthread_mutex_unlock on a mutex owned by another thread
27+
assert_eq!(libc::pthread_mutex_unlock(lock_copy.0.get() as *mut _), 0); //~ ERROR: Undefined Behavior: unlocked a PTHREAD_MUTEX_DEFAULT mutex that was not locked
2828
})
2929
.join()
3030
.unwrap();

tests/run-pass/concurrency/sync.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ fn check_conditional_variables_timed_wait_timeout() {
9191
let now = Instant::now();
9292
let (_guard, timeout) = cvar.wait_timeout(guard, Duration::from_millis(100)).unwrap();
9393
assert!(timeout.timed_out());
94-
assert!(now.elapsed().as_millis() >= 100);
94+
let elapsed_time = now.elapsed().as_millis();
95+
assert!(100 <= elapsed_time && elapsed_time <= 300);
9596
}
9697

9798
/// Test that signaling a conditional variable when waiting with a timeout works
@@ -243,7 +244,7 @@ fn get_cached_val() -> usize {
243244
fn expensive_computation() -> usize {
244245
let mut i = 1;
245246
let mut c = 1;
246-
while i < 10000 {
247+
while i < 1000 {
247248
i *= c;
248249
c += 1;
249250
}
@@ -257,7 +258,7 @@ fn check_once() {
257258
thread::spawn(|| {
258259
thread::yield_now();
259260
let val = get_cached_val();
260-
assert_eq!(val, 40320);
261+
assert_eq!(val, 5040);
261262
})
262263
})
263264
.collect();

0 commit comments

Comments
 (0)