Skip to content

Mutex not releasing lock properly when being used through an accessor struct on ARM #41023

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
timvisee opened this issue Apr 2, 2017 · 2 comments

Comments

@timvisee
Copy link
Contributor

timvisee commented Apr 2, 2017

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.

@hanna-kruppe
Copy link
Contributor

Might be #40593? A fix for that has been merged about a week ago, can you try to reproduce on a current nightly?

@timvisee
Copy link
Contributor Author

timvisee commented Apr 2, 2017

@rkruppe I've updated to the latest nightly on arm.

This did indeed fix the issue regarding the non-releasing lock. Hurray! 🎉
Thank you for the quick and useful reply.

For other readers bumping against the same problem, here's the exact version information of new Rust version I'm using on arm that fixed the issue:

rustc 1.18.0-nightly (5e122f59b 2017-04-01)
binary: rustc
commit-hash: 5e122f59ba23494d460466cca53c71646d99c767
commit-date: 2017-04-01
host: armv7-unknown-linux-gnueabihf
release: 1.18.0-nightly
LLVM version: 3.9

@timvisee timvisee closed this as completed Apr 2, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants