//! Async synchronization primitives.
//!
//! This crate provides the following primitives:
//!
//! * [`Barrier`] - enables tasks to synchronize all together at the same time.
//! * [`Mutex`] - a mutual exclusion lock.
//! * [`RwLock`] - a reader-writer lock, allowing any number of readers or a single writer.
//! * [`Semaphore`] - limits the number of concurrent operations.
//!
//! ## Relationship with `std::sync`
//!
//! In general, you should consider using [`std::sync`] types over types from this crate.
//!
//! There are two primary use cases for types from this crate:
//!
//! - You need to use a synchronization primitive in a `no_std` environment.
//! - You need to hold a lock across an `.await` point.
//!   (Holding an [`std::sync`] lock guard across an `.await` will make your future non-`Send`,
//!   and is also highly likely to cause deadlocks.)
//!
//! If you already use `libstd` and you aren't holding locks across await points (there is a
//! Clippy lint called [`await_holding_lock`] that emits warnings for this scenario), you should
//! consider [`std::sync`] instead of this crate. Those types are optimized for the currently
//! running operating system, are less complex and are generally much faster.
//!
//! In contrast, `async-lock`'s notification system uses `std::sync::Mutex` under the hood if
//! the `std` feature is enabled, and will fall back to a significantly slower strategy if it is
//! not. So, there are few cases where `async-lock` is a win for performance over [`std::sync`].
//!
//! [`std::sync`]: https://doc.rust-lang.org/std/sync/index.html
//! [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/stable/index.html#/await_holding_lock

#![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)]
#![doc(
    html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png"
)]
#![doc(
    html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png"
)]

extern crate alloc;

/// Make the given function const if the given condition is true.
macro_rules! const_fn {
    (
        const_if: #[cfg($($cfg:tt)+)];
        $(#[$($attr:tt)*])*
        $vis:vis const fn $($rest:tt)*
    ) => {
        #[cfg($($cfg)+)]
        $(#[$($attr)*])*
        $vis const fn $($rest)*
        #[cfg(not($($cfg)+))]
        $(#[$($attr)*])*
        $vis fn $($rest)*
    };
}

mod barrier;
mod mutex;
mod once_cell;
mod rwlock;
mod semaphore;

pub use barrier::{Barrier, BarrierWaitResult};
pub use mutex::{Mutex, MutexGuard, MutexGuardArc};
pub use once_cell::OnceCell;
pub use rwlock::{
    RwLock, RwLockReadGuard, RwLockReadGuardArc, RwLockUpgradableReadGuard,
    RwLockUpgradableReadGuardArc, RwLockWriteGuard, RwLockWriteGuardArc,
};
pub use semaphore::{Semaphore, SemaphoreGuard, SemaphoreGuardArc};

pub mod futures {
    //! Named futures for use with `async_lock` primitives.

    pub use crate::barrier::BarrierWait;
    pub use crate::mutex::{Lock, LockArc};
    pub use crate::rwlock::futures::{
        Read, ReadArc, UpgradableRead, UpgradableReadArc, Upgrade, UpgradeArc, Write, WriteArc,
    };
    pub use crate::semaphore::{Acquire, AcquireArc};
}

#[cfg(not(loom))]
/// Synchronization primitive implementation.
mod sync {
    pub(super) use core::sync::atomic;

    pub(super) trait WithMut {
        type Output;

        fn with_mut<F, R>(&mut self, f: F) -> R
        where
            F: FnOnce(&mut Self::Output) -> R;
    }

    impl WithMut for atomic::AtomicUsize {
        type Output = usize;

        #[inline]
        fn with_mut<F, R>(&mut self, f: F) -> R
        where
            F: FnOnce(&mut Self::Output) -> R,
        {
            f(self.get_mut())
        }
    }
}

#[cfg(loom)]
/// Synchronization primitive implementation.
mod sync {
    pub(super) use loom::sync::atomic;
}

#[cold]
fn abort() -> ! {
    // For no_std targets, panicking while panicking is defined as an abort
    #[cfg(not(feature = "std"))]
    {
        struct Bomb;

        impl Drop for Bomb {
            fn drop(&mut self) {
                panic!("Panicking while panicking to abort")
            }
        }

        let _bomb = Bomb;
        panic!("Panicking while panicking to abort")
    }

    // For libstd targets, abort using std::process::abort
    #[cfg(feature = "std")]
    std::process::abort()
}
