Skip to content

Fix: bitmatrix clone for concurrent decode#81

Open
bashhack wants to merge 1 commit intomakiuchi-d:masterfrom
bashhack:fix-bitmatrix-clone-for-concurrent-decode
Open

Fix: bitmatrix clone for concurrent decode#81
bashhack wants to merge 1 commit intomakiuchi-d:masterfrom
bashhack:fix-bitmatrix-clone-for-concurrent-decode

Conversation

@bashhack
Copy link
Copy Markdown
Contributor

Hey @makiuchi-d - hope you're doing okay, apologies for the long gap between contributions - but I have one more to propose.

Summary

BitMatrixParser mutates its input BitMatrix during decoding (UnmaskBitMatrix, Remask, Mirror), but since #75 introduced sync.Once caching in BinaryBitmap.GetBlackMatrix(), all concurrent decoders share the same cached matrix instance. When multiple goroutines decode from the same BinaryBitmap, one goroutine's Flip() calls corrupt the matrix while another is reading, causing occasional NotFoundException with invalid coordinates (e.g. (53, -2)).

This then is a follow-up to my previous contribution #75, which fixed the GetBlackMatrix() race but exposed this deeper mutation-sharing issue.

Changes

  • BitMatrix.Clone(): Implements the deep copy method (stub comment already existed: // public BitMatrix clone()). Returns a new BitMatrix with an independent copy of the backing []uint32 slice.
  • NewBitMatrixParser: Clones the input matrix so that decode mutations (UnmaskBitMatrix, Remask, Mirror) operate on a private copy rather than the shared cached original.

Why clone in the parser, not elsewhere?

  • Removing sync.Once caching from GetBlackMatrix() would work but forces expensive re-binarization on every call. Cloning a BitMatrix is a single copy() of the backing slice.
  • The decoder is the component that needs private mutable state — I still feel the cache is correct to share read-only data.
  • DataMatrix's BitMatrixParser is unaffected: it extracts a data region into a new matrix and never mutates the input.

Behavioral change

Previously, Decoder.Decode(bits, hints) mutated bits as a side effect (left it in an unmasked/mirrored state). Now bits is untouched after decode. This is arguably more correct, I think, as the mutations are implementation details of the decode retry logic, not a documented contract. No code in the repository reads the BitMatrix after decoding.

Testing

Updates and added tests, of course - but while I was here, I also tackled that TestDefaultGridSampler_SampleGrid failure.

@bashhack bashhack force-pushed the fix-bitmatrix-clone-for-concurrent-decode branch from 7cb0f3e to e2a5d3b Compare March 15, 2026 07:52
@bashhack bashhack changed the title Fix bitmatrix clone for concurrent decode Fix: bitmatrix clone for concurrent decode Mar 15, 2026
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 15, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.93%. Comparing base (95e256b) to head (e2a5d3b).

Additional details and impacted files
@@           Coverage Diff           @@
##           master      #81   +/-   ##
=======================================
  Coverage   97.93%   97.93%           
=======================================
  Files         122      122           
  Lines       12951    12955    +4     
=======================================
+ Hits        12684    12688    +4     
  Misses        191      191           
  Partials       76       76           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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.

1 participant