From 93de3c8392b0ae199959cd0bb7dd58653d56591c Mon Sep 17 00:00:00 2001 From: Vadim Salavatov Date: Fri, 10 Jul 2026 13:11:22 +0000 Subject: [PATCH] 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 --- .../IntellijSensitiveDataValidator.kt | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/platform/statistics/src/com/intellij/internal/statistic/eventLog/validator/IntellijSensitiveDataValidator.kt b/platform/statistics/src/com/intellij/internal/statistic/eventLog/validator/IntellijSensitiveDataValidator.kt index 542adf4c4065..0996173044d8 100644 --- a/platform/statistics/src/com/intellij/internal/statistic/eventLog/validator/IntellijSensitiveDataValidator.kt +++ b/platform/statistics/src/com/intellij/internal/statistic/eventLog/validator/IntellijSensitiveDataValidator.kt @@ -90,7 +90,7 @@ open class IntellijSensitiveDataValidator protected constructor( private val recorderId: String, ) : SensitiveDataValidator>(fusComponents?.metadataStorage ?: EMPTY_METADATA_STORAGE) { companion object { - private val instances = ConcurrentHashMap() + private val instances = ConcurrentHashMap>() 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 = object : MetadataStorage { @@ -261,4 +265,4 @@ private class BlindSensitiveDataValidator( } override suspend fun isGroupAllowed(group: EventLogGroup): Boolean = true -} \ No newline at end of file +}