async_lock/lib.rs
1//! Async synchronization primitives.
2//!
3//! This crate provides the following primitives:
4//!
5//! * [`Barrier`] - enables tasks to synchronize all together at the same time.
6//! * [`Mutex`] - a mutual exclusion lock.
7//! * [`RwLock`] - a reader-writer lock, allowing any number of readers or a single writer.
8//! * [`Semaphore`] - limits the number of concurrent operations.
9//!
10//! ## Relationship with `std::sync`
11//!
12//! In general, you should consider using [`std::sync`] types over types from this crate.
13//!
14//! There are two primary use cases for types from this crate:
15//!
16//! - You need to use a synchronization primitive in a `no_std` environment.
17//! - You need to hold a lock across an `.await` point.
18//! (Holding an [`std::sync`] lock guard across an `.await` will make your future non-`Send`,
19//! and is also highly likely to cause deadlocks.)
20//!
21//! If you already use `libstd` and you aren't holding locks across await points (there is a
22//! Clippy lint called [`await_holding_lock`] that emits warnings for this scenario), you should
23//! consider [`std::sync`] instead of this crate. Those types are optimized for the currently
24//! running operating system, are less complex and are generally much faster.
25//!
26//! In contrast, `async-lock`'s notification system uses `std::sync::Mutex` under the hood if
27//! the `std` feature is enabled, and will fall back to a significantly slower strategy if it is
28//! not. So, there are few cases where `async-lock` is a win for performance over [`std::sync`].
29//!
30//! [`std::sync`]: https://doc.rust-lang.org/std/sync/index.html
31//! [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/stable/index.html#/await_holding_lock
32
33#![cfg_attr(not(feature = "std"), no_std)]
34#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)]
35#![doc(
36 html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png"
37)]
38#![doc(
39 html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png"
40)]
41
42extern crate alloc;
43
44/// Simple macro to extract the value of `Poll` or return `Pending`.
45///
46/// TODO: Drop in favor of `core::task::ready`, once MSRV is bumped to 1.64.
47macro_rules! ready {
48 ($e:expr) => {{
49 use ::core::task::Poll;
50
51 match $e {
52 Poll::Ready(v) => v,
53 Poll::Pending => return Poll::Pending,
54 }
55 }};
56}
57
58/// Pins a variable on the stack.
59///
60/// TODO: Drop in favor of `core::pin::pin`, once MSRV is bumped to 1.68.
61#[cfg(all(feature = "std", not(target_family = "wasm")))]
62macro_rules! pin {
63 ($($x:ident),* $(,)?) => {
64 $(
65 let mut $x = $x;
66 #[allow(unused_mut)]
67 let mut $x = unsafe {
68 core::pin::Pin::new_unchecked(&mut $x)
69 };
70 )*
71 }
72}
73
74/// Make the given function const if the given condition is true.
75macro_rules! const_fn {
76 (
77 const_if: #[cfg($($cfg:tt)+)];
78 $(#[$($attr:tt)*])*
79 $vis:vis const fn $($rest:tt)*
80 ) => {
81 #[cfg($($cfg)+)]
82 $(#[$($attr)*])*
83 $vis const fn $($rest)*
84 #[cfg(not($($cfg)+))]
85 $(#[$($attr)*])*
86 $vis fn $($rest)*
87 };
88}
89
90mod barrier;
91mod mutex;
92mod once_cell;
93mod rwlock;
94mod semaphore;
95
96pub use barrier::{Barrier, BarrierWaitResult};
97pub use mutex::{Mutex, MutexGuard, MutexGuardArc};
98pub use once_cell::OnceCell;
99pub use rwlock::{
100 RwLock, RwLockReadGuard, RwLockReadGuardArc, RwLockUpgradableReadGuard,
101 RwLockUpgradableReadGuardArc, RwLockWriteGuard, RwLockWriteGuardArc,
102};
103pub use semaphore::{Semaphore, SemaphoreGuard, SemaphoreGuardArc};
104
105pub mod futures {
106 //! Named futures for use with `async_lock` primitives.
107
108 pub use crate::barrier::BarrierWait;
109 pub use crate::mutex::{Lock, LockArc};
110 pub use crate::rwlock::futures::{
111 Read, ReadArc, UpgradableRead, UpgradableReadArc, Upgrade, UpgradeArc, Write, WriteArc,
112 };
113 pub use crate::semaphore::{Acquire, AcquireArc};
114}
115
116#[cfg(not(loom))]
117/// Synchronization primitive implementation.
118mod sync {
119 pub(super) use core::sync::atomic;
120
121 pub(super) trait WithMut {
122 type Output;
123
124 fn with_mut<F, R>(&mut self, f: F) -> R
125 where
126 F: FnOnce(&mut Self::Output) -> R;
127 }
128
129 impl WithMut for atomic::AtomicUsize {
130 type Output = usize;
131
132 #[inline]
133 fn with_mut<F, R>(&mut self, f: F) -> R
134 where
135 F: FnOnce(&mut Self::Output) -> R,
136 {
137 f(self.get_mut())
138 }
139 }
140}
141
142#[cfg(loom)]
143/// Synchronization primitive implementation.
144mod sync {
145 pub(super) use loom::sync::atomic;
146}
147
148#[cold]
149fn abort() -> ! {
150 // For no_std targets, panicking while panicking is defined as an abort
151 #[cfg(not(feature = "std"))]
152 {
153 struct Bomb;
154
155 impl Drop for Bomb {
156 fn drop(&mut self) {
157 panic!("Panicking while panicking to abort")
158 }
159 }
160
161 let _bomb = Bomb;
162 panic!("Panicking while panicking to abort")
163 }
164
165 // For libstd targets, abort using std::process::abort
166 #[cfg(feature = "std")]
167 std::process::abort()
168}