xsync

Concurrent data structures for Go — a CLHT-based map, striped counter, lock-free queues, and a reader-biased mutex built to outscale the standard sync package.

Library
Go
vv3.5.1
1,719stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
56/100Fair
Development Activity28
Maintenance48
Community48
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
78/100Good
Architecture82
Code Quality88
Innovation78
Learning Curve65

xsync provides drop-in replacements for Go’s sync package primitives that are built for high-contention, multicore workloads. Its flagship Map uses a Cache-Line Hash Table (CLHT) design with immutable key/value pair structs and cooperative parallel rehashing, consistently outperforming sync.Map in both read- and write-heavy benchmarks. Alongside the map, the library ships a striped Counter modeled on Java’s LongAdder, bounded and unbounded lock-free queues (SPSC, MPMC, UMPSC), and RBMutex, a reader-biased mutex based on the BRAVO algorithm that gives read-heavy workloads a fast, per-reader path.

The project is used inside other performance-sensitive Go libraries, including the Otter caching library, and is validated with an extensive concurrent stress-testing suite plus published multicore benchmarks comparing it against the standard library and Java’s ConcurrentHashMap.

What You Get

  • A CLHT-based Map[K,V] with Compute, Size, Range/All iteration, lock-free RangeRelaxed iteration, and bulk DeleteMatching
  • A striped Counter for high-throughput increment/decrement counting under contention
  • Three queue variants — bounded SPSCQueue, bounded MPMCQueue, and unbounded UMPSCQueue — for different producer/consumer topologies
  • RBMutex, a reader-biased reader/writer lock with both blocking and optimistic (Try…) lock methods

Common Use Cases

  • Replacing sync.Map in caches and registries that see heavy concurrent reads and writes
  • Counting high-frequency events (metrics, rate limiting) without contending on a single atomic integer
  • Building producer/consumer pipelines that need a lock-free bounded or unbounded queue
  • Protecting read-heavy shared state (config, routing tables) with a reader-biased lock instead of sync.RWMutex

Under The Hood

Architecture The package is a flat, single-package library (package xsync) where each concurrency primitive lives in its own file (map.go, counter.go, rbmutex.go, spscqueue.go, mpmcqueue.go, umpscqueue.go) and shares only low-level helpers from util.go — cache-line-size padding constants and SWAR bit-twiddling routines used to avoid false sharing. Map, the most complex piece, holds an atomic.Pointer to the current bucket table plus a nextTable pointer for in-progress resizes, coordinated through a resizeCtl atomic.Uint64 that packs a resize sequence number and helper-goroutine count, with a sync.Cond used to wake resize waiters. The primitives don’t depend on each other, so the real shared abstraction is util.go’s padding/bit-manipulation layer that every structure builds on to stay cache-friendly.

Tech Stack xsync has zero third-party runtime dependencies — go.mod declares no require entries. It relies entirely on Go 1.24+ stdlib packages (sync, sync/atomic, hash/maphash, math/bits, runtime, unsafe, iter) plus a go:linkname into runtime.cheaprand for a fast non-cryptographic RNG. There’s no bundler, codegen, or build tool beyond go build/go test. CI (GitHub Actions) runs a matrix across Go 1.24 and 1.25, a dedicated 32-bit (GOARCH=386) build, go vet, race-detector test runs, and Codecov coverage reporting.

Code Quality Test files sit near 1:1 with implementation files; map_test.go alone carries dozens of Test functions and over a dozen Benchmark functions exercising concurrent access, resizing, and iteration, and example_test.go supplies runnable godoc examples. Every push runs go vet and the race detector in addition to a 32-bit architecture build, which matters heavily for code doing manual atomic/unsafe bit-packing. Error handling is idiomatic Go — most operations can’t fail, and internal invariant checks are gated behind a test-only assertionsEnabled flag rather than shipped as runtime panics. Naming follows Go convention consistently (New*/Try* constructors), and the trickier unsafe/atomic sections carry explanatory comments on bit-layout invariants.

API Design The public API deliberately mirrors familiar stdlib shapes: Map follows sync.Map’s Load/Store/Delete naming while extending it with Compute, Size, Range/All, lock-free RangeRelaxed, and bulk DeleteMatching, so migrating from sync.Map needs minimal relearning. RBMutex mirrors sync.RWMutex’s Lock/Unlock naming with an added reader-token contract for RLock/RUnlock. Generics (Map[K,V], SPSCQueue[T], UMPSCQueue[T]) give type-safe usage without interface{} boxing, and deprecated pre-generics *Of type aliases are kept for backward compatibility to ease migration across major versions. The trade-off is that several queue/mutex methods are explicitly optimistic (TryEnqueue, TryLock) and leave back-off strategy to the caller — a deliberate low-level choice that raises the bar versus a higher-level channel-based API.

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