Global Mutex, lock() does not retrurn

I am trying to understand global static data with a mutex. It boils down to this:

use std::sync::{Mutex};

#[derive(Debug)]
struct MyStruct {
    i_value: i32,
}

static mut MY_GLOBAL_MUT: Mutex<Option<MyStruct>> = Mutex::new(None);

fn main() {
    for i in 0..3 {
        unsafe {
            let mut the_option = MY_GLOBAL_MUT.lock().expect("Cant get the lock!");
            match &*the_option {
                Some(_v) => {
                    println!("{i}: Found a value!");
                }
                None => {
                    println!("{i}: Need to create the value!");
                    MY_GLOBAL_MUT = Mutex::new(Some(MyStruct { i_value: 42 }));
                    the_option = MY_GLOBAL_MUT.lock().expect("Cant get the lock!");
                }
            }
            println!("{i}: local_mystruct = {}", the_option.as_ref().unwrap().i_value);
        }
    }
}

Interesting... on the Rust Playground, this code does exactly what I want/hope. Compiling it locally, the first lock() blocks when coming around the second time.

Why? (the guard is dropped every run through the loop)

This line is very problematic:

This destroys and replaces the mutex that you have an active lock on. The compiler doesn't catch it because you are using a static mut global, which are extremely frowned upon. Change it to an static.

Once you do that, you will no longer need any unsafe. You can replace the line I mentioned with

*the_option = Some(MyStruct { i_value: 42 });

Okay, so why does your code deadlock now? It's because this line:

behaves like this:

let new_value = MY_GLOBAL_MUT.lock().expect("Cant get the lock!");
drop(the_option);
the_option = new_value;

So you are calling lock before the previous lock guard is dropped.

3 Likes

Your code exhibits undefined behavior.
Here you lock the mutex, which returns a guard holding a reference to the static global:

Here you mutate this static global while the reference is still held:

This is instant UB and that's why your code behaves differently across platforms.

This sort of bug is why you should never use mutable statics, and why static mut will be deprecated in Rust 2024. the future, maybe.

4 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.