我希望这不算太离题,但我认为理解问题是必要的:
我目前正在使用Rust编写JIT,跟踪操作,将其编译为spirv并在GPU上执行计算(受EPFL的Dr.Jit启发很深)。我编写了一个用于记录操作的后端(支持手动引用计数)。每个变量都通过向量中的索引进行访问。现在我想为Rust编写一个前端,在其中我拥有内部表示的全局实例,并在Mutex之后。为了使用引用计数,当释放该变量时,我必须递减计数器。这需要在Variable的drop函数中锁定Mutex。
impl Drop for Var {
fn drop(&mut self) {
IR.lock().unwrap().dec_ref_count(self.0);
}
}
然而,由于记录操作也需要锁定Mutex,当将变量索引传递给后端函数时,我会遇到死锁问题。
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
}
}
我通过手动删除变量来解决了问题。然而,这似乎不是很惯用的方法。
有没有一种在Rust中构建结构的方式,以便我不会遇到死锁,并且不必重复手动删除调用?我想象FFI相对经常处理类似的问题(因为C代码经常使用全局状态),它们如何解决这个问题,以及有哪些关于Rust中死锁预防的资源?
Arc
比手动实现带互斥锁的引用计数 始终 更快。 - Chayim Friedman