lockfree-object-pool

A lock-free, thread-safe Rust object pool with automatic return, four pool strategies, and zero runtime dependencies.

Library
Cargo
v0.1.6
49stars
BSL-1.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
23/100Needs Attention
Development Activity0
Maintenance0
Community20
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
68/100Good
Architecture78
Code Quality72
Innovation68
Learning Curve55

lockfree-object-pool is a Rust crate providing a thread-safe object pool collection built for high-throughput allocation and deallocation across threads. It ships four interchangeable pool implementations — LinearObjectPool (a lock-free linked list of atomic-bitmask pages), SpinLockObjectPool, MutexObjectPool, and NoneObjectPool (plain allocation without pooling, useful as a baseline) — all sharing the same pull()/pull_owned() API so switching strategies is a one-line change.

Every pulled item is wrapped in a Reusable or OwnedReusable guard that implements Deref/DerefMut and automatically returns the item to its pool (running your reset closure) when the guard drops, so callers never manage pool state manually. The crate has zero runtime dependencies, is published on crates.io with docs.rs documentation, and includes criterion benchmarks comparing all four strategies against the object-pool and sharded-slab crates for allocation, message-forwarding, and deallocation latency.

What You Get

  • Four pool implementations (Linear/lock-free, SpinLock, Mutex, None) behind one consistent API
  • RAII guards (Reusable, OwnedReusable) that auto-return items to the pool on drop via Deref/DerefMut
  • pull_owned() variants for sending pooled items across thread boundaries via Arc
  • Zero runtime dependencies — built entirely on std::sync primitives
  • A criterion-based benchmark suite comparing pool strategies against the object-pool and sharded-slab crates

Common Use Cases

  • Reducing allocation pressure in hot loops that repeatedly create and drop the same type
  • Sharing a pool of reusable buffers or structs across worker threads without per-pull locking overhead (LinearObjectPool)
  • Passing pooled objects between threads via channels using pull_owned() without lifetime headaches
  • Benchmarking or swapping pooling strategies (lock-free vs spinlock vs mutex vs none) to find the best fit for a workload

Under The Hood

Architecture The crate implements four pool variants (Linear, SpinLock, Mutex, None) sharing a common Page/PageId primitive (src/page.rs) that packs up to 32 slots per page behind an AtomicU32 free-bitmask allocated and freed via CAS loops, plus a linked list of pages (src/linear_page.rs) built from raw AtomicPtr nodes that lets LinearObjectPool grow lock-free without ever moving existing allocations. Each pool exposes pull()/pull_owned() returning RAII guards (LinearReusable, LinearOwnedReusable, and their SpinLock/Mutex/None counterparts) that call back into the owning pool’s reset closure via Drop to return an item to its page’s bitmask; the pull_owned() variants hold an Arc-cloned reference to the pool so guards can outlive the stack frame that created them, letting items cross thread boundaries safely. The SpinLockObjectPool and MutexObjectPool build on a hand-rolled SpinLock (src/spin_lock.rs, a compare_exchange_weak loop over an AtomicBool) or std::sync::Mutex respectively, reusing the same guard/reset ownership pattern as the lock-free variant, so swapping pool implementation is a drop-in replacement everywhere pull() is called. Because slot reuse is driven entirely by atomic bitmasks and CAS/pointer tricks rather than data movement, changing Page’s fixed 32-slot layout or PageId’s width would ripple through every pool variant and every guard type.

Tech Stack Pure Rust, edition 2021, with zero runtime dependencies — Cargo.toml declares no [dependencies] entries at all, only dev-dependencies (sharded-slab, object-pool, criterion, criterion-plot) used for benchmarking against comparable pool crates. It relies solely on std primitives — std::sync::atomic (AtomicU32, AtomicBool, AtomicPtr), std::cell::UnsafeCell, std::sync::{Arc, Mutex} — and is published to crates.io and docs.rs with no build-time codegen. CI (.github/workflows/ci.yml) runs cargo check, cargo test, clippy with warnings-as-errors, and cargo fmt —check on every pull request, push to master, and a weekly cron, and the criterion micro-benchmarks checked into the repo under benches/ drive the performance comparison tables published in the README.

Code Quality Tests live under tests/ using shared macros (test_generic.rs’s test_generic_01/02 and test_recycle_generic_01!) instantiated per pool variant (test_linear.rs, test_mutex.rs, test_spin_lock.rs, test_none.rs) covering single-thread pull/reset and five-thread concurrent pull/forward scenarios, plus unit tests inside src/page.rs exercising the bitmask alloc/free logic bit by bit. Unsafe blocks — raw pointer dereferences in LinearPage, UnsafeCell access in Page and SpinLock — are each annotated with a SAFETY comment explaining the invariant relied on, and clippy runs with warnings-as-errors in CI so lint regressions fail the build; error handling favors Option/Result composition (Page::alloc returns Option<PageId> via fetch_update) over panics. There is no evidence of fuzzing or Miri integration, and the unsafe surface, while commented, is inherently harder to fully verify than an all-safe-Rust equivalent.

API Design The public API is small and consistent across all four pool types — each exposes new(init, reset) (or new(init) for NoneObjectPool), pull() returning a Reusable<T>, and pull_owned() via Arc<Self> returning an OwnedReusable<T>, with both guard types implementing Deref/DerefMut so pooled items are used exactly like owned values and returned to the pool automatically on drop, with no explicit release() call ever required. Doc comments on public items include runnable rustdoc examples that are doctested via cargo test, and the top-level lib.rs doc carries a comparison table and rationale against similar crates (object-pool, sharded-slab), which lowers the cost of picking the right variant. The tradeoff for that uniformity is some constructor boilerplate — every pool but NoneObjectPool requires both an init and a reset closure even for types that rarely need custom reset logic.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search