Skip to content

Commit 60c182d

Browse files
committed
apple os_unfair_lock: throw 'unsupported' when we would behave different from the real implementation
1 parent 3def776 commit 60c182d

File tree

3 files changed

+39
-33
lines changed

3 files changed

+39
-33
lines changed

src/shims/unix/macos/sync.rs

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,14 @@ use crate::*;
2020

2121
#[derive(Clone)]
2222
enum MacOsUnfairLock {
23-
Active { mutex_ref: MutexRef },
24-
PermanentlyLocked,
23+
Active {
24+
mutex_ref: MutexRef,
25+
},
26+
/// If a lock gets copied while being held, we put it in this state.
27+
/// It seems like in the real implementation, the lock actually remembers who held it,
28+
/// and still behaves as-if it was held by that thread in the new location. In Miri, we don't
29+
/// know who actually owns this lock at the moment.
30+
PermanentlyLockedByUnknown,
2531
}
2632

2733
impl SyncObj for MacOsUnfairLock {
@@ -93,10 +99,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
9399
// locks when they get released, so it got copied while locked. Unfortunately
94100
// that is something `std` needs to support (the guard could have been leaked).
95101
// On the plus side, we know nobody was queued for the lock while it got copied;
96-
// that would have been rejected by our `on_access`. So we behave like a
97-
// futex-based lock would in this case: any attempt to acquire the lock will
98-
// just wait forever, since there's nobody to wake us up.
99-
interp_ok(MacOsUnfairLock::PermanentlyLocked)
102+
// that would have been rejected by our `on_access`.
103+
// The real implementation would apparently remember who held the old lock, and
104+
// consider them to hold the copy as well -- but our copies don't preserve sync
105+
// object metadata so we instead move the lock into a "permanently locked"
106+
// state.
107+
interp_ok(MacOsUnfairLock::PermanentlyLockedByUnknown)
100108
} else {
101109
throw_ub_format!("`os_unfair_lock` was not properly initialized at this location, or it got overwritten");
102110
}
@@ -303,18 +311,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
303311
let this = self.eval_context_mut();
304312

305313
let MacOsUnfairLock::Active { mutex_ref } = this.os_unfair_lock_get_data(lock_op)? else {
306-
// Trying to get a poisoned lock. Just block forever...
307-
this.block_thread(
308-
BlockReason::Sleep,
309-
None,
310-
callback!(
311-
@capture<'tcx> {}
312-
|_this, _unblock: UnblockKind| {
313-
panic!("we shouldn't wake up ever")
314-
}
315-
),
314+
// Trying to lock a perma-locked lock. On macOS this would block or abort depending
315+
// on whether the current thread is considered to be the one holding this lock. We
316+
// don't know who is considered to be holding the lock so we don't know what to do.
317+
throw_unsup_format!(
318+
"attempted to lock an os_unfair_lock that was copied while being locked"
316319
);
317-
return interp_ok(());
318320
};
319321
let mutex_ref = mutex_ref.clone();
320322

@@ -342,15 +344,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
342344
let this = self.eval_context_mut();
343345

344346
let MacOsUnfairLock::Active { mutex_ref } = this.os_unfair_lock_get_data(lock_op)? else {
345-
// Trying to get a poisoned lock. That never works.
347+
// Trying to lock a perma-locked lock. That behaves the same no matter who the owner is
348+
// so we can implement the real behavior here.
346349
this.write_scalar(Scalar::from_bool(false), dest)?;
347350
return interp_ok(());
348351
};
349352
let mutex_ref = mutex_ref.clone();
350353

351354
if mutex_ref.owner().is_some() {
352-
// Contrary to the blocking lock function, this does not check for
353-
// reentrancy.
355+
// Contrary to the blocking lock function, this does not check for reentrancy.
354356
this.write_scalar(Scalar::from_bool(false), dest)?;
355357
} else {
356358
this.mutex_lock(&mutex_ref)?;
@@ -364,10 +366,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
364366
let this = self.eval_context_mut();
365367

366368
let MacOsUnfairLock::Active { mutex_ref } = this.os_unfair_lock_get_data(lock_op)? else {
367-
// A perma-locked lock is definitely not held by us.
368-
throw_machine_stop!(TerminationInfo::Abort(
369-
"attempted to unlock an os_unfair_lock not owned by the current thread".to_owned()
370-
));
369+
// We don't know who the owner is so we cannot proceed.
370+
throw_unsup_format!(
371+
"attempted to unlock an os_unfair_lock that was copied while being locked"
372+
);
371373
};
372374
let mutex_ref = mutex_ref.clone();
373375

@@ -393,10 +395,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
393395
let this = self.eval_context_mut();
394396

395397
let MacOsUnfairLock::Active { mutex_ref } = this.os_unfair_lock_get_data(lock_op)? else {
396-
// A perma-locked lock is definitely not held by us.
397-
throw_machine_stop!(TerminationInfo::Abort(
398-
"called os_unfair_lock_assert_owner on an os_unfair_lock not owned by the current thread".to_owned()
399-
));
398+
// We don't know who the owner is so we cannot proceed.
399+
throw_unsup_format!(
400+
"attempted to assert the owner of an os_unfair_lock that was copied while being locked"
401+
);
400402
};
401403
let mutex_ref = mutex_ref.clone();
402404

@@ -415,8 +417,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
415417
let this = self.eval_context_mut();
416418

417419
let MacOsUnfairLock::Active { mutex_ref } = this.os_unfair_lock_get_data(lock_op)? else {
418-
// A perma-locked lock is definitely not held by us.
419-
return interp_ok(());
420+
// We don't know who the owner is so we cannot proceed.
421+
throw_unsup_format!(
422+
"attempted to assert the owner of an os_unfair_lock that was copied while being locked"
423+
);
420424
};
421425
let mutex_ref = mutex_ref.clone();
422426

tests/fail-dep/concurrency/apple_os_unfair_lock_move_deadlock.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ fn main() {
99
let lock = lock;
1010
// This needs to either error or deadlock.
1111
unsafe { libc::os_unfair_lock_lock(lock.get()) };
12-
//~^ error: deadlock
12+
//~^ error: lock an os_unfair_lock that was copied while being locked
1313
}

tests/fail-dep/concurrency/apple_os_unfair_lock_move_deadlock.stderr

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
error: the evaluated program deadlocked
1+
error: unsupported operation: attempted to lock an os_unfair_lock that was copied while being locked
22
--> tests/fail-dep/concurrency/apple_os_unfair_lock_move_deadlock.rs:LL:CC
33
|
44
LL | unsafe { libc::os_unfair_lock_lock(lock.get()) };
5-
| ^ this thread got stuck here
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsupported operation occurred here
6+
|
7+
= help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
68

79
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
810

0 commit comments

Comments
 (0)