demo(06-concurrency-race): 06 — Concurrency: lock removed from shared counter increment#155
demo(06-concurrency-race): 06 — Concurrency: lock removed from shared counter increment#155
Conversation
… counter increment See scenarios/06-concurrency-race/README.md for the expected verdict.
There was a problem hiding this comment.
GauntletCI found the following issues:
These findings reference lines outside the PR diff, so they appear here instead of inline. Expand each entry for full evidence, rationale, and suggested action.
GCI0016 — Concurrency and State Risk: Static mutable field detected — potential shared state without synchronization.
GCI0016 — Concurrency and State Risk
Static mutable field detected — potential shared state without synchronization.
Evidence:
Line 12: private static long _processedCount;
Line 34: public static long ProcessedCount => _processedCount;
💡 Suggested action: Make the field readonly, use Interlocked for simple counters, or prefer instance fields with DI.
Confidence: Medium | Severity: Block | 2 occurrences
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 48b91d4c4f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } | ||
|
|
||
| await _repo.UpdateAsync(order, ct).ConfigureAwait(false); | ||
| _processedCount++; |
There was a problem hiding this comment.
Use atomic increment for shared processed counter
ProcessAsync is executed for concurrent HTTP requests, but this line updates a static field with _processedCount++ and no synchronization. That operation is a non-atomic read/modify/write, so simultaneous requests can lose increments and produce an incorrect processed-order metric (and long access can tear on 32-bit runtimes). Use Interlocked.Increment(ref _processedCount) (and an atomic read strategy) or protect updates with a lock.
Useful? React with 👍 / 👎.
06 — Concurrency: lock removed from shared counter increment
Expected verdict: ❌ Fails — GauntletCI should fire GCI0016 (concurrency / non-atomic shared state).
What changed
OrderProcessorgained astatic long _processedCountfor a metricsrollout, exposed via
ProcessedCount. InsideProcessAsyncthe counteris incremented with the bare
++operator:Why this is risky
OrderProcessoris registered as scoped in DI but_processedCountis static — every concurrent HTTP request races on it.
long++on a 32-bit ABI is two non-atomic operations. Reads fromanother thread can observe torn values.
by a lot. The bug looks like "instrumentation drift" instead of a race.
The fix is one of
Interlocked.Increment(ref _processedCount), alock, or a proper metrics primitive (Counter<long>).What GauntletCI catches
GCI0016 Concurrency / non-atomic shared state— astaticmutablefield is being mutated without synchronisation in a code path reachable
from concurrent request handling.