Skip to content

Commit fdfd56b

Browse files
committed
Small changes.
1 parent 3da61fa commit fdfd56b

File tree

5 files changed

+143
-61
lines changed

5 files changed

+143
-61
lines changed

src/shims/sync.rs

Lines changed: 27 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ fn condattr_set_clock_id<'mir, 'tcx: 'mir>(
202202
// Our chosen memory layout for the emulated conditional variable (does not have
203203
// to match the platform layout!):
204204

205+
// bytes 0-3: reserved for signature on macOS
205206
// bytes 4-7: the conditional variable id as u32 or 0 if id is not assigned yet.
206207
// bytes 8-11: the clock id constant as i32
207208

@@ -275,19 +276,13 @@ fn release_cond_mutex<'mir, 'tcx: 'mir>(
275276
active_thread: ThreadId,
276277
mutex: MutexId,
277278
) -> InterpResult<'tcx> {
278-
if let Some((owner_thread, current_locked_count)) = ecx.mutex_unlock(mutex) {
279-
if current_locked_count != 0 {
280-
throw_unsup_format!("awaiting on multiple times acquired lock is not supported");
279+
if let Some((old_owner_thread, old_locked_count)) = ecx.mutex_unlock(mutex)? {
280+
if old_locked_count != 1 {
281+
throw_unsup_format!("awaiting on a lock acquired multiple times is not supported");
281282
}
282-
if owner_thread != active_thread {
283+
if old_owner_thread != active_thread {
283284
throw_ub_format!("awaiting on a mutex owned by a different thread");
284285
}
285-
if let Some(thread) = ecx.mutex_dequeue(mutex) {
286-
// We have at least one thread waiting on this mutex. Transfer
287-
// ownership to it.
288-
ecx.mutex_lock(mutex, thread);
289-
ecx.unblock_thread(thread)?;
290-
}
291286
} else {
292287
throw_ub_format!("awaiting on unlocked mutex");
293288
}
@@ -349,7 +344,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
349344
mutexattr_get_kind(this, attr_op)?.not_undef()?
350345
};
351346

352-
let _ = mutex_get_or_create_id(this, mutex_op)?;
347+
// Write 0 to use the same code path as the static initializers.
348+
mutex_set_id(this, mutex_op, Scalar::from_i32(0))?;
349+
353350
mutex_set_kind(this, mutex_op, kind)?;
354351

355352
Ok(0)
@@ -427,19 +424,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
427424
let kind = mutex_get_kind(this, mutex_op)?.not_undef()?;
428425
let id = mutex_get_or_create_id(this, mutex_op)?;
429426

430-
if let Some((owner_thread, current_locked_count)) = this.mutex_unlock(id) {
431-
if owner_thread != this.get_active_thread()? {
427+
if let Some((old_owner_thread, _old_locked_count)) = this.mutex_unlock(id)? {
428+
if old_owner_thread != this.get_active_thread()? {
432429
throw_ub_format!("called pthread_mutex_unlock on a mutex owned by another thread");
433430
}
434-
if current_locked_count == 0 {
435-
// The mutex is unlocked.
436-
if let Some(thread) = this.mutex_dequeue(id) {
437-
// We have at least one thread waiting on this mutex. Transfer
438-
// ownership to it.
439-
this.mutex_lock(id, thread);
440-
this.unblock_thread(thread)?;
441-
}
442-
}
443431
Ok(0)
444432
} else {
445433
if kind == this.eval_libc("PTHREAD_MUTEX_NORMAL")? {
@@ -476,11 +464,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
476464
let active_thread = this.get_active_thread()?;
477465

478466
if this.rwlock_is_write_locked(id) {
479-
this.rwlock_enqueue_reader(id, active_thread);
480-
this.block_thread(active_thread)?;
467+
this.rwlock_enqueue_and_block_reader(id, active_thread)?;
481468
Ok(0)
482469
} else {
483-
this.rwlock_reader_add(id, active_thread);
470+
this.rwlock_reader_lock(id, active_thread);
484471
Ok(0)
485472
}
486473
}
@@ -494,7 +481,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
494481
if this.rwlock_is_write_locked(id) {
495482
this.eval_libc_i32("EBUSY")
496483
} else {
497-
this.rwlock_reader_add(id, active_thread);
484+
this.rwlock_reader_lock(id, active_thread);
498485
Ok(0)
499486
}
500487
}
@@ -506,10 +493,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
506493
let active_thread = this.get_active_thread()?;
507494

508495
if this.rwlock_is_locked(id) {
509-
this.block_thread(active_thread)?;
510-
this.rwlock_enqueue_writer(id, active_thread);
496+
this.rwlock_enqueue_and_block_writer(id, active_thread)?;
511497
} else {
512-
this.rwlock_writer_set(id, active_thread);
498+
this.rwlock_writer_lock(id, active_thread);
513499
}
514500

515501
Ok(0)
@@ -524,7 +510,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
524510
if this.rwlock_is_locked(id) {
525511
this.eval_libc_i32("EBUSY")
526512
} else {
527-
this.rwlock_writer_set(id, active_thread);
513+
this.rwlock_writer_lock(id, active_thread);
528514
Ok(0)
529515
}
530516
}
@@ -535,18 +521,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
535521
let id = rwlock_get_or_create_id(this, rwlock_op)?;
536522
let active_thread = this.get_active_thread()?;
537523

538-
if this.rwlock_reader_remove(id, active_thread) {
524+
if this.rwlock_reader_unlock(id, active_thread) {
539525
// The thread was a reader.
540526
if this.rwlock_is_locked(id) {
541527
// No more readers owning the lock. Give it to a writer if there
542528
// is any.
543529
if let Some(writer) = this.rwlock_dequeue_writer(id) {
544530
this.unblock_thread(writer)?;
545-
this.rwlock_writer_set(id, writer);
531+
this.rwlock_writer_lock(id, writer);
546532
}
547533
}
548534
Ok(0)
549-
} else if Some(active_thread) == this.rwlock_writer_remove(id) {
535+
} else if Some(active_thread) == this.rwlock_writer_unlock(id) {
550536
// The thread was a writer.
551537
//
552538
// We are prioritizing writers here against the readers. As a
@@ -555,12 +541,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
555541
if let Some(writer) = this.rwlock_dequeue_writer(id) {
556542
// Give the lock to another writer.
557543
this.unblock_thread(writer)?;
558-
this.rwlock_writer_set(id, writer);
544+
this.rwlock_writer_lock(id, writer);
559545
} else {
560546
// Give the lock to all readers.
561547
while let Some(reader) = this.rwlock_dequeue_reader(id) {
562548
this.unblock_thread(reader)?;
563-
this.rwlock_reader_add(id, reader);
549+
this.rwlock_reader_lock(id, reader);
564550
}
565551
}
566552
Ok(0)
@@ -586,6 +572,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
586572
fn pthread_condattr_init(&mut self, attr_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
587573
let this = self.eval_context_mut();
588574

575+
// The default value of the clock attribute shall refer to the system
576+
// clock.
577+
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_condattr_setclock.html
589578
let default_clock_id = this.eval_libc("CLOCK_REALTIME")?;
590579
condattr_set_clock_id(this, attr_op, default_clock_id)?;
591580

@@ -647,7 +636,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
647636
condattr_get_clock_id(this, attr_op)?.not_undef()?
648637
};
649638

650-
let _ = cond_get_or_create_id(this, cond_op)?;
639+
// Write 0 to use the same code path as the static initializers.
640+
cond_set_id(this, cond_op, Scalar::from_i32(0))?;
641+
651642
cond_set_clock_id(this, cond_op, clock_id)?;
652643

653644
Ok(0)

src/sync.rs

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::collections::{hash_map::Entry, HashMap, VecDeque};
22
use std::convert::TryFrom;
33
use std::num::NonZeroU32;
4+
use std::ops::Not;
45

56
use rustc_index::vec::{Idx, IndexVec};
67

@@ -142,34 +143,47 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
142143
mutex.lock_count = mutex.lock_count.checked_add(1).unwrap();
143144
}
144145

145-
/// Unlock by decreasing the lock count. If the lock count reaches 0, unset
146-
/// the owner.
147-
fn mutex_unlock(&mut self, id: MutexId) -> Option<(ThreadId, usize)> {
146+
/// Try unlocking by decreasing the lock count and returning the old owner
147+
/// and the old lock count. If the lock count reaches 0, release the lock
148+
/// and potentially give to a new owner. If the lock was not locked, return
149+
/// `None`.
150+
///
151+
/// Note: It is the caller's responsibility to check that the thread that
152+
/// unlocked the lock actually is the same one, which owned it.
153+
fn mutex_unlock(&mut self, id: MutexId) -> InterpResult<'tcx, Option<(ThreadId, usize)>> {
148154
let this = self.eval_context_mut();
149155
let mutex = &mut this.machine.threads.sync.mutexes[id];
150156
if let Some(current_owner) = mutex.owner {
151-
mutex.lock_count = mutex
152-
.lock_count
157+
// Mutex is locked.
158+
let old_lock_count = mutex.lock_count;
159+
mutex.lock_count = old_lock_count
153160
.checked_sub(1)
154161
.expect("invariant violation: lock_count == 0 iff the thread is unlocked");
155162
if mutex.lock_count == 0 {
156163
mutex.owner = None;
164+
// The mutex is completely unlocked. Try transfering ownership
165+
// to another thread.
166+
if let Some(new_owner) = this.mutex_dequeue(id) {
167+
this.mutex_lock(id, new_owner);
168+
this.unblock_thread(new_owner)?;
169+
}
157170
}
158-
Some((current_owner, mutex.lock_count))
171+
Ok(Some((current_owner, old_lock_count)))
159172
} else {
160-
None
173+
// Mutex is unlocked.
174+
Ok(None)
161175
}
162176
}
163177

164178
#[inline]
165-
/// Take a thread out the queue waiting for the lock.
179+
/// Put the thread into the queue waiting for the lock.
166180
fn mutex_enqueue(&mut self, id: MutexId, thread: ThreadId) {
167181
let this = self.eval_context_mut();
168182
this.machine.threads.sync.mutexes[id].queue.push_back(thread);
169183
}
170184

171185
#[inline]
172-
/// Take a thread out the queue waiting for the lock.
186+
/// Take a thread out of the queue waiting for the lock.
173187
fn mutex_dequeue(&mut self, id: MutexId) -> Option<ThreadId> {
174188
let this = self.eval_context_mut();
175189
this.machine.threads.sync.mutexes[id].queue.pop_front()
@@ -187,7 +201,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
187201
fn rwlock_is_locked(&mut self, id: RwLockId) -> bool {
188202
let this = self.eval_context_mut();
189203
this.machine.threads.sync.rwlocks[id].writer.is_some()
190-
|| !this.machine.threads.sync.rwlocks[id].readers.is_empty()
204+
|| this.machine.threads.sync.rwlocks[id].readers.is_empty().not()
191205
}
192206

193207
#[inline]
@@ -197,16 +211,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
197211
this.machine.threads.sync.rwlocks[id].writer.is_some()
198212
}
199213

200-
/// Add a reader that collectively with other readers owns the lock.
201-
fn rwlock_reader_add(&mut self, id: RwLockId, reader: ThreadId) {
214+
/// Read-lock the lock by adding the `reader` the list of threads that own
215+
/// this lock.
216+
fn rwlock_reader_lock(&mut self, id: RwLockId, reader: ThreadId) {
202217
let this = self.eval_context_mut();
203218
assert!(!this.rwlock_is_write_locked(id), "the lock is write locked");
204219
let count = this.machine.threads.sync.rwlocks[id].readers.entry(reader).or_insert(0);
205-
*count += 1;
220+
*count = count.checked_add(1).expect("the reader counter overflowed");
206221
}
207222

208-
/// Try removing the reader. Returns `true` if succeeded.
209-
fn rwlock_reader_remove(&mut self, id: RwLockId, reader: ThreadId) -> bool {
223+
/// Try read-unlock the lock for `reader`. Returns `true` if succeeded,
224+
/// `false` if this `reader` did not hold the lock.
225+
fn rwlock_reader_unlock(&mut self, id: RwLockId, reader: ThreadId) -> bool {
210226
let this = self.eval_context_mut();
211227
match this.machine.threads.sync.rwlocks[id].readers.entry(reader) {
212228
Entry::Occupied(mut entry) => {
@@ -222,41 +238,51 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
222238
}
223239

224240
#[inline]
225-
/// Put the reader in the queue waiting for the lock.
226-
fn rwlock_enqueue_reader(&mut self, id: RwLockId, reader: ThreadId) {
241+
/// Put the reader in the queue waiting for the lock and block it.
242+
fn rwlock_enqueue_and_block_reader(
243+
&mut self,
244+
id: RwLockId,
245+
reader: ThreadId,
246+
) -> InterpResult<'tcx> {
227247
let this = self.eval_context_mut();
228248
assert!(this.rwlock_is_write_locked(id), "queueing on not write locked lock");
229249
this.machine.threads.sync.rwlocks[id].reader_queue.push_back(reader);
250+
this.block_thread(reader)
230251
}
231252

232253
#[inline]
233-
/// Take the reader out the queue waiting for the lock.
254+
/// Take a reader out the queue waiting for the lock.
234255
fn rwlock_dequeue_reader(&mut self, id: RwLockId) -> Option<ThreadId> {
235256
let this = self.eval_context_mut();
236257
this.machine.threads.sync.rwlocks[id].reader_queue.pop_front()
237258
}
238259

239260
#[inline]
240261
/// Lock by setting the writer that owns the lock.
241-
fn rwlock_writer_set(&mut self, id: RwLockId, writer: ThreadId) {
262+
fn rwlock_writer_lock(&mut self, id: RwLockId, writer: ThreadId) {
242263
let this = self.eval_context_mut();
243264
assert!(!this.rwlock_is_locked(id), "the lock is already locked");
244265
this.machine.threads.sync.rwlocks[id].writer = Some(writer);
245266
}
246267

247268
#[inline]
248-
/// Try removing the writer.
249-
fn rwlock_writer_remove(&mut self, id: RwLockId) -> Option<ThreadId> {
269+
/// Try to unlock by removing the writer.
270+
fn rwlock_writer_unlock(&mut self, id: RwLockId) -> Option<ThreadId> {
250271
let this = self.eval_context_mut();
251272
this.machine.threads.sync.rwlocks[id].writer.take()
252273
}
253274

254275
#[inline]
255276
/// Put the writer in the queue waiting for the lock.
256-
fn rwlock_enqueue_writer(&mut self, id: RwLockId, writer: ThreadId) {
277+
fn rwlock_enqueue_and_block_writer(
278+
&mut self,
279+
id: RwLockId,
280+
writer: ThreadId,
281+
) -> InterpResult<'tcx> {
257282
let this = self.eval_context_mut();
258283
assert!(this.rwlock_is_locked(id), "queueing on unlocked lock");
259284
this.machine.threads.sync.rwlocks[id].writer_queue.push_back(writer);
285+
this.block_thread(writer)
260286
}
261287

262288
#[inline]

src/thread.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub enum SchedulingAction {
3232
Stop,
3333
}
3434

35-
/// Timeout timeout_callbacks can be created by synchronization primitives to tell the
35+
/// Timeout callbacks can be created by synchronization primitives to tell the
3636
/// scheduler that they should be called once some period of time passes.
3737
type TimeoutCallback<'mir, 'tcx> =
3838
Box<dyn FnOnce(&mut InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>) -> InterpResult<'tcx> + 'tcx>;
@@ -189,7 +189,7 @@ struct TimeoutCallbackInfo<'mir, 'tcx> {
189189

190190
impl<'mir, 'tcx> std::fmt::Debug for TimeoutCallbackInfo<'mir, 'tcx> {
191191
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
192-
write!(f, "CallBack({:?})", self.call_time)
192+
write!(f, "TimeoutCallback({:?})", self.call_time)
193193
}
194194
}
195195

@@ -394,7 +394,8 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
394394

395395
/// Get a callback that is ready to be called.
396396
fn get_ready_callback(&mut self) -> Option<(ThreadId, TimeoutCallback<'mir, 'tcx>)> {
397-
// We use a for loop here to make the scheduler more deterministic.
397+
// We iterate over all threads in the order of their indices because
398+
// this allows us to have a deterministic scheduler.
398399
for thread in self.threads.indices() {
399400
match self.timeout_callbacks.entry(thread) {
400401
Entry::Occupied(entry) =>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// ignore-windows: No libc on Windows
2+
3+
#![feature(rustc_private)]
4+
5+
extern crate libc;
6+
7+
use std::cell::UnsafeCell;
8+
use std::sync::Arc;
9+
use std::thread;
10+
11+
struct RwLock(UnsafeCell<libc::pthread_rwlock_t>);
12+
13+
unsafe impl Send for RwLock {}
14+
unsafe impl Sync for RwLock {}
15+
16+
fn new_lock() -> Arc<RwLock> {
17+
Arc::new(RwLock(UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER)))
18+
}
19+
20+
fn main() {
21+
unsafe {
22+
let lock = new_lock();
23+
assert_eq!(libc::pthread_rwlock_rdlock(lock.0.get() as *mut _), 0);
24+
25+
let lock_copy = lock.clone();
26+
thread::spawn(move || {
27+
assert_eq!(libc::pthread_rwlock_unlock(lock_copy.0.get() as *mut _), 0); //~ ERROR: Undefined Behavior: unlocked an rwlock that was not locked by the active thread
28+
})
29+
.join()
30+
.unwrap();
31+
}
32+
}

0 commit comments

Comments
 (0)