Make Pipe serialization-completion flags volatile to fix cross-lock visibility race#2570
Make Pipe serialization-completion flags volatile to fix cross-lock visibility race#2570gynoro wants to merge 1 commit into
Conversation
serializationComplete and rawSerializationComplete are written under synchronized(this) / no lock, but read in consume()/consumePostActions() while holding the ReentrantLock 'lock'. Because the fields are not volatile and the write and read paths use different monitors, the JMM provides no happens-before edge between them. On the content-altering out path (outputBuffer != null) the consumer can therefore observe a stale completion flag at the empty-buffer boundary and either complete the response early (truncation) or fail to suspend the encoder (short/spliced body), corrupting responses at ~8 KB multiples under load. The pure-relay path is unaffected because it gates on producerCompleted, which is published under the same 'lock'. Marking both flags volatile establishes happens-before for every read and write independent of which lock is held, closing the visibility gap with no change to locking behavior. See wso2/api-manager#5099 for the full analysis.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughSummaryThis PR fixes a memory visibility race condition in the Changes Made
Why This MattersThese fields were previously written under ImpactThe fix is minimal and behavior-preserving. By making these fields WalkthroughIn 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Purpose
serializationCompleteandrawSerializationCompleteinPipeare written undersynchronized(this)(or with no lock, insetRawSerializationComplete) but read inconsume()/consumePostActions()while the calling thread holds theReentrantLock lock. Because the fields are notvolatileand the write/read paths use different monitors, the JMM provides no happens-before edge between them. On the content-altering out path (outputBuffer != null) the consumer can observe a stale completion flag at the empty-buffer boundary and either complete the response early (truncation) or fail to suspend the encoder (short/spliced body), corrupting responses at ~8 KB IO-buffer multiples under load. The pure-relay path (outputBuffer == null) is unaffected because it gates onproducerCompleted, which is published under the samelock.Resolves wso2/api-manager#5099
Goals
Close the cross-lock memory-visibility gap on the two serialization-completion flags so the consumer always observes the latest value regardless of which lock the writer held.
Approach
Mark both
serializationCompleteandrawSerializationCompleteasvolatile. This establishes a happens-before edge for every read and write independent of the monitor held, with no change to locking strategy or control flow. Minimal, behavior-preserving fix.Release note
Fixed a rare data race on the PassThrough
Pipeserialization-completion flags that could produce truncated or spliced HTTP responses at IO-buffer boundaries under load.Documentation
N/A — internal concurrency fix, no user-facing or API change.
Training
N/A
Certification
N/A — no behavioral or API change.
Automation tests
Security checks
Samples
N/A
Related PRs
N/A
Migrations (if applicable)
N/A
Test environment
Field-validated against WSO2 API Manager 2.6.0 (synapse-core 2.1.7-wso2v80) on JDK 8 / Linux. The corruption (~5–10 per million responses, at ~8 KB multiples) disappears when a no-op
<enrich>mediator forces an in-memory copy, consistent with the diagnosed race on the content-altering path.Learning
Diagnosed from production field evidence by correlating truncated/spliced response sizes with IO-buffer multiples, then tracing the lock asymmetry between the flag writers (
synchronized(this)) and readers (ReentrantLock). See wso2/api-manager#5099 for the full analysis.