PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Tuesday, September 20, 2022

[FIXED] Why did moving variables out of HashMap's insert method scope solve a deadlock?

 September 20, 2022     deadlock, hashmap, rust     No comments   

Issue

OB_HM is Arc<Mutex<HashMap<...>>>

This caused the deadlock at very first iteration:

let mut OB_HM__:HashMap<u8, (f64, f64, f64, f64)> = HashMap::new();
    for i in &p {
        OB_HM__.insert(
            *i, 
            (
                OB_HM.lock().unwrap()[i].0, OB_HM.lock().unwrap()[i].1 
                R[&i].0,
                R[&i].1
            )
        );
    };
    let mut OB_HM = OB_HM__;

And this solved it:

let mut OB_HM__:HashMap<u8, (f64, f64, f64, f64)> = HashMap::new();
    for i in &p {
        let x = OB_HM.lock().unwrap()[i].0;
        let y = OB_HM.lock().unwrap()[i].1;
        OB_HM__.insert(
            *i, 
            (
                x, y, 
                R[&i].0,
                R[&i].1
            )
        );
    };
    let mut OB_HM = OB_HM__;

Why so? I had mere intuition but I need to understand the internals behind it. I'm guessing it has to do with how Rust creates tuples or how the method insert works?

And a side question about insert - why does it require ; EOL when it's the only instruction inside a loop?


Solution

This caused the deadlock at very first iteration [...] Why so?

Shortly, because of temporary lifetime extension.

Generally1, OB_HM.lock() create a new object, a mutex guard. This is a temporary object and it implements Deref trait (allowing autoderef).

De-referencing the mutex guard creates a borrow of the inner object (i.e., the HashMap) that extends the lifetime of the guard itself.

Therefore, the mutex guard is not dropped until the end of entire function call (i.e., insert) and so the Mutex is not unlocked. Since you have two locks in your function call expression, that generates a deadlock.

Maybe a toy-example might help to understand: example.

From the above,

foo(mutex.lock().value(),  // <--- first lock
    mutex.lock().value()); // <--- second lock

produces a deadlock because both locks are released only after the entire expression foo (the function call) has been evaluated.

Indeed the output:

MutexLock!       // <--- First lock
MutexLock!       // <--- Second lock
 > !DEADLOCK! <  // <--- Two interleaved locks! Deadlock!
foo: Call!       
                 // <--- Now the function ends
MutexUnlock      // <--- And only at the end the mutex are unlocked!
MutexUnlock

Note that, this is independent from tuples.

The "working" solution works because:

let x = OB_HM.lock().unwrap()[i].0;
let y = OB_HM.lock().unwrap()[i].1;

the temporary expression of lock lasts for the entire statement. So after x has been assigned, the temporary lock is released. Allowing y to acquire it without deadlocks.


And a side question about insert - why does it require ; EOL

Because of rust syntax I guess.

From here: loop syntax

loop BlockExpression

where BlockExpression is defined as zero or more statements.

Expression are accepted only as expression-statements (that is, with ;).


  1. It might depend on your Mutex type.


Answered By - BiagioF
Answer Checked By - Terry (PHPFixing Volunteer)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing