Fix java/unsynchronized-getter CodeQL alerts in DebugLogPublisher - #788
Merged
vharseko merged 1 commit intoJul 30, 2026
Merged
Conversation
The trace settings maps were mutated under synchronized setters while the getters read them without any lock, which CodeQL flags as inconsistent synchronization (alerts 651 and 652). Make the access lock-free instead of locking the getters: both maps are now final, eagerly created ConcurrentHashMaps, so they are safely published and the null checks spread over the accessors become unnecessary. The compound updates keep their atomicity through ConcurrentHashMap.compute() on the class name: adding a method setting creates the enclosing map and inserts in one atomic step, and removing the last method setting discards that map without racing against a concurrent insertion. Add DebugLogPublisherTest covering the scope resolution, the removal bookkeeping and the concurrent add/remove path.
maximthomas
approved these changes
Jul 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the two remaining
error-severity code scanning alerts, #651 and #652 (java/unsynchronized-getter), ongetClassSettings()andgetMethodSettings().#785 already made the trace settings maps concurrent, but the alerts stayed open: the rule is structural — a
synchronizedsetter paired with an unsynchronized getter — andsetClassSettings(),setMethodSettings()andremoveTraceSettings()still held the monitor.Rather than locking the getters, this makes the whole access path lock-free:
final, eagerly createdConcurrentHashMaps. They are therefore safely published to every reader withoutvolatile, and thenullchecks spread over four accessors go away — the constructor always adds the global scope, sonullwas unreachable anyway.synchronizedis dropped from all three mutators, so no method of the class is synchronized any more.ConcurrentHashMap.compute()keyed by class name.setMethodSettings()creates the enclosing map and inserts the setting in one atomic step;removeTraceSettings()removes the method and discards the map once it is empty in the same computation. Both go throughcompute()on the same key, so they remain mutually exclusive.This matters beyond the alert:
getMethodSettings()hands out the live inner map, whichDebugTracercaches and reads on the hottraceException()path while an administrative change to a debug target can be mutating it.Behaviour is unchanged — the walk up the scope hierarchy, the global fallback that only applies while
size() == 1, thenullreturned bygetMethodSettings()when no method-level tracing is configured, and the removed settings returned byremoveTraceSettings(). The javadoc ofgetMethodSettings()was also promising an unmodifiable map while returning a live one; it now describes what it returns.TraceSettingsstill mutates its own non-volatile fields fromapplyConfigurationChange()while tracer threads read them. CodeQL does not flag it and it needs a different fix (make the class immutable and replace the instance in the map), so it is left out of this change.Testing
New
DebugLogPublisherTestcovers the defaults, the global fallback, the most-specific-scope resolution, the method settings bookkeeping, dropping the enclosing map with its last method setting, and a contention test (4 writers × 2 readers × 5000 rounds) asserting that no setting is lost.mvn -pl opendj-server-legacy test-compile— success;DebugLogPublisherTest6/6 and the neighbouringTraceSettingsTest3/3 pass.compute()atomicity rather than passing vacuously.