fix(core): fix check-then-act race conditions on ConcurrentHashMap in core module#14752
fix(core): fix check-then-act race conditions on ConcurrentHashMap in core module#14752daguimu wants to merge 1 commit intoalibaba:developfrom
Conversation
… core module Replace containsKey()+get() with single get()+null check in 2 classes: - IdGeneratorManager.nextId - PluginManager.loadPersistedData
|
Thanks for your this PR. 🙏 感谢您提交的PR。 🙏 |
KomachiSion
left a comment
There was a problem hiding this comment.
This PR I think is over protected. So I think can't be merged.
| Map<String, Boolean> states = persistence.loadAllStates(); | ||
| states.forEach((pluginId, enabled) -> { | ||
| if (pluginRegistry.containsKey(pluginId)) { | ||
| PluginInfo pluginInfo = pluginRegistry.get(pluginId); |
There was a problem hiding this comment.
This method will be called in single thread(only in once at ApplicationReadyEvent callback).
| public long nextId(String resource) { | ||
| if (generatorMap.containsKey(resource)) { | ||
| return generatorMap.get(resource).nextId(); | ||
| IdGenerator generator = generatorMap.get(resource); |
There was a problem hiding this comment.
These is no problem, IdGenerator will not be removed
Problem
Two classes in the core module use
containsKey()followed byget()onConcurrentHashMapfields. Although each method is individually thread-safe, the compound operation is not atomic, creating a check-then-act race condition that can causeNullPointerException.Race Condition Explained
Take
IdGeneratorManager.nextId()as an example:Between Step 1 and Step 2, another thread may modify the map:
ConcurrentHashMapguarantees thread-safety for individual operations, but NOT for compound operations spanning two separate method calls. The time window betweencontainsKey()returningtrueand the subsequentget()call is a race condition window.Root Cause
IdGeneratorManager.generatorMapis aConcurrentHashMap<String, IdGenerator>(line 38), populated viaregister()and accessed from multiple threads innextId().PluginManager.pluginRegistryis aConcurrentHashMap<String, PluginInfo>(line 92), modified at runtime byApplicationReadyEventlistener and plugin state synchronizer, whileloadPersistedData()reads it.Fix
Replace the two-step
containsKey()+get()with a singleget()call and null check:This eliminates the race window because:
get())Changes
IdGeneratorManagernextId(String resource)PluginManagerloadPersistedData()Tests
All existing tests pass (7
IdGeneratorManagerTest+ 29PluginManagerTest). The fix is behavior-preserving under non-concurrent conditions - it only prevents NPE when concurrent map modifications occur.Impact
Prevents potential NPE in ID generation and plugin management paths. No behavioral change under normal single-threaded access.