3 个版本 (破坏性更新)

0.3.0 2020年6月28日
0.2.0 2020年6月27日
0.1.0 2020年6月21日

#743 in 并发

MIT 许可证

12KB
196

可证明、可组合、编译时无死锁。

如何使用 locktree

这个 crate 围绕一个宏:locktree

locktree 的工作原理(待更新:使用最新格式)

locktree(滥用)Rust 的类型系统来确保锁总是以相同的顺序获取。在 locktree 管理下的锁被组织成一条线性序列。只能通过向前移动到这个序列来获取锁,并且在向后移动时必须释放锁。因此,只要所有锁都由 locktree 管理,静态上就不可能有两个线程以不同的顺序获取相同的锁集,因此死锁是不可能的。

为了实现这一点,locktree 依赖于对可变引用的别名规则、生命周期以及序列中每个点的单独类型。在最简单的情况下,只有一个锁

locktree! {
  Main {
    main: StdMutex<String>,
  }
}

该宏将生成一个“入口点”,您可以使用它来锁定任何东西(在这种情况下,只有 main,为了说明目的,显式指定生命周期)

struct MainLockTree {
  main: ::std::sync::Mutex<String>,
}

impl MainLockTree {
  fn lock_main<'a>(
    &'a mut self
  ) -> (::std::sync::MutexGuard<'a, String>, MainLockTreeMain<'a>) {
    // ...
    # unimplemented!()
  }
}

所有锁函数都接受 &mut self 并返回适当的锁和一个 前向锁树。两者都通过其生命周期与 MainLockTree 实例绑定,因此(在安全 Rust 中)不可能使用该实例来锁定其他任何东西。进一步的锁定只能通过前向锁树进行。在这种情况下,MainLockTreeMain 完全为空,因此无法获取更多锁

impl<'b> MainLockTreeMain<'b> {}

这是因为 main 之后没有更多的锁。如果我们有两个锁

locktree! {
  Main {
    first: StdMutex<String>,
    second: StdRwLock<Vec<usize>>,
  }
}

我们就能从入口点锁定任何一个

struct MainLockTree {
  first: ::std::sync::Mutex<String>,
  second: ::std::sync::RwLock<Vec<usize>>,
}

impl MainLockTree {
  fn lock_first<'a>(
    &'a mut self
  ) -> (::std::sync::MutexGuard<'a, String>, MainLockTreeFirst<'a>) {
    // ...
    # unimplemented!()
  }

  fn read_second<'a>(
    &'a mut self
  ) -> (::std::sync::RwLockReadGuard<'a, Vec<usize>>, MainLockTreeSecond<'a>) {
    // ...
    # unimplemented!()
  }

  fn write_second<'a>(
    &'a mut self
  ) -> (::std::sync::RwLockWriteGuard<'a, Vec<usize>>, MainLockTreeSecond<'a>) {
    // ...
    # unimplemented!()
  }
}

MainLockTreeSecond 再次为空,因为它是序列中的最后一个。然而,MainLockTreeFirst 允许 second(但不能是 fist)按顺序被锁定

struct MainLockTreeSecond<'a>(&'a MainLockTree);

impl MainLockTree {
  fn read_second<'a>(
    &'a mut self
  ) -> (::std::sync::RwLockReadGuard<'a, Vec<usize>>, MainLockTreeSecond<'a>) {
    // ...
    # unimplemented!()
  }

  fn write_second<'a>(
    &'a mut self
  ) -> (::std::sync::RwLockWriteGuard<'a, Vec<usize>>, MainLockTreeSecond<'a>) {
    // ...
    # unimplemented!()
  }
}

因此,强制执行了适当的锁定顺序。请注意,您可以选择在当前状态和目标锁之间不锁定任何东西,但如果有必要锁定跳过的任何内容,则必须将其丢弃并重新获取。

组合

待更新

依赖项

~1.2–2.3MB
~47K SLoC