crossync provides concurrent data structures and synchronization primitives for Rust.
It combines atomic operations, backoff strategies, and internal locks to support shared access in multithreaded applications.
- Concurrent collections for shared state, queues, and message passing
- Interior mutability with synchronized access
- Reference-counted handles for sharing supported containers without copying their contents
Add crossync to your Cargo.toml:
[dependencies]
crossync = "0.2.1"AtomicVec<T> is a thread-safe collection supporting concurrent push and pop operations.
It uses block-based allocation, atomic indices, and backoff strategies to manage storage and contention.
- Suitable for work queues and dynamic collections
- Shared and exclusive locking for access and reset operations
- Automatic block recycling and free-list management
- Conversion to a standard
Vec<T>by consuming elements
use std::thread;
use crossync::atomic::AtomicVec;
let h = AtomicVec::new();
h.push("hello");
let b = h.clone();
drop(h);
{
let b = b.clone();
let t = thread::spawn(move || {
if let Some(v) = b.pop() {
assert_eq!(v, "hello");
}
});
t.join().unwrap();
}
assert!(b.pop().is_none());AtomicHashMap<K, V> is a thread-safe hash map that supports concurrent insertion, retrieval, and removal of key-value pairs.
It combines atomic operations with internal locks to coordinate access.
- Suitable for shared caches and application state
- Resizable bucket storage for growing collections
use std::thread;
use crossync::atomic::AtomicHashMap;
let h = AtomicHashMap::new();
h.insert("c", "hello");
let b = h.clone();
drop(h);
{
let b = b.clone();
let t = thread::spawn(move || {
if let Some(mut v) = b.get_mut("c") {
*v = "world";
}
});
t.join().unwrap();
}
assert_eq!(b.get("c").unwrap(), "world");AtomicBuffer<T> is a bounded, thread-safe ring buffer with per-slot sequence numbers.
It uses atomic push and pop operations for concurrent producers and consumers. A stalled reservation can delay other operations.
- Suitable for work queues, message passing, and object pools
push_boxandpop_boxtransfer ownership safely; rawpushis unsafe and requires a uniquely owned, non-nullBoxallocation
use crossync::atomic::AtomicBuffer;
use std::thread;
let buffer = AtomicBuffer::with_capacity(2);
let producer = {
let buffer = buffer.clone();
thread::spawn(move || {
buffer.push_box(Box::new(1)).unwrap();
buffer.push_box(Box::new(2)).unwrap();
})
};
let consumer = {
let buffer = buffer.clone();
thread::spawn(move || {
let mut count = 1;
while count <= 2 {
if let Some(value) = buffer.pop_box() {
assert_eq!(*value, count);
count += 1;
}
}
})
};
producer.join().unwrap();
consumer.join().unwrap();AtomicCell<T> is a thread-safe, lock-assisted container for a single value.
It provides interior mutability through synchronized access and uses reference counting to share the value across cloned handles.
- Suitable for shared single-value state in multithreaded programs
use std::thread;
use crossync::atomic::AtomicCell;
let c = AtomicCell::new(10);
let c2 = c.clone();
let handle = thread::spawn(move || {
let mut v = c2.get_mut();
*v += 1;
});
handle.join().unwrap();
assert_eq!(*c.get(), 11);AtomicArray<T> is a lock-assisted, thread-safe array supporting concurrent reads and writes.
It combines atomic indices and per-slot locks to coordinate access to stored values.
- Backoff strategies manage contention during concurrent operations
use std::thread;
use crossync::atomic::AtomicArray;
let arr = AtomicArray::with_capacity(4);
let arr_clone = arr.clone();
let t = thread::spawn(move || {
let _ = arr_clone.push(10);
});
t.join().unwrap();
arr.for_each_mut(|v| {
*v *= 2;
});
assert_eq!(*arr.get(0).unwrap(), 20);Atomic<T> is a generic, lock-assisted container that provides synchronized access to a Rust value.
It supports primitives, structs, enums, collections, and other user-defined types. Sharing an Atomic container between threads requires T: Send, including types such as Cell and RefCell that are !Sync. The container manages locking internally.
- Load, store, swap, update, and compare-exchange operations, subject to method-specific trait bounds
- Specialized methods for
Vec<T>,String, andOption<T> - Numeric and bitwise operations such as
fetch_addandfetch_sub
use crossync::atomic::Atomic;
use std::sync::Arc;
use std::thread;
#[derive(Debug, Clone, PartialEq)]
struct Person {
name: String,
age: u32,
}
let atomic = Arc::new(Atomic::new(Person {
name: "Alice".to_string(),
age: 30,
}));
let atomic2 = atomic.clone();
let handle = thread::spawn(move || {
atomic2.update(|p| {
p.name = "Bob".to_string();
p.age += 1;
});
});
handle.join().unwrap();
let result = atomic.load();
assert_eq!(result.name, "Bob");
assert_eq!(result.age, 31);The following rules apply to the lock-assisted atomic containers:
Atomic,AtomicCell,AtomicArray, andAtomicHashMapserialize access to the same value or map bucket, including read callbacks andget()guards.- Nested reads on the acquiring thread are supported. Conflicting mutable reentrance and read-to-write upgrades panic before creating an alias.
- Release guards before conflicting access or array reset; acquire distinct locks in a consistent order.
- Read guards can transfer between threads when
T: Sync; exclusive guards can transfer whenT: Send. Reentrance checks use the acquiring-thread identity until the acquisition or read group is released. AtomicHashMap::hasher()returns aDeref<Target = S>guard. Usemap.hasher().build_hasher()for method calls,&*map.hasher()for&Sarguments, ormap.hasher_ref()whenS: Sync. Default constructors allow parallel access to theirSyncbuilder.
See CONCURRENCY.md for synchronization details and verification commands.
use crossync::atomic::AtomicCell;
use std::cell::Cell;
use std::thread;
let value = AtomicCell::new(Cell::new(0));
thread::scope(|scope| {
for _ in 0..4 {
let value = &value;
scope.spawn(move || value.with(|cell| cell.set(cell.get() + 1)));
}
});
assert_eq!(value.get().get(), 4);RwLock<T> is a synchronization primitive that supports multiple readers or a single writer.
It uses atomic operations and platform-specific waiting mechanisms to coordinate access.
- Shared (read) and exclusive (write) locking modes
- Reference-counted cloning without copying the stored value
- No lock poisoning
use crossync::sync::RwLock;
use std::thread;
use std::thread::sleep;
use std::time::Duration;
let mutex = RwLock::new(5);
let m1 = mutex.clone();
let h1 = thread::spawn(move || {
let _guard = m1.lock_exclusive();
sleep(Duration::from_millis(10));
});
let m2 = mutex.clone();
let h2 = thread::spawn(move || {
let _guard = m2.lock_shared();
sleep(Duration::from_millis(10));
});
h1.join().unwrap();
h2.join().unwrap();Barrier coordinates groups of threads by blocking waiters until the required number of arrivals is reached.
Barrier::with_capacity(n, bucket) requires n arrivals for the first phase and bucket arrivals for subsequent phases.
A zero bucket disables the barrier after the first phase; a zero n creates an already-disabled barrier.
- Suitable for parallel algorithms, phased execution, and workload synchronization
use crossync::sync::Barrier;
use std::thread;
let barrier = Barrier::with_capacity(3, 0);
let mut handles = vec![];
for _ in 0..3 {
let c = barrier.clone();
handles.push(thread::spawn(move || {
println!("Waiting...");
c.wait();
println!("Released!");
}));
}
for h in handles {
h.join().unwrap();
}Licensed under the Apache License 2.0.