Crate saa

Crate saa 

Source
Expand description

§Synchronous and Asynchronous Synchronization Primitives

Cargo Crates.io GitHub Workflow Status

Low-level synchronization primitives providing both asynchronous and synchronous interfaces.

§Features

  • Provides both asynchronous and synchronous interfaces.
  • Loom support: features = ["loom"].

§Lock

saa::Lock is a low-level shared-exclusive lock providing both asynchronous and synchronous interfaces. Synchronous locking methods such as lock_sync and share_sync can be used alongside their asynchronous counterparts lock_async and share_async simultaneously. saa::Lock implements a heap-allocation-free fair wait queue shared between both synchronous and asynchronous methods.

§Examples

use saa::Lock;

let lock = Lock::default();

lock.lock_sync();

assert!(!lock.try_lock());
assert!(!lock.try_share());

assert!(!lock.release_share());
assert!(lock.release_lock());

async {
    lock.share_async();
    assert!(lock.release_share());
};

§Semaphore

saa::Semaphore is a synchronization primitive that allows a fixed number of threads to access a resource concurrently.

§Examples

use saa::Semaphore;

let semaphore = Semaphore::default();

semaphore.acquire_many_sync(Semaphore::MAX_PERMITS - 1);

assert!(semaphore.try_acquire());
assert!(!semaphore.try_acquire());

assert!(semaphore.release());
assert!(!semaphore.release_many(Semaphore::MAX_PERMITS));
assert!(semaphore.release_many(Semaphore::MAX_PERMITS - 1));

async {
    semaphore.acquire_async().await;
    assert!(semaphore.release());
};

§Gate

saa::Gate is an unbounded barrier that can be opened or sealed manually as needed.

§Examples

use std::sync::Arc;
use std::thread;

use saa::Gate;
use saa::gate::State;

let gate = Arc::new(Gate::default());

let mut threads = Vec::new();

for _ in 0..4 {
    let gate = gate.clone();
    threads.push(thread::spawn(move || {
        assert_eq!(gate.enter_sync(), Ok(State::Controlled));
    }));
}

let mut cnt = 0;
while cnt != 4 {
    if let Ok(n) = gate.permit() {
        cnt += n;
    }
}

for thread in threads {
    thread.join().unwrap();
}

§Notes

Using synchronous methods in an asynchronous context may lead to deadlocks. Consider a scenario where an asynchronous runtime uses two threads to execute three tasks.

  • ThreadId(0): task-0: share-waiting / pending || task-1: "synchronous"-lock-waiting.
  • ThreadId(1): task-2: release-lock / ready: wake-up task-0 -> task-2: lock-waiting / pending.

In this example, task-0 has logically acquired a shared lock transferred from task-2; however, it may remain in the task queue indefinitely depending on the asynchronous runtime’s scheduling policy.

§Changelog

Re-exports§

pub use gate::Gate;

Modules§

gate
Gate is a synchronization primitive that blocks tasks from entering a critical section until they are allowed to do so.

Structs§

Lock
Lock is a low-level locking primitive for both synchronous and asynchronous operations.
Semaphore
Semaphore is a synchronization primitive that allows a fixed number of threads to access a resource concurrently.