Skip to content

Serialize copy-on-write marks during Clone - #574

Open
SashaMIT wants to merge 1 commit into
RoaringBitmap:masterfrom
SashaMIT:fix/cow-clone-race
Open

SashaMIT wants to merge 1 commit into
RoaringBitmap:masterfrom
SashaMIT:fix/cow-clone-race

Conversation

@SashaMIT

@SashaMIT SashaMIT commented Oct 9, 2026 •

Copy link
Copy Markdown

Description

Concurrent Clone of one copy-on-write bitmap writes the source needCopyOnWrite slice. Two goroutines that only clone the same bitmap race on that slice, even when nobody else writes. The mark stays, because a later write still has to copy containers that are shared with the source. The source mark now runs under a mutex.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Performance improvement
  • Code refactoring
  • Documentation update
  • Test improvements
  • Build/CI changes

Changes Made

What was changed?

  • roaringArray and roaringArray64 hold a pointer mutex that guards the source flag write inside Clone.
  • SetCopyOnWrite(true), frozen deserialization, and the 64-bit portable reader allocate that mutex before clones can run together.
  • roaring64 matches the 32-bit path.

Why was it changed?

How was it changed?

  • The mutex is a pointer, so Bitmap can still be copied by value.
  • The lock covers only the source mark. The child mark writes a fresh slice and is not locked.

Testing

Please add a unit test if you are fixing a bug.

go test -race -run TestConcurrentCloneOfCOWBitmap failed before the lock with a data race in markAllAsNeedingCopyOnWrite. The same test passes after the lock, in the root package and in roaring64. A write to the clone does not show up on the source.

go test -count=1 -timeout 180s . ./roaring64 ./BitSliceIndexing passed.

go vet still reports the existing ReadFrom signature mismatch in roaring.go. This change does not touch that method.

Formatting

go fmt was run on the changed Go files.

Fuzzing

The serialization format is unchanged, so I did not regenerate the SMAT corpus or run the 300s fuzzer.

Performance Impact

The mutex is taken only around the flag writes during a copy-on-write clone. Container copies are unchanged. I did not run the benchmark suite.

Breaking Changes

None.

Related Issues

Fixes #528

Concurrent Clone calls wrote the source needCopyOnWrite flags at the same time. The mark is still required so a later write copies shared containers, and it now runs under cowMu.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bitmap.Clone() data race on copy-on-write bitmaps (no writers)

1 participant