1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
//! Make things anchored and forbid them from crossing `.await` point.
//!
//! # Quick Example
//! The two basic things in this crate are:
//! - [unanchored] attribute macro for marking functions and methods you want to apply the `.await`
//! point crossing check.
//! - [Anchored] wrapper struct to wrap over things you want to anchored.
//!
//! They usually work with each other like this: **First** wrap [Anchored] on the object you want it
//! to keep away from `.await`, like `MutexGuard`, a `&mut` reference or anything else. **Then** add
//! [unanchored] attribute to the async function / method to enable this compile time check.
//!
//! That's all. Now the compiler will check it for you.
//!
//! For example, the following code can't compile because `bar` is trying to cross the `.await`
//! point.
//! ```rust, ignore
//! # use anchored::{Anchored, unanchored};
//! # struct Bar{}
//! # async fn async_fn(){}
//! #[unanchored]
//! async fn foo(){
//! let bar = Anchored::new(Bar {});
//! async_fn().await;
//! drop(bar);
//! }
//! ```
//! And after limiting `bar`'s scope, everything is fine.
//! ```rust
//! # use anchored::{Anchored, unanchored};
//! # struct Bar{}
//! # async fn async_fn(){}
//! #[unanchored]
//! async fn foo(){
//! {
//! let bar = Anchored::new(Bar {});
//! }
//! async_fn().await;
//! }
//! ```
//!
//! # Motivation
//! Some type is not intended to be used under async context, like the blocking `Mutex` from std, or
//! interior mutable wrapper `RefCell` or `UnsafeCell`. Keeping them across the `.await` point may
//! cause unexpected problems like data race, dead lock etc. When using these types in an async
//! block, we need to check it carefully to ensure they are bounded in the sync context. In other
//! words, not crossing the `.await` point.
//!
//! This crate provides a way to enforce this check at compile time. Providing more safety than
//! manually check and can avoid some checks at runtime like `RefCell` does.
//!
//! # How
//! This crate is pretty simple. It brings an auto trait `Unanchored`, and opt-out it for
//! [Anchored]. Like the `Unpin` trait and `Pin` wrapper couple from std.
//!
//! We use the mechanism that when converting an async block
//! into generator, all the variables in scope before one `.await` will be captured into generator's
//! state. By checking the converted future's type we can tell whether an [Anchored] is captured
//! (crossed the `.await` point).
//!
//! # Limitation
//! The type inference phase only cares about the "scope" and won't take "ownership" into
//! consideration. Which means drop the variable does works fine and have no problem in runtime, but
//! it will fail the check:
//! ```rust, ignore
//! # use anchored::{Anchored, unanchored};
//! # struct Bar{}
//! # async fn async_fn(){}
//! #[unanchored]
//! async fn foo(){
//! let bar = Anchored::new(Bar {});
//! drop(bar);
//! async_fn().await;
//! }
//! ```
//! It should be enclosed into a smaller scope like above example explicit.
//!
//! # Related Work
//! [clippy][clippy] provides two lint options `await_holding_lock` and `await_holding_refcell_ref`
//! that can check some specific lock and reference types.
//!
//! [clippy]: https://github.com/rust-lang/rust-clippy
#![feature(negative_impls)]
#![feature(auto_traits)]
mod anchored;
pub use crate::anchored::{Anchored, Unanchored};
pub use anchored_macros::unanchored;