#await #point #cross #can-t #check #unanchored

nightly anchored

锚定事物无法穿越 .await 点

1 个不稳定版本

0.1.0 2021年9月20日

#6#can-t

Apache-2.0

8KB

锚定

使事物锚定并禁止它们穿越 .await 点。


lib.rs:

使事物锚定并禁止它们穿越 .await 点。

快速示例

本软件包中的两个基本元素是

  • [unanchored] 属性宏,用于标记您希望应用 .await 点穿越检查的函数和方法。
  • [Anchored] 包装结构,用于包装您想要锚定的内容。

它们通常像这样一起工作:首先,将 [Anchored] 包装在您想要远离 .await 的对象上,如 MutexGuard、&mut 引用或其他内容。然后,将 [unanchored] 属性添加到异步函数/方法中,以启用此编译时检查。

这就是全部。现在编译器会为您进行检查。

例如,以下代码无法编译,因为 bar 正在尝试穿越 .await 点。

#[unanchored]
async fn foo(){
    let bar = Anchored::new(Bar {});
    async_fn().await;
    drop(bar);
}

限制 bar 的作用域后,一切正常。

#[unanchored]
async fn foo(){
    {
        let bar = Anchored::new(Bar {});
    }
    async_fn().await;
}

动机

某些类型在异步上下文中不是预期使用的,例如来自 std 的阻塞 Mutex、内部可变包装 RefCell 或 UnsafeCell。在 .await 点保持它们可能会导致意外问题,如数据竞争、死锁等。当在异步块中使用这些类型时,我们需要仔细检查以确保它们在同步上下文中受限制。换句话说,不要穿越 .await 点。

此软件包提供了一种在编译时强制执行此检查的方法。它比手动检查提供更多安全性,并且可以避免像 RefCell 那样在运行时进行的某些检查。

如何

此软件包非常简单。它引入了一个自动 trait Unanchored,并针对 [Anchored] 取消了它。类似于 std 中的 Unpin trait 和 Pin 包装器。

我们使用将异步块转换为生成器的机制。在转换到生成器之前,作用域中的所有变量都会被捕获到生成器的状态中。通过检查转换后的未来的类型,我们可以判断 [Anchored] 是否被捕获(穿越了 .await 点)。

限制

类型推理阶段只关注“作用域”,而不会考虑“所有权”。这意味着丢弃变量是正常且在运行时没有问题的,但它将导致检查失败。

#[unanchored]
async fn foo(){
    let bar = Anchored::new(Bar {});
    drop(bar);
    async_fn().await;
}

它应该像上面的示例那样明确地封装在一个更小的作用域中。

相关工作

clippy 提供了两种 lint 选项 await_holding_lockawait_holding_refcell_ref,可以检查某些特定的锁和引用类型。

依赖项

~1.5MB
~35K SLoC