Amity
Collection of concurrency algorithms. The collection is not fixed and more algorithms will be added. Algorithm may be removed from crate only if its implementation is unsound and cannot be fixed. In this case it will be deprecated first and removed in later version.
Most algorithms require its own feature flag to be enabled.
Available Algorithms
🔄 Backoff
Provides utilities for implementing exponential backoff strategies, useful in retry mechanisms.
Examples
Here is an example of using the BackOff struct to implement exponential backoff when waiting for a resource:
use BackOff;
If blocking is not an option, you can use BackOff::wait().
use BackOff;
The BackOff struct helps manage contention by implementing an efficient waiting strategy - first spinning, then yielding, and finally suggesting when the thread should block completely.
🌀 Cache
Implements utilities for working with cache lines, optimizing memory access patterns in concurrent programming.
Examples
Here is an example of using the CachePadded struct to prevent false sharing in a concurrent scenario:
use CachePadded;
use ;
unsafe
let counters = new;
let counters_clone = clone;
// Thread 1 updates counter1
let thread1 = spawn;
let counters_clone = clone;
// Thread 2 updates counter2 without false sharing
let thread2 = spawn;
thread1.join.unwrap;
thread2.join.unwrap;
This example demonstrates how CachePadded can be used to prevent false sharing when multiple threads need to update different data simultaneously.
📐 State Pointer
Combines state and pointer into a single atomic value, enabling efficient state management in concurrent programming.
Examples
Here is an example of using the PtrState and AtomicPtrState to efficiently combine state information with pointers:
use ;
use Ordering;
// Create a sample node
let mut node = Node ;
// Create a state value (limited by pointer alignment)
let state = new.unwrap;
// Combine pointer and state into a single value
let ptr_state = new_mut;
// Extract pointer and state separately
let ptr = ptr_state.ptr;
let extracted_state = ptr_state.state;
// Use the extracted pointer safely
unsafe
println!;
// Atomic version for thread-safe operations
let atomic_ptr_state = new_mut;
// Load the combined value atomically
let loaded_ptr_state = atomic_ptr_state.load;
// Update state while preserving the pointer
let new_state = new.unwrap;
let updated_ptr_state = loaded_ptr_state.with_state;
// Store the updated value atomically
atomic_ptr_state.store;
println!;
This pattern is useful in concurrent data structures where you need to pack flags or other state information with pointers without additional memory overhead. For example, it can be used in lock-free algorithms to mark nodes as "deleted" or to store version counters for ABA problem prevention.
🔗 Ring Buffer
Implements simple ring-buffer that can be used to build concurrent data structures.
Feature: ring-buffer
Examples
Here is an example of using the RingBuffer struct for efficient FIFO operations:
use RingBuffer;
// Create a new ring buffer
let mut buffer = new;
// Push some elements
buffer.push;
buffer.push;
buffer.push;
// Check the buffer status
println!;
println!;
// Pop elements (FIFO order)
while let Some = buffer.pop
// Buffer is now empty
assert!;
// Using with_capacity for performance
let mut buffer = with_capacity;
// Fill the buffer
for i in 0..5
// Use drain to consume all elements
for item in buffer.drain
// Buffer is automatically cleared after drain
assert!;
Ring buffers are particularly useful in scenarios requiring fixed-size queues or when implementing producer-consumer patterns where elements need to be processed in order.
🔃 Flip Queue
Queue implementation that allows concurrent writes, but only exclusive reads.
This is useful for scenarios where multiple threads need to write data concurrently, and single thread swaps inner and own RingBuffer to read it.
FlipBuffer is lockless version and requires mutable access to read and to expand internal buffer when full.
FlipQueue uses read-write lock to allow concurrent pushes until buffer is full,
and locks it for exclusive access to grow buffer and to swap it with reader.
Feature: flip-queue
Examples
Here is an example of using the FlipQueue for concurrent writes from multiple threads and exclusive reads:
use FlipQueue;
use RingBuffer;
use Arc;
use thread;
// Create a shared flip queue with initial capacity
let queue = new;
// Spawn multiple producer threads
let mut handles = vec!;
for thread_id in 0..4
// Wait for producers to finish
for handle in handles
// Using drain_locking for exclusive access
let items: = queue.drain_locking;
println!;
// Alternative approach: swap with an empty buffer for bulk processing
let mut queue = new;
// Add some items
for i in 0..10
// Prepare an empty buffer to swap with
let mut buffer = new;
// Swap buffers - very efficient for bulk processing
queue.swap_buffer;
// Process items from the swapped buffer
while let Some = buffer.pop
This pattern is especially useful when you have multiple producers that need to add data concurrently without blocking each other, but processing happens on a single consumer thread. The swap_buffer method provides a very efficient way to batch process items without holding locks for extended periods.
🔺 Triple
Implements triple-buffering for wait-free data transfer between single producer single and consumer threads. This allows for efficient data exchange without the need for locks.
Both consumer and producer has exclusive access to its own slot, allowing taking a mutable reference to it, which grants the ability to modify data in place, unlike channel-based approaches.
Feature: triple
Examples
Here is an example of using the TripleBuffer:
use TripleBuffer;
// Create a new triple buffer with initial values
let mut buffer = default;
// Split the buffer into producer and consumer
let = buffer.split_mut;
// Producer updates its element
*producer.get_mut = 42;
// Publish the updated element
producer.publish;
// Consumer consumes the element
if consumer.consume
📡 Broad
A broadcast mechanism to notify multiple listeners of events concurrently.
Feature: broad
Examples
Here is an example of using the broad module:
use ;
// Create a new broadcast channel with an initial value
let mut tx = new;
let mut rx = tx.receiver;
// Sender sends a new value
tx.send;
// Receiver receives the new value
if let Some = rx.recv
🔁 Spin
Provides a low-latency spinlock implementation for mutual exclusion in critical sections. Also includes a not-totally-unfair read-write spin lock for efficient concurrent read and write operations.
Feature: spin
Examples
Here is an example of using the Spin mutex for mutual exclusion:
use Spin;
use Arc;
use thread;
let counter = new;
let mut handles = vec!;
// Spawn multiple threads that increment the counter
for _ in 0..10
// Wait for all threads to complete
for handle in handles
println!;
assert_eq!;
Here is an example of using the RwSpin read-write lock for concurrent read access and exclusive write access:
use RwSpin;
use Arc;
use thread;
use Duration;
let data = new;
let mut handles = vec!;
// Spawn reader threads
for i in 0..3
// Spawn writer threads
for i in 0..2
// Wait for all threads to complete
for handle in handles
// Print the final state
let final_data = data.read;
println!;
no-std support
If algorithm requires std its feature name will have -std suffix and enable std feature.
License
Licensed under either of
- Apache License, Version 2.0, (license/APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (license/MIT or http://opensource.org/licenses/MIT)
at your option.
Contributions
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.