Mea (Make Easy Async) is a runtime-agnostic library providing essential synchronization primitives for asynchronous Rust programming. The library offers a collection of well-tested, efficient synchronization tools that work with any async runtime.
- Barrier: A synchronization primitive that enables tasks to wait until all participants arrive.
- Condvar: A condition variable that allows tasks to wait for a notification.
- Latch: A synchronization primitive that allows one or more tasks to wait until a set of operations completes.
- Mutex: A mutual exclusion primitive for protecting shared data.
- Once: A primitive that ensures a one-time asynchronous operation runs at most once, even when called concurrently.
- OnceCell: A cell that can be written to at most once, providing safe, lazy initialization.
- RwLock: A reader-writer lock that allows multiple readers or a single writer at a time.
- Semaphore: A synchronization primitive that controls access to a shared resource.
- ShutdownSend & ShutdownRecv: A composite synchronization primitive for managing shutdown signals.
- WaitGroup: A synchronization primitive that allows waiting for multiple tasks to complete.
- atomicbox: A safe, owning version of AtomicPtr for heap-allocated data.
- broadcast::channel: A multi-producer, multi-consumer broadcast channel.
- mpsc::bounded: A multi-producer, single-consumer bounded queue for sending values between asynchronous tasks.
- mpsc::unbounded: A multi-producer, single-consumer unbounded queue for sending values between asynchronous tasks.
- oneshot::channel: A one-shot channel for sending a single value between tasks.
Add the dependency to your Cargo.toml via:
cargo add meaAll synchronization primitives in this library are runtime-agnostic, meaning they can be used with any async runtime like Tokio, async-std, or others. This makes the library highly versatile and portable.
All types in this library implement Send and Sync, making them safe to share across thread boundaries. This is essential for concurrent programming where data needs to be accessed from multiple threads.
This crate is built against the latest stable release, and its minimum supported rustc version is 1.85.0.
The policy is that the minimum Rust version required to use this crate can be increased in minor version updates. For example, if Mea 1.0 requires Rust 1.20.0, then Mea 1.0.z for all values of z will also require Rust 1.20.0 or newer. However, Mea 1.y for y > 0 may require a newer minimum version of Rust.
This project is licensed under Apache License, Version 2.0.
This crate collects runtime-agnostic synchronization primitives from spare parts:
- Barrier is inspired by
std::sync::Barrierandtokio::sync::Barrier, with a different implementation based on the internalWaitSetprimitive. - Condvar is inspired by
std::sync::Condvarandasync_std::sync::Condvar, with a different implementation based on the internalSemaphoreprimitive. Different from the async_std implementation, this condvar is fair. - Latch is inspired by
latches, with a different implementation based on the internalCountdownStateprimitive. Nowaitorwatchmethod is provided, since it can be easily implemented by composing delay futures. No sync variant is provided, since it can be easily implemented with block_on of any runtime. - Mutex is derived from
tokio::sync::Mutex. No blocking method is provided, since it can be easily implemented with block_on of any runtime. - OnceCell is derived from
tokio::sync::OnceCell, but using our own semaphore implementation. - RwLock is derived from
tokio::sync::RwLock, but themax_readerscan be anyNonZeroUsize(effectively any positiveusize) instead of[0, u32::MAX >> 3]. No blocking method is provided, since it can be easily implemented with block_on of any runtime. - Semaphore is derived from
tokio::sync::Semaphore, withoutclosemethod since it is quite tricky to use. And thus, this semaphore doesn't have the limitation of max permits. Besides, new methods likeforget_exactare added to fit the specific use case. - WaitGroup is inspired by
waitgroup-rs, providing different API flavor with a different implementation based on the internalCountdownStateprimitive. - atomicbox is forked from
atomicboxat commit 07756444. - broadcast::channel is derived from
tokio::sync::broadcast::channel, with a different implementation based on the internalWaitSetprimitive. - oneshot::channel is derived from
oneshot, with significant simplifications since we need not support synchronized receiving functions.
Other parts are written from scratch.
NB. The optimization considerations are different when implementing a sync primitive for sync code and async code. Generally speaking, once you have an async + runtime-agnostic implementation, you can immediately have a sync implementation by block_on any async runtime (pollster is the most lightweight runtime that park the current thread). However, a sync-oriented implementation may leverage some platform-specific features to achieve better performance. This library is designed for async code, so it doesn't consider sync-oriented optimization. I often find libraries that try to provide both sync and async implementations end up with a clumsy API design. So I prefer to keep them separate.