An efficient async condition variable for lock-free algorithms

Overview

async-event

An efficient async condition variable for lock-free algorithms, a.k.a. "eventcount".

Cargo Documentation License

Overview

Eventcount-like primitives are useful to make some operations on a lock-free structure blocking, for instance to transform bounded queues into bounded channels. Such a primitive allows an interested task to block until a predicate is satisfied by checking the predicate each time it receives a notification.

While functionally similar to the event_listener crate, this implementation is more opinionated and limited to the async case. It strives to be more efficient, however, by limiting the amount of locking operations on the mutex-protected list of notifiers: the lock is typically taken only once for each time a waiter is blocked and once for notifying, thus reducing the need for synchronization operations. Finally, spurious wake-ups are only generated in very rare circumstances.

Note that if you only need to send notifications to a single task, you may use instead the Diatomic Waker crate for extra performance.

This library is an offshoot of Asynchronix, an ongoing effort at a high performance asynchronous computation framework for system simulation. It is also used in the Tachyonix MPSC channel.

Usage

Add this to your Cargo.toml:

[dependencies]
async-event = "0.1.0"

Differences with event_listener

This Event primitive is expected to be faster than that of the event_listener crate in the general case. That being said, your mileage may vary depending on your particular application and you should probably benchmark both.

The API is more opinionated and designed to preventing potential misuse such as:

  • Forgetting to check again the predicate after requesting a notification, i.e. after a call to Event::listen() in the event_listener crate. async-event provides instead the Event::wait_until method which takes care of checking the predicate whenever necessary to prevent races.
  • Confusion between notify and notify_additional in the event_listener crate. Our experience and the API of other similar libraries suggest that the latter is almost always what the user needs, so the notify* methods in this crate actually behave like notify_additional in the event_listener crate.
  • Inadequate atomic synchronization of the predicate. The notify* and wait_until methods always insert atomic fences to ensure proper synchronization: there is no equivalent to notify_additional_relaxed.

Examples

Send a non-zero value asynchronously

use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::thread;

use futures_executor::block_on;

use async_event::Event;

let value = Arc::new(AtomicUsize::new(0));
let event = Arc::new(Event::new());

// Set a non-zero value concurrently.
thread::spawn({
    let value = value.clone();
    let event = event.clone();

    move || {
        // A relaxed store is sufficient here: `Event::notify*` methods insert
        // atomic fences to warrant adequate synchronization.
        value.store(42, Ordering::Relaxed);
        event.notify_one();
    }
});

// Wait until the value is set.
block_on(async move {
    let v = event
        .wait_until(|| {
            // A relaxed load is sufficient here: `Event::wait_until` inserts
            // atomic fences to warrant adequate synchronization.
            let v = value.load(Ordering::Relaxed);
            if v != 0 { Some(v) } else { None }
        })
        .await;

     assert_eq!(v, 42);
});

Single-slot MPMC channel for non-zero values

See implementation in the examples directory.

Safety

This is a low-level primitive and as such its implementation relies on unsafe. The test suite makes extensive use of Loom and MIRI to assess its correctness. As amazing as they are, however, Loom and MIRI cannot formally prove the absence of data races so soundness issues are possible.

License

This software is licensed under the Apache License, Version 2.0 or the MIT license, at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

You might also like...
Bolt is a desktop application that is designed to make the process of developing and testing APIs easier and more efficient.

Bolt ⚡ Bolt is a desktop application that is designed to make the process of developing and testing APIs easier and more efficient. Quick start 👩‍💻

A simple, efficient Rust library for handling asynchronous job processing and task queuing.

job_queue Setup cargo add job_queue Usage Create a job use job_queue::{Error, Job, typetag, async_trait, serde}; #[derive(Debug, serde::Deserialize,

Async executor for WebAssembly

There are a number of async task executors available in Rust's ecosystem. However, most (if not all?) of them rely on primitives that might not be available or optimal for WebAssembly deployment at the time.

The feature-rich, portable async channel library

The feature-rich, portable async channel library crates.io docs.rs Why use Postage? Includes a rich set of channels. | barrier | broadcast | dispa

Mix async code with CPU-heavy thread pools using Tokio + Rayon

tokio-rayon Mix async code with CPU-heavy thread pools using Tokio + Rayon Resources Documentation crates.io TL;DR Sometimes, you're doing async stuff

Another Async IO Framework based on io_uring

kbio, the Async IO Framework based on io_uring, is used in KuiBaDB to implement async io. Features Support multi-threading concurrent task submission.

async-alloc-counter measures max allocations in a future invocation

async-alloc-counter measures max allocations in a future invocation see examples/ for usage This allocator can be used as follows: use async_alloc_cou

Golang like WaitGroup implementation for sync/async Rust.

wg Golang like WaitGroup implementation for sync/async Rust.

single file, std only, async Rust executor

whorl - A single file, std only, async Rust executor whorl was created to teach you how async executors work in Rust. It is not the fastest executor n

Owner
Asynchronics
Asynchronics
Free and open-source reimplementation of Native Mouse Lock (display_mouse_lock) in rust.

dml-rs display_mouse_lock in rust. Free, open-source reimplementation of display_mouse_lock (Native Mouse Lock) in Rust. Written because I felt like i

Tomat 4 Feb 12, 2023
A Zincati lock backend for stateful workloads.

This repository is deprecated. We realized CoreOS is probably not a good fit for us. The repository will be kept up on the off chance that this is use

Open Computing Facility 6 Dec 7, 2022
transmute-free Rust library to work with the Arrow format

Arrow2: Transmute-free Arrow This repository contains a Rust library to work with the Arrow format. It is a re-write of the official Arrow crate using

Jorge Leitao 708 Dec 30, 2022
Removes generated and downloaded files from code projects to free up space

makeclean Removes generated and downloaded files from code projects to free up space. Features: List, cleans and archives projects depending on how lo

Kevin Bader 2 Mar 11, 2022
Astronomical algorithms in Rust

astro-rust Contents API Docs About Usage Contributing References About astro-rust is a library of advanced astronomical algorithms for the Rust progra

Saurav Sachidanand 213 Jan 7, 2023
A memory efficient immutable string type that can store up to 24* bytes on the stack

compact_str A memory efficient immutable string type that can store up to 24* bytes on the stack. * 12 bytes for 32-bit architectures About A CompactS

Parker Timmerman 342 Jan 2, 2023
The efficient and elegant crate to count variants of Rust's Enum.

variant-counter The efficient and elegant crate to count variants of Rust's Enum. Get started #[derive(VariantCount)] #[derive(VariantCount)] pub enum

Folyd 16 Sep 29, 2022
Simple and efficient time representation in Rust.

timens-rs Simple and efficient timestamp representation. The main objective being interoperability with OCaml Core_kernel.Time_ns. A significant part

Laurent Mazare 7 Oct 17, 2022
An efficient method of heaplessly converting numbers into their string representations, storing the representation within a reusable byte array.

NumToA #![no_std] Compatible with Zero Heap Allocations The standard library provides a convenient method of converting numbers into strings, but thes

Michael Murphy 42 Sep 6, 2022
A flexible, simple to use, immutable, clone-efficient String replacement for Rust

A flexible, simple to use, immutable, clone-efficient String replacement for Rust. It unifies literals, inlined, and heap allocated strings into a single type.

Scott Meeuwsen 119 Dec 12, 2022