From: Vytautas Astrauskas Date: Sat, 18 Apr 2020 19:25:11 +0000 (-0700) Subject: Add concurrency tests. X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;ds=sidebyside;h=421be273cc389a5d426063f71cba82bf1c364f00;p=rust.git Add concurrency tests. --- diff --git a/src/shims/sync.rs b/src/shims/sync.rs index d8a00156384..6a1ea108dbb 100644 --- a/src/shims/sync.rs +++ b/src/shims/sync.rs @@ -532,6 +532,7 @@ fn pthread_rwlock_trywrlock(&mut self, rwlock_op: OpTy<'tcx, Tag>) -> InterpResu } } + // FIXME: We should check that this lock was locked by the active thread. fn pthread_rwlock_unlock(&mut self, rwlock_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); diff --git a/src/shims/tls.rs b/src/shims/tls.rs index 722b24d7475..89ec1659659 100644 --- a/src/shims/tls.rs +++ b/src/shims/tls.rs @@ -233,6 +233,8 @@ fn run_windows_tls_dtors(&mut self) -> InterpResult<'tcx> { /// /// Note: on Windows OS this function is a no-op because we do not support /// concurrency on Windows yet. + /// + /// FIXME: we do not support yet deallocation of thread local statics. fn run_tls_dtors_for_active_thread(&mut self) -> InterpResult<'tcx> { let this = self.eval_context_mut(); if this.tcx.sess.target.target.target_os == "windows" { diff --git a/src/thread.rs b/src/thread.rs index 31296ad96ff..ab6a4c94db8 100644 --- a/src/thread.rs +++ b/src/thread.rs @@ -164,13 +164,13 @@ fn default() -> Self { impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { /// Check if we have an allocation for the given thread local static for the /// active thread. - pub fn get_thread_local_alloc_id(&self, def_id: DefId) -> Option { + fn get_thread_local_alloc_id(&self, def_id: DefId) -> Option { self.thread_local_alloc_ids.borrow().get(&(def_id, self.active_thread)).cloned() } /// Set the allocation id as the allocation id of the given thread local /// static for the active thread. - pub fn set_thread_local_alloc_id(&self, def_id: DefId, new_alloc_id: AllocId) { + fn set_thread_local_alloc_id(&self, def_id: DefId, new_alloc_id: AllocId) { assert!( self.thread_local_alloc_ids .borrow_mut() diff --git a/tests/compile-fail/concurrency/dangling_tls_lib.rs b/tests/compile-fail/concurrency/dangling_tls_lib.rs new file mode 100644 index 00000000000..ad12c107bff --- /dev/null +++ b/tests/compile-fail/concurrency/dangling_tls_lib.rs @@ -0,0 +1,46 @@ +// ignore-windows + +#![feature(thread_local_internals)] + +use std::cell::RefCell; +use std::thread; + +static A: std::thread::LocalKey> = { + #[inline] + fn __init() -> RefCell { + RefCell::new(0) + } + + unsafe fn __getit() -> Option<&'static RefCell> { + static __KEY: std::thread::__OsLocalKeyInner> = + std::thread::__OsLocalKeyInner::new(); + __KEY.get(__init) + } + + unsafe { std::thread::LocalKey::new(__getit) } +}; + +struct Sender(*mut u8); + +unsafe impl Send for Sender {} + +fn main() { + A.with(|f| { + assert_eq!(*f.borrow(), 0); + *f.borrow_mut() = 4; + }); + + let handle = thread::spawn(|| { + let ptr = A.with(|f| { + assert_eq!(*f.borrow(), 0); + *f.borrow_mut() = 5; + &mut *f.borrow_mut() as *mut u8 + }); + Sender(ptr) + }); + let ptr = handle.join().unwrap().0; + A.with(|f| { + assert_eq!(*f.borrow(), 4); + }); + let _x = unsafe { *ptr }; //~ ERROR Undefined Behavior +} diff --git a/tests/compile-fail/concurrency/libc_pthread_mutex_deadlock.rs b/tests/compile-fail/concurrency/libc_pthread_mutex_deadlock.rs new file mode 100644 index 00000000000..5d04635a36c --- /dev/null +++ b/tests/compile-fail/concurrency/libc_pthread_mutex_deadlock.rs @@ -0,0 +1,32 @@ +// ignore-windows: No libc on Windows + +#![feature(rustc_private)] + +extern crate libc; + +use std::cell::UnsafeCell; +use std::sync::Arc; +use std::thread; + +struct Mutex(UnsafeCell); + +unsafe impl Send for Mutex {} +unsafe impl Sync for Mutex {} + +fn new_lock() -> Arc { + Arc::new(Mutex(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER))) +} + +fn main() { + unsafe { + let lock = new_lock(); + assert_eq!(libc::pthread_mutex_lock(lock.0.get() as *mut _), 0); + + let lock_copy = lock.clone(); + thread::spawn(move || { + assert_eq!(libc::pthread_mutex_lock(lock_copy.0.get() as *mut _), 0); //~ ERROR: deadlock + }) + .join() + .unwrap(); + } +} diff --git a/tests/compile-fail/concurrency/libc_pthread_mutex_wrong_owner.rs b/tests/compile-fail/concurrency/libc_pthread_mutex_wrong_owner.rs new file mode 100644 index 00000000000..3009721abe2 --- /dev/null +++ b/tests/compile-fail/concurrency/libc_pthread_mutex_wrong_owner.rs @@ -0,0 +1,32 @@ +// ignore-windows: No libc on Windows + +#![feature(rustc_private)] + +extern crate libc; + +use std::cell::UnsafeCell; +use std::sync::Arc; +use std::thread; + +struct Mutex(UnsafeCell); + +unsafe impl Send for Mutex {} +unsafe impl Sync for Mutex {} + +fn new_lock() -> Arc { + Arc::new(Mutex(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER))) +} + +fn main() { + unsafe { + let lock = new_lock(); + assert_eq!(libc::pthread_mutex_lock(lock.0.get() as *mut _), 0); + + let lock_copy = lock.clone(); + thread::spawn(move || { + 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 + }) + .join() + .unwrap(); + } +} diff --git a/tests/compile-fail/concurrency/libc_pthread_rwlock_write_read_deadlock.rs b/tests/compile-fail/concurrency/libc_pthread_rwlock_write_read_deadlock.rs new file mode 100644 index 00000000000..19dce431c8b --- /dev/null +++ b/tests/compile-fail/concurrency/libc_pthread_rwlock_write_read_deadlock.rs @@ -0,0 +1,32 @@ +// ignore-windows: No libc on Windows + +#![feature(rustc_private)] + +extern crate libc; + +use std::cell::UnsafeCell; +use std::sync::Arc; +use std::thread; + +struct RwLock(UnsafeCell); + +unsafe impl Send for RwLock {} +unsafe impl Sync for RwLock {} + +fn new_lock() -> Arc { + Arc::new(RwLock(UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER))) +} + +fn main() { + unsafe { + let lock = new_lock(); + assert_eq!(libc::pthread_rwlock_rdlock(lock.0.get() as *mut _), 0); + + let lock_copy = lock.clone(); + thread::spawn(move || { + assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mut _), 0); //~ ERROR: deadlock + }) + .join() + .unwrap(); + } +} diff --git a/tests/compile-fail/concurrency/libc_pthread_rwlock_write_write_deadlock.rs b/tests/compile-fail/concurrency/libc_pthread_rwlock_write_write_deadlock.rs new file mode 100644 index 00000000000..098c1c2fe26 --- /dev/null +++ b/tests/compile-fail/concurrency/libc_pthread_rwlock_write_write_deadlock.rs @@ -0,0 +1,32 @@ +// ignore-windows: No libc on Windows + +#![feature(rustc_private)] + +extern crate libc; + +use std::cell::UnsafeCell; +use std::sync::Arc; +use std::thread; + +struct RwLock(UnsafeCell); + +unsafe impl Send for RwLock {} +unsafe impl Sync for RwLock {} + +fn new_lock() -> Arc { + Arc::new(RwLock(UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER))) +} + +fn main() { + unsafe { + let lock = new_lock(); + assert_eq!(libc::pthread_rwlock_wrlock(lock.0.get() as *mut _), 0); + + let lock_copy = lock.clone(); + thread::spawn(move || { + assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mut _), 0); //~ ERROR: deadlock + }) + .join() + .unwrap(); + } +} diff --git a/tests/run-pass/concurrency/locks.rs b/tests/run-pass/concurrency/locks.rs index 3c8373691b8..90c10b8ffe2 100644 --- a/tests/run-pass/concurrency/locks.rs +++ b/tests/run-pass/concurrency/locks.rs @@ -1,11 +1,9 @@ // ignore-windows -//! This test just calls the relevant APIs to check if Miri crashes. - -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, RwLock}; use std::thread; -fn main() { +fn check_mutex() { let data = Arc::new(Mutex::new(0)); let mut threads = Vec::new(); @@ -27,3 +25,49 @@ fn main() { let data = Arc::try_unwrap(data).unwrap().into_inner().unwrap(); assert_eq!(data, 3); } + +fn check_rwlock_write() { + let data = Arc::new(RwLock::new(0)); + let mut threads = Vec::new(); + + for _ in 0..3 { + let data = Arc::clone(&data); + let thread = thread::spawn(move || { + let mut data = data.write().unwrap(); + *data += 1; + }); + threads.push(thread); + } + + for thread in threads { + thread.join().unwrap(); + } + + assert!(data.try_write().is_ok()); + + let data = Arc::try_unwrap(data).unwrap().into_inner().unwrap(); + assert_eq!(data, 3); +} + +fn check_rwlock_read_no_deadlock() { + let l1 = Arc::new(RwLock::new(0)); + let l2 = Arc::new(RwLock::new(0)); + + let l1_copy = Arc::clone(&l1); + let l2_copy = Arc::clone(&l2); + let _guard1 = l1.read().unwrap(); + let handle = thread::spawn(move || { + let _guard2 = l2_copy.read().unwrap(); + thread::yield_now(); + let _guard1 = l1_copy.read().unwrap(); + }); + thread::yield_now(); + let _guard2 = l2.read().unwrap(); + handle.join().unwrap(); +} + +fn main() { + check_mutex(); + check_rwlock_write(); + check_rwlock_read_no_deadlock(); +}