17 stable releases
Uses new Rust 2024
| 2.0.0 | Jan 24, 2026 |
|---|---|
| 1.9.3 | Jan 24, 2026 |
| 1.8.2 | Dec 28, 2025 |
| 1.7.0 | Nov 30, 2025 |
| 0.1.0 | Nov 5, 2025 |
#240 in Concurrency
Used in ubq
81KB
1.5K
SLoC
AtomicList
AtomicList is an experimental circular list built directly from intrusive,
reference-counted nodes. The crate ships nodes and cursors only—there is no
separate list container—so you keep a Node<T> (and optionally a shared
cursor::Cursor) as your handle into the ring. A sync::Node<T> acts like a
tiny Arc<T> that also stores strong and weak successors, letting CAS
loops splice nodes in and out of a ring without locks or external hazard-pointer
schemes.
Current surface area
Node<T>/WeakNode<T>: Arc/Weak-like handles with embedded successor edges.- Lock-free splice helpers:
push_before,pop_when, andremoverearrange a ring while maintaining the self-loop invariants on detached nodes. - Successor introspection:
load_next_*plusresolve_nextfollow weak breadcrumbs after a node has been popped out. - Shared traversal:
cursor::Cursoris an atomic cursor that multiple holders advance together. - Reusable atomics:
atm_pexports generic atomic pointer wrappers used by the list but also usable forArc/Weak.
Working with nodes
use atomic_list::sync::Node;
let root = Node::new("root");
root.push_before("worker-1", |cur| *cur == "root").unwrap();
root.push_before("worker-2", |_| true).unwrap();
// Remove the first node whose payload ends with "1".
let removed = Node::pop_when(&root, |cur| cur.ends_with('1')).unwrap();
assert_eq!(*removed, "worker-1");
// The ring closes back over the gap.
let worker_2 = Node::load_next_strong(&root);
assert_eq!(worker_2.to_string(), "worker-2");
assert!(Node::ptr_eq(
&Node::load_next_strong(&worker_2),
&root
));
// Detached nodes keep a weak breadcrumb into the live ring.
assert_eq!(
Node::resolve_next(&removed).unwrap().to_string(),
"root"
);
assert_eq!(Node::into_inner(removed), Some("worker-1"));
Coordinated traversal
use atomic_list::{cursor::Cursor, sync::Node};
let head = Node::new(0);
for i in 1..=3 {
head.push_before(i, |_| true).unwrap();
}
// Multiple holders share the same atomic cursor position.
let mut cursor_a = Cursor::new(head.clone());
let mut cursor_b = cursor_a.clone();
assert_eq!(*cursor_a.next().unwrap(), 3);
assert_eq!(*cursor_b.next().unwrap(), 3);
// Advancing from either handle moves everyone forward.
assert_eq!(*cursor_a.next().unwrap(), 2);
assert_eq!(*cursor_b.next().unwrap(), 2);
Notes
- Pure atomics: no hazard pointers or epoch GC, just ref-counted nodes.
- There is no separate
AtomicListstruct; hang on to aNode<T>as your entry point into the ring. - The API is experimental and may still shift as iteration ergonomics improve.
License
This project is distributed under the terms of the MIT license. See LICENSE for details.