I hope this isnt too much of a tangent however I think it is neccesary to understand the problem:
I am currently writing a JIT in rust that traces operations, compiles them to spirv and executes the computations on the GPU (heavily inspired by Dr.Jit from EPFL). I have written a backend for recording the operations (it supports manual reference counting). Every variable is accesed through an index into a vector. I now want to write a frontend for Rust where i have a global instance of the internal representation behind a Mutex. In order to use reference counting I have to decrement the counter for that variable when dropping it. This requires locking the Mutex in the drop function of a Variable.
impl Drop for Var {
fn drop(&mut self) {
IR.lock().unwrap().dec_ref_count(self.0);
}
}
However because recording operations also requires locking the Mutex I reach a deadlock when passing the Variable index to a backend function.
impl<T: Into<Var>> ops::Add<T> for Var {
type Output = Var;
fn add(self, rhs: T) -> Self::Output {
let rhs = rhs.into();
let ret = Self(IR.lock().unwrap().[<$bop:lower>](self.0, rhs.0));
drop(rhs);// This is a workaround and does not seem very idiomatic
ret
}
}
I fixed the problem by manually droping the variable after getting the value. However this does not seem very idiomatic.
Is there a way to structure this in Rust so that I don't get a Deadlock and don't have to repeat the manual drop calls? I would imagine that FFIs would have to deal with somethinge similar relatively often (as C code often uses global states) how do these resolve that issue and what are some resources on Deadlock prevention in Rust?
If just
Self(IR.lock().unwrap().[<$bop:lower>](self.0, rhs.into().0))doesn't work, you could consider doing something like this:Still not great, but since you need to get
rhsto fall out of scope first, if it's not falling out of scope at the end of this function call with...(self.0, rhs.into().0)(I assume this is a function call?), your options are basically usingdrop(also consider using asstd::mem::dropfor distinction), or scoping it with a block expression as in the example above.This section of the Rust Reference has some more in depth details around the rules of dropping if you want to learn more.