| //! Regression test for <https://github.com/rust-lang/rust/issues/33770>. |
| //! Ensure both `Mutex` and `RwLock` cannot be locked twice on same |
| //! thread. |
| //! |
| //! This was possible as mutexes were initialized with `PTHREAD_MUTEX_DEFAULT` |
| //! for which attempt to relock from the same thread is considered undefined |
| //! behaviour, and during glibc's lock-elision transaction lock appeared |
| //! unlocked, which allowed to lock it again from the same thread. |
| //! |
| //! This resulted in ability to aquire 2 mutable references at the same time. |
| //@ run-pass |
| //@ needs-subprocess |
| |
| use std::process::{Command, Stdio}; |
| use std::env; |
| use std::sync::{Mutex, RwLock}; |
| use std::time::Duration; |
| use std::thread; |
| |
| fn test_mutex() { |
| let m = Mutex::new(0); |
| let _g = m.lock().unwrap(); |
| let _g2 = m.lock().unwrap(); |
| } |
| |
| fn test_try_mutex() { |
| let m = Mutex::new(0); |
| let _g = m.lock().unwrap(); |
| let _g2 = m.try_lock().unwrap(); |
| } |
| |
| fn test_rwlock_ww() { |
| let m = RwLock::new(0); |
| let _g = m.write().unwrap(); |
| let _g2 = m.write().unwrap(); |
| } |
| |
| fn test_try_rwlock_ww() { |
| let m = RwLock::new(0); |
| let _g = m.write().unwrap(); |
| let _g2 = m.try_write().unwrap(); |
| } |
| |
| fn test_rwlock_rw() { |
| let m = RwLock::new(0); |
| let _g = m.read().unwrap(); |
| let _g2 = m.write().unwrap(); |
| } |
| |
| fn test_try_rwlock_rw() { |
| let m = RwLock::new(0); |
| let _g = m.read().unwrap(); |
| let _g2 = m.try_write().unwrap(); |
| } |
| |
| fn test_rwlock_wr() { |
| let m = RwLock::new(0); |
| let _g = m.write().unwrap(); |
| let _g2 = m.read().unwrap(); |
| } |
| |
| fn test_try_rwlock_wr() { |
| let m = RwLock::new(0); |
| let _g = m.write().unwrap(); |
| let _g2 = m.try_read().unwrap(); |
| } |
| |
| fn main() { |
| let args: Vec<String> = env::args().collect(); |
| if args.len() > 1 { |
| match &*args[1] { |
| "mutex" => test_mutex(), |
| "try_mutex" => test_try_mutex(), |
| "rwlock_ww" => test_rwlock_ww(), |
| "try_rwlock_ww" => test_try_rwlock_ww(), |
| "rwlock_rw" => test_rwlock_rw(), |
| "try_rwlock_rw" => test_try_rwlock_rw(), |
| "rwlock_wr" => test_rwlock_wr(), |
| "try_rwlock_wr" => test_try_rwlock_wr(), |
| _ => unreachable!(), |
| } |
| // If we reach this point then the test failed |
| println!("TEST FAILED: {}", args[1]); |
| } else { |
| let mut v = vec![]; |
| v.push(Command::new(&args[0]).arg("mutex").stderr(Stdio::null()).spawn().unwrap()); |
| v.push(Command::new(&args[0]).arg("try_mutex").stderr(Stdio::null()).spawn().unwrap()); |
| v.push(Command::new(&args[0]).arg("rwlock_ww").stderr(Stdio::null()).spawn().unwrap()); |
| v.push(Command::new(&args[0]).arg("try_rwlock_ww").stderr(Stdio::null()).spawn().unwrap()); |
| v.push(Command::new(&args[0]).arg("rwlock_rw").stderr(Stdio::null()).spawn().unwrap()); |
| v.push(Command::new(&args[0]).arg("try_rwlock_rw").stderr(Stdio::null()).spawn().unwrap()); |
| v.push(Command::new(&args[0]).arg("rwlock_wr").stderr(Stdio::null()).spawn().unwrap()); |
| v.push(Command::new(&args[0]).arg("try_rwlock_wr").stderr(Stdio::null()).spawn().unwrap()); |
| |
| thread::sleep(Duration::new(1, 0)); |
| |
| // Make sure all subprocesses either panicked or were killed because they deadlocked |
| for mut c in v { |
| c.kill().ok(); |
| assert!(!c.wait().unwrap().success()); |
| } |
| } |
| } |