Skip to content

FIX: Address matrix race conditions with sync.Once#75

Merged
makiuchi-d merged 4 commits intomakiuchi-d:masterfrom
bashhack:fix-matrix-race-condition
Jul 20, 2025
Merged

FIX: Address matrix race conditions with sync.Once#75
makiuchi-d merged 4 commits intomakiuchi-d:masterfrom
bashhack:fix-matrix-race-condition

Conversation

@bashhack
Copy link
Copy Markdown
Contributor

@bashhack bashhack commented Jul 17, 2025

@makiuchi-d first of all - thank you for maintaining gozxing!

It's wonderful to see a pure Go implementation and with such a clean API design, it's great to use! In that spirit, I hope you don't mind my submission for consideration.

Problem

While using gozxing in a high-throughput QR scanning environment, BinaryBitmap.GetBlackMatrix() and HybridBinarizer.GetBlackMatrix() have race conditions when accessed concurrently by multiple goroutines.

This prevents safely caching and reusing BinaryBitmap instances across requests.

Why This Matters

Creating a BinaryBitmap from an image can be computationally expensive:

  • 1.8.ms for a 400x400 QR code
  • 8.7ms for a 1080p document

For services processing thousands of QR codes per second, caching these preprocessed bitmaps can provider real-world benefit. My ad hoc benchmarking here suggests:

  • 12x faster QR code detection
  • 1000x fewer memory allocations
  • 45x less memory usage

However, with the code today, it appears that developers must choose between:

  1. Performance - Cache the bitmaps, but run risk of crashes
  2. Safety - Create new bitmaps for each request with (12x slower)

Proposed Solution

I think we can get the benefits of your great library here but extend its performance use cases. The approach I've taken is to use sync.Once to ensure thread-safe lazy initialization of the black matrix. This is a minimal change that (at least in my testing) preserves all existing behavior while adding thread safety.

I've applied the same pattern to the HybridBinarizer, of course.

Verification

  1. Race detector passes now - go test -race ./... shows no issues
  2. Benchmarks unchanged - Benefit of caching the BinaryBitmap does not suffer with my changes
  3. Added test coverage - I've aimed to ensure coverage shows safety of this proposal

Demonstration

A complete comparison showing the race conditions and the fix is available at:
https://github.com/bashhack/gozxing-thread-safe-compare

I show there how this change:

  • Fixes the race conditions
  • Keeps performance characteristics
  • Enables developers to leverage caching strategies with gozxing safely

Breaking Changes

None that I can tell - the behavior for single-threaded access remains identical. Fully backward compatible.

Checklist

  • Tests pass with go test -race
  • No performance regression for single-threaded use
  • Documentation updated with thread safety information
  • Real-world use case demonstrated with benchmarks

I'm happy to make any adjustments to better align with your project's goals. Thank you for considering this contribution!

@makiuchi-d
Copy link
Copy Markdown
Owner

Thank you for your great contribution!
I’m happy to merge it.

@makiuchi-d makiuchi-d merged commit 95e256b into makiuchi-d:master Jul 20, 2025
1 check passed
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.

2 participants