IJPL-247224 avoid FUS validator IO under CHM lock

IntellijSensitiveDataValidator used ConcurrentHashMap.computeIfAbsent to construct validator instances. The construction path initializes FUS metadata storage and can perform file IO, so a concurrent EP listener that clears the validator cache during dynamic plugin loading could block the EDT on the map's internal lock.

Cache a synchronized Lazy per recorder instead. The map computation now only installs the lightweight lazy holder, while metadata storage initialization happens after computeIfAbsent returns. The custom validation rule listeners still clear the cache synchronously, but no longer wait for the expensive initialization under ConcurrentHashMap locks.

getIfInitialized now checks Lazy.isInitialized() to preserve its non-initializing behavior.

M-Session-Id: M-db7da5cd-2c3e-4950-b720-e50f19fd2500

GitOrigin-RevId: d856f96e5e5a5180856ac4a0ef9adab49f402b77
This commit is contained in:
Vadim Salavatov
2026-07-20 13:20:45 +00:00
committed by intellij-monorepo-bot
parent 654b90f5c9
commit 93de3c8392
@@ -90,7 +90,7 @@ open class IntellijSensitiveDataValidator protected constructor(
private val recorderId: String,
) : SensitiveDataValidator<MetadataStorage<EventLogBuild>>(fusComponents?.metadataStorage ?: EMPTY_METADATA_STORAGE) {
companion object {
private val instances = ConcurrentHashMap<String, IntellijSensitiveDataValidator>()
private val instances = ConcurrentHashMap<String, Lazy<IntellijSensitiveDataValidator>>()
init {
CustomValidationRule.EP_NAME.addChangeListener({ instances.clear() }, null)
@@ -105,18 +105,22 @@ open class IntellijSensitiveDataValidator protected constructor(
@JvmStatic
fun getInstance(recorderId: String): IntellijSensitiveDataValidator {
return instances.computeIfAbsent(recorderId) { id ->
if (ApplicationManager.getApplication().isUnitTestMode) {
BlindSensitiveDataValidator(FusComponentProvider.createBlindFusComponents(id), id)
// Validator creation initializes metadata storage and may do IO; keep it outside ConcurrentHashMap locks.
lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
if (ApplicationManager.getApplication().isUnitTestMode) {
BlindSensitiveDataValidator(FusComponentProvider.createBlindFusComponents(id), id)
}
else {
IntellijSensitiveDataValidator(FusComponentProvider.createFusComponents(id), id)
}
}
else {
IntellijSensitiveDataValidator(FusComponentProvider.createFusComponents(id), id)
}
}
}.value
}
@JvmStatic
fun getIfInitialized(recorderId: String): IntellijSensitiveDataValidator? {
return instances[recorderId]
val validator = instances[recorderId] ?: return null
return if (validator.isInitialized()) validator.value else null
}
private val EMPTY_METADATA_STORAGE: MetadataStorage<EventLogBuild> = object : MetadataStorage<EventLogBuild> {
@@ -261,4 +265,4 @@ private class BlindSensitiveDataValidator(
}
override suspend fun isGroupAllowed(group: EventLogGroup): Boolean = true
}
}