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