lukaszlenart commented on PR #1781:
URL: https://github.com/apache/struts/pull/1781#issuecomment-5002231569

   ## Code Review
   
   ### Overview
   Fixes a data race in `XSLTResult.getTemplates()`. The static templates cache 
was a plain `HashMap` read via an unsynchronized `get()` while writes happened 
inside a `synchronized` block. The PR swaps `HashMap` → `ConcurrentHashMap`, 
adds a double-checked read inside the `synchronized` block, and inlines the 
field initializer (dropping the `static {}` block).
   
   The diagnosis is correct and the fix is sound. Concurrent read/write on a 
plain `HashMap` is a genuine bug (data corruption, and CPU-spinning infinite 
loops on older JDKs) — a legitimate correctness/hardening fix.
   
   ### Correctness ✅
   Traced the `noCache` interaction, the part most likely to break:
   
   - **`noCache == false`, concurrent miss**: outer `get()` null → both threads 
enter `synchronized` sequentially → the double-check `get()` lets the second 
thread see the first thread's `put()` and skip recompilation. Exactly the 
redundant-compilation fix, and correct.
   - **`noCache == true`**: the inner guard preserves `noCache || (...)`, so it 
still recompiles every call. Behavior unchanged.
   - **Return value**: `templates` is correctly reassigned on every path (cache 
hit, double-check hit, fresh compile), so the returned reference is always 
valid.
   
   No regression in observable behavior; the only new effect is avoiding 
duplicate compilation on concurrent misses, which is the stated goal.
   
   ### Style / Conventions ✅
   - Inlining the initializer and removing the `static {}` block is a clean 
simplification.
   - The re-check comment is helpful and matches surrounding style.
   
   ### Suggestions / Minor Notes
   - **Pre-existing, not introduced here:** with `noCache == true` the code 
still executes `templatesCache.put(path, templates)`, polluting the shared 
static cache from a "no-cache" instance. Out of scope here, but worth a 
follow-up — arguably a `noCache` result should never populate the shared cache.
   - **`synchronized (templatesCache)` as a map lock is now redundant** (the 
map is thread-safe), but it's still needed to serialize *compilation* and dedup 
work — keeping it is right. A `computeIfAbsent` alternative would hold a bin 
lock across the slow `newTemplates()` call, so the current approach is 
preferable. No change needed.
   - **Test coverage:** `XSLTResultTest.java` exists but the PR adds no test. 
Concurrency is hard to test deterministically, but the double-check dedup 
behavior is testable — e.g. a subclass counting `newTemplates` invocations to 
assert a single path is compiled once. Optional but would lock in the intent.
   
   ### Security
   The underlying `HashMap` race could theoretically spin CPU under concurrent 
access, but this is defensive concurrency hardening on an internal template 
cache, not a classic Struts attack surface (OGNL/params/upload). Reasonable as 
a public PR.
   
   ### Verdict
   **Approve.** Small, correct, well-reasoned fix. The 
`noCache`-populates-cache observation and a dedup unit test are the only 
follow-ups worth considering, both optional.
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to