Description
I'm having some weird problems with the Mutex
struct provided by the standard library of Rust. The release of mutex guards seems to be inconsistent across platforms. Because of that, a mutex lock isn't released properly on specific platforms.
I'm not 100% sure if this caused by a bug, or because of defined platform-specific behavior. Based on the issue contributing document I figured I'd post this issue anyway as I couldn't find any relevant issue.
Problem description
In my case, I'm using an accessor struct, that wraps a mutex guard (lock). This accessor provides various methods that help you easily access data through the mutex guard. The mutex guard should be dropped when the accessor struct is dropped, this should release the lock on the mutex that is held by the guard.y case, I'm using an accessor struct, that wraps a mutex guard (lock). This accessor provides various methods that help you easily access data through the
When running the troublesome code on an x86_46
machine, everything works fine. Locks are created by the accessor and released when the code is done using the accessor. However, when running the code on an arm
machine, the lock does never seem to be released. Therefore, the code blocks when trying to create the second accessor struct, as the mutex lock is blocking.
Code
I've tried to simplify the code to a bare minimum, to make the bug reproducible. It creates a manager struct, that holds a data struct (MyStruct
) wrapped in a Mutex
in an Arc
. The manager then creates a looping thread, that accesses the data field from the manager for each iteration. This is done by instantiating the accessor struct which is holding the mutex lock. A status message before and after aquiring the lock is printed to the console.
use std::sync::{Arc, Mutex, MutexGuard};
use std::thread;
use std::time::Duration;
fn main() {
// Create our manager
let manager = MyManager::new();
// Start a thread that uses the manager's data
manager.start_thread();
}
// A structure containing data, that we want to wrap in a mutex to 'make' it Sync
pub struct MyStruct {
value: i32,
}
impl MyStruct {
pub fn new(value: i32) -> Self {
MyStruct {
value: value,
}
}
pub fn get(&self) -> &i32 {
&self.value
}
}
// A managing struct, that holds an arc with the mutex
pub struct MyManager {
field: Arc<Mutex<MyStruct>>,
}
impl MyManager {
pub fn new() -> Self {
MyManager {
field: Arc::new(Mutex::new(MyStruct::new(3))),
}
}
pub fn start_thread(&self) {
// Clone the arc wrapping the mutex
let field_arc = self.field.clone();
thread::spawn(move || {
loop {
println!("Acquiring lock...");
// Try to create an accessor from the arc, to acquire a lock on the mutex
let _ = Accessor::from(&field_arc);
println!("Lock acquired.");
thread::sleep(Duration::new(1, 0));
}
}).join().unwrap();
}
}
// An accessor, that wraps a mutex guard and makes the data accessible through
// helping methods on the accessor. It drops the guard (and it's mutex lock)
// when the accessor is dropped.
pub struct Accessor<'a> {
guard: MutexGuard<'a, MyStruct>,
}
impl<'a> Accessor<'a> {
pub fn new(guard: MutexGuard<'a, MyStruct>) -> Self {
Accessor {
guard: guard,
}
}
pub fn from(data: &'a Mutex<MyStruct>) -> Accessor<'a> {
Self::new(data.lock().unwrap())
}
// A helper method, to access the data
pub fn value(&self) -> &i32 {
self.guard.get()
}
}
I'm sure this can be simplified much more, to really focus on the actual problem, although I'm currently not sure how. In that case, I'll edit the code sample above.
Output
The expected output of the code are repeated, alternating console messages showing:
Acquiring lock...
Lock acquired!
Here follows the actual sample code output on the system's I've tested it on.
x86_64
machine:
Aquiring lock...
Lock aquired.
Aquiring lock...
Lock aquired.
Aquiring lock...
Lock aquired.
[...]
arm
machine:
Aquiring lock...
Lock aquired.
Aquiring lock...
[program is stuck...]
On the x86_64
machine, the program gives the expected results. This is not the case on the arm
machine however.
The program isn't able to lock a second time, probably, because the previous lock isn't released. That is not what should happen based on my understanding.
This is a problem!
Meta
rustc --version --verbose
on x86_64
:
rustc 1.18.0-nightly (5e122f59b 2017-04-01)
binary: rustc
commit-hash: 5e122f59ba23494d460466cca53c71646d99c767
commit-date: 2017-04-01
host: x86_64-unknown-linux-gnu
release: 1.18.0-nightly
LLVM version: 3.9
rustc --version --verbose
on arm
:
rustc 1.17.0-nightly (6f10e2f63 2017-03-14)
binary: rustc
commit-hash: 6f10e2f63de720468e2b4bfcb275e4b90b1f9870
commit-date: 2017-03-14
host: armv7-unknown-linux-gnueabihf
release: 1.17.0-nightly
LLVM version: 3.9
The arm
machine I'm using is a Raspberry Pi 3B to be exact.