Skip to content

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

Closed
@timvisee

Description

@timvisee

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions