Skip to content

Commit 4fd0aa3

Browse files
committed
Auto merge of #1441 - RalfJung:sync-cleanup, r=RalfJung
Synchronization primitive cleanup Make some methods infallible, move a bit more work into the platform-independent `sync.rs`, and fix a bug in rwlock unlocking.
2 parents 177d5f8 + 0b6ec57 commit 4fd0aa3

File tree

10 files changed

+176
-121
lines changed

10 files changed

+176
-121
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ There is no way to list all the infinite things Miri cannot do, but the
112112
interpreter will explicitly tell you when it finds something unsupported:
113113

114114
```
115-
error: unsupported operation: Miri does not support threading
115+
error: unsupported operation: can't call foreign function: bind
116116
...
117117
= help: this is likely not a bug in the program; it indicates that the program \
118118
performed an operation that the interpreter does not support

src/shims/foreign_items/posix.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,14 +254,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
254254
"pthread_getspecific" => {
255255
let &[key] = check_arg_count(args)?;
256256
let key = this.force_bits(this.read_scalar(key)?.not_undef()?, key.layout.size)?;
257-
let active_thread = this.get_active_thread()?;
257+
let active_thread = this.get_active_thread();
258258
let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
259259
this.write_scalar(ptr, dest)?;
260260
}
261261
"pthread_setspecific" => {
262262
let &[key, new_ptr] = check_arg_count(args)?;
263263
let key = this.force_bits(this.read_scalar(key)?.not_undef()?, key.layout.size)?;
264-
let active_thread = this.get_active_thread()?;
264+
let active_thread = this.get_active_thread();
265265
let new_ptr = this.read_scalar(new_ptr)?.not_undef()?;
266266
this.machine.tls.store_tls(key, active_thread, this.test_null(new_ptr)?)?;
267267

src/shims/foreign_items/posix/macos.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
9898
let dtor = this.read_scalar(dtor)?.not_undef()?;
9999
let dtor = this.memory.get_fn(dtor)?.as_instance()?;
100100
let data = this.read_scalar(data)?.not_undef()?;
101-
let active_thread = this.get_active_thread()?;
101+
let active_thread = this.get_active_thread();
102102
this.machine.tls.set_macos_thread_dtor(active_thread, dtor, data)?;
103103
}
104104

src/shims/foreign_items/windows.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,14 +163,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
163163
"TlsGetValue" => {
164164
let &[key] = check_arg_count(args)?;
165165
let key = u128::from(this.read_scalar(key)?.to_u32()?);
166-
let active_thread = this.get_active_thread()?;
166+
let active_thread = this.get_active_thread();
167167
let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
168168
this.write_scalar(ptr, dest)?;
169169
}
170170
"TlsSetValue" => {
171171
let &[key, new_ptr] = check_arg_count(args)?;
172172
let key = u128::from(this.read_scalar(key)?.to_u32()?);
173-
let active_thread = this.get_active_thread()?;
173+
let active_thread = this.get_active_thread();
174174
let new_ptr = this.read_scalar(new_ptr)?.not_undef()?;
175175
this.machine.tls.store_tls(key, active_thread, this.test_null(new_ptr)?)?;
176176

@@ -291,7 +291,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
291291
if this.frame().instance.to_string().starts_with("std::sys::windows::") => {
292292
#[allow(non_snake_case)]
293293
let &[_lpCriticalSection] = check_arg_count(args)?;
294-
assert_eq!(this.get_total_thread_count()?, 1, "concurrency on Windows not supported");
294+
assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows not supported");
295295
// Nothing to do, not even a return value.
296296
// (Windows locks are reentrant, and we have only 1 thread,
297297
// so not doing any futher checks here is at least not incorrect.)
@@ -300,7 +300,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
300300
if this.frame().instance.to_string().starts_with("std::sys::windows::") => {
301301
#[allow(non_snake_case)]
302302
let &[_lpCriticalSection] = check_arg_count(args)?;
303-
assert_eq!(this.get_total_thread_count()?, 1, "concurrency on Windows not supported");
303+
assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows not supported");
304304
// There is only one thread, so this always succeeds and returns TRUE
305305
this.write_scalar(Scalar::from_i32(1), dest)?;
306306
}

src/shims/sync.rs

Lines changed: 35 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::convert::TryInto;
22
use std::time::{Duration, SystemTime};
3+
use std::ops::Not;
34

45
use rustc_middle::ty::{layout::TyAndLayout, TyKind, TypeAndMut};
56
use rustc_target::abi::{LayoutOf, Size};
@@ -284,15 +285,16 @@ fn reacquire_cond_mutex<'mir, 'tcx: 'mir>(
284285
thread: ThreadId,
285286
mutex: MutexId,
286287
) -> InterpResult<'tcx> {
288+
ecx.unblock_thread(thread);
287289
if ecx.mutex_is_locked(mutex) {
288-
ecx.mutex_enqueue(mutex, thread);
290+
ecx.mutex_enqueue_and_block(mutex, thread);
289291
} else {
290292
ecx.mutex_lock(mutex, thread);
291-
ecx.unblock_thread(thread)?;
292293
}
293294
Ok(())
294295
}
295296

297+
/// After a thread waiting on a condvar was signalled:
296298
/// Reacquire the conditional variable and remove the timeout callback if any
297299
/// was registered.
298300
fn post_cond_signal<'mir, 'tcx: 'mir>(
@@ -303,24 +305,25 @@ fn post_cond_signal<'mir, 'tcx: 'mir>(
303305
reacquire_cond_mutex(ecx, thread, mutex)?;
304306
// Waiting for the mutex is not included in the waiting time because we need
305307
// to acquire the mutex always even if we get a timeout.
306-
ecx.unregister_timeout_callback_if_exists(thread)
308+
ecx.unregister_timeout_callback_if_exists(thread);
309+
Ok(())
307310
}
308311

309312
/// Release the mutex associated with the condition variable because we are
310313
/// entering the waiting state.
311-
fn release_cond_mutex<'mir, 'tcx: 'mir>(
314+
fn release_cond_mutex_and_block<'mir, 'tcx: 'mir>(
312315
ecx: &mut MiriEvalContext<'mir, 'tcx>,
313316
active_thread: ThreadId,
314317
mutex: MutexId,
315318
) -> InterpResult<'tcx> {
316-
if let Some(old_locked_count) = ecx.mutex_unlock(mutex, active_thread)? {
319+
if let Some(old_locked_count) = ecx.mutex_unlock(mutex, active_thread) {
317320
if old_locked_count != 1 {
318321
throw_unsup_format!("awaiting on a lock acquired multiple times is not supported");
319322
}
320323
} else {
321324
throw_ub_format!("awaiting on unlocked or owned by a different thread mutex");
322325
}
323-
ecx.block_thread(active_thread)?;
326+
ecx.block_thread(active_thread);
324327
Ok(())
325328
}
326329

@@ -411,14 +414,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
411414

412415
let kind = mutex_get_kind(this, mutex_op)?.not_undef()?;
413416
let id = mutex_get_or_create_id(this, mutex_op)?;
414-
let active_thread = this.get_active_thread()?;
417+
let active_thread = this.get_active_thread();
415418

416419
if this.mutex_is_locked(id) {
417420
let owner_thread = this.mutex_get_owner(id);
418421
if owner_thread != active_thread {
419-
// Block the active thread.
420-
this.block_thread(active_thread)?;
421-
this.mutex_enqueue(id, active_thread);
422+
// Enqueue the active thread.
423+
this.mutex_enqueue_and_block(id, active_thread);
422424
Ok(0)
423425
} else {
424426
// Trying to acquire the same mutex again.
@@ -449,7 +451,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
449451

450452
let kind = mutex_get_kind(this, mutex_op)?.not_undef()?;
451453
let id = mutex_get_or_create_id(this, mutex_op)?;
452-
let active_thread = this.get_active_thread()?;
454+
let active_thread = this.get_active_thread();
453455

454456
if this.mutex_is_locked(id) {
455457
let owner_thread = this.mutex_get_owner(id);
@@ -482,9 +484,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
482484

483485
let kind = mutex_get_kind(this, mutex_op)?.not_undef()?;
484486
let id = mutex_get_or_create_id(this, mutex_op)?;
485-
let active_thread = this.get_active_thread()?;
487+
let active_thread = this.get_active_thread();
486488

487-
if let Some(_old_locked_count) = this.mutex_unlock(id, active_thread)? {
489+
if let Some(_old_locked_count) = this.mutex_unlock(id, active_thread) {
488490
// The mutex was locked by the current thread.
489491
Ok(0)
490492
} else {
@@ -528,10 +530,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
528530
let this = self.eval_context_mut();
529531

530532
let id = rwlock_get_or_create_id(this, rwlock_op)?;
531-
let active_thread = this.get_active_thread()?;
533+
let active_thread = this.get_active_thread();
532534

533535
if this.rwlock_is_write_locked(id) {
534-
this.rwlock_enqueue_and_block_reader(id, active_thread)?;
536+
this.rwlock_enqueue_and_block_reader(id, active_thread);
535537
Ok(0)
536538
} else {
537539
this.rwlock_reader_lock(id, active_thread);
@@ -543,7 +545,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
543545
let this = self.eval_context_mut();
544546

545547
let id = rwlock_get_or_create_id(this, rwlock_op)?;
546-
let active_thread = this.get_active_thread()?;
548+
let active_thread = this.get_active_thread();
547549

548550
if this.rwlock_is_write_locked(id) {
549551
this.eval_libc_i32("EBUSY")
@@ -557,22 +559,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
557559
let this = self.eval_context_mut();
558560

559561
let id = rwlock_get_or_create_id(this, rwlock_op)?;
560-
let active_thread = this.get_active_thread()?;
562+
let active_thread = this.get_active_thread();
561563

562564
if this.rwlock_is_locked(id) {
563565
// Note: this will deadlock if the lock is already locked by this
564566
// thread in any way.
565567
//
566568
// Relevant documentation:
567569
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_wrlock.html
568-
// An in depth discussion on this topic:
570+
// An in-depth discussion on this topic:
569571
// https://github.com/rust-lang/rust/issues/53127
570572
//
571573
// FIXME: Detect and report the deadlock proactively. (We currently
572574
// report the deadlock only when no thread can continue execution,
573575
// but we could detect that this lock is already locked and report
574576
// an error.)
575-
this.rwlock_enqueue_and_block_writer(id, active_thread)?;
577+
this.rwlock_enqueue_and_block_writer(id, active_thread);
576578
} else {
577579
this.rwlock_writer_lock(id, active_thread);
578580
}
@@ -584,7 +586,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
584586
let this = self.eval_context_mut();
585587

586588
let id = rwlock_get_or_create_id(this, rwlock_op)?;
587-
let active_thread = this.get_active_thread()?;
589+
let active_thread = this.get_active_thread();
588590

589591
if this.rwlock_is_locked(id) {
590592
this.eval_libc_i32("EBUSY")
@@ -598,17 +600,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
598600
let this = self.eval_context_mut();
599601

600602
let id = rwlock_get_or_create_id(this, rwlock_op)?;
601-
let active_thread = this.get_active_thread()?;
603+
let active_thread = this.get_active_thread();
602604

603605
if this.rwlock_reader_unlock(id, active_thread) {
604606
// The thread was a reader.
605-
if this.rwlock_is_locked(id) {
607+
if this.rwlock_is_locked(id).not() {
606608
// No more readers owning the lock. Give it to a writer if there
607609
// is any.
608-
if let Some(writer) = this.rwlock_dequeue_writer(id) {
609-
this.unblock_thread(writer)?;
610-
this.rwlock_writer_lock(id, writer);
611-
}
610+
this.rwlock_dequeue_and_lock_writer(id);
612611
}
613612
Ok(0)
614613
} else if Some(active_thread) == this.rwlock_writer_unlock(id) {
@@ -617,15 +616,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
617616
// We are prioritizing writers here against the readers. As a
618617
// result, not only readers can starve writers, but also writers can
619618
// starve readers.
620-
if let Some(writer) = this.rwlock_dequeue_writer(id) {
621-
// Give the lock to another writer.
622-
this.unblock_thread(writer)?;
623-
this.rwlock_writer_lock(id, writer);
619+
if this.rwlock_dequeue_and_lock_writer(id) {
620+
// Someone got the write lock, nice.
624621
} else {
625622
// Give the lock to all readers.
626-
while let Some(reader) = this.rwlock_dequeue_reader(id) {
627-
this.unblock_thread(reader)?;
628-
this.rwlock_reader_lock(id, reader);
623+
while this.rwlock_dequeue_and_lock_reader(id) {
624+
// Rinse and repeat.
629625
}
630626
}
631627
Ok(0)
@@ -753,9 +749,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
753749

754750
let id = cond_get_or_create_id(this, cond_op)?;
755751
let mutex_id = mutex_get_or_create_id(this, mutex_op)?;
756-
let active_thread = this.get_active_thread()?;
752+
let active_thread = this.get_active_thread();
757753

758-
release_cond_mutex(this, active_thread, mutex_id)?;
754+
release_cond_mutex_and_block(this, active_thread, mutex_id)?;
759755
this.condvar_wait(id, active_thread, mutex_id);
760756

761757
Ok(0)
@@ -774,9 +770,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
774770

775771
let id = cond_get_or_create_id(this, cond_op)?;
776772
let mutex_id = mutex_get_or_create_id(this, mutex_op)?;
777-
let active_thread = this.get_active_thread()?;
773+
let active_thread = this.get_active_thread();
778774

779-
release_cond_mutex(this, active_thread, mutex_id)?;
775+
release_cond_mutex_and_block(this, active_thread, mutex_id)?;
780776
this.condvar_wait(id, active_thread, mutex_id);
781777

782778
// We return success for now and override it in the timeout callback.
@@ -823,7 +819,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
823819

824820
Ok(())
825821
}),
826-
)?;
822+
);
827823

828824
Ok(())
829825
}
@@ -833,7 +829,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
833829

834830
let id = cond_get_or_create_id(this, cond_op)?;
835831
if this.condvar_is_awaited(id) {
836-
throw_ub_format!("destroyed an awaited conditional variable");
832+
throw_ub_format!("destroying an awaited conditional variable");
837833
}
838834
cond_set_id(this, cond_op, ScalarMaybeUninit::Uninit)?;
839835
cond_set_clock_id(this, cond_op, ScalarMaybeUninit::Uninit)?;

src/shims/thread.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
1919
For example, Miri does not detect data races yet.",
2020
);
2121

22-
let new_thread_id = this.create_thread()?;
22+
let new_thread_id = this.create_thread();
2323
// Also switch to new thread so that we can push the first stackframe.
24-
let old_thread_id = this.set_active_thread(new_thread_id)?;
24+
let old_thread_id = this.set_active_thread(new_thread_id);
2525

2626
let thread_info_place = this.deref_operand(thread)?;
2727
this.write_scalar(
@@ -47,7 +47,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
4747
StackPopCleanup::None { cleanup: true },
4848
)?;
4949

50-
this.set_active_thread(old_thread_id)?;
50+
this.set_active_thread(old_thread_id);
5151

5252
Ok(0)
5353
}
@@ -82,7 +82,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
8282
fn pthread_self(&mut self, dest: PlaceTy<'tcx, Tag>) -> InterpResult<'tcx> {
8383
let this = self.eval_context_mut();
8484

85-
let thread_id = this.get_active_thread()?;
85+
let thread_id = this.get_active_thread();
8686
this.write_scalar(Scalar::from_uint(thread_id.to_u32(), dest.layout.size), dest)
8787
}
8888

@@ -105,10 +105,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
105105
// byte. Since `read_c_str` returns the string without the null
106106
// byte, we need to truncate to 15.
107107
name.truncate(15);
108-
this.set_active_thread_name(name)?;
108+
this.set_active_thread_name(name);
109109
} else if option == this.eval_libc_i32("PR_GET_NAME")? {
110110
let address = this.read_scalar(arg2)?.not_undef()?;
111-
let mut name = this.get_active_thread_name()?.to_vec();
111+
let mut name = this.get_active_thread_name().to_vec();
112112
name.push(0u8);
113113
assert!(name.len() <= 16);
114114
this.memory.write_bytes(address, name)?;
@@ -127,15 +127,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
127127
this.assert_target_os("macos", "pthread_setname_np");
128128

129129
let name = this.memory.read_c_str(name)?.to_owned();
130-
this.set_active_thread_name(name)?;
130+
this.set_active_thread_name(name);
131131

132132
Ok(())
133133
}
134134

135135
fn sched_yield(&mut self) -> InterpResult<'tcx, i32> {
136136
let this = self.eval_context_mut();
137137

138-
this.yield_active_thread()?;
138+
this.yield_active_thread();
139139

140140
Ok(0)
141141
}

0 commit comments

Comments
 (0)