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 tuple
s 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 lock
s 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 ;
).
- It might depend on your
Mutex
type.
Answered By - BiagioF Answer Checked By - Terry (PHPFixing Volunteer)
0 Comments:
Post a Comment
Note: Only a member of this blog may post a comment.