From 0087518064b389ee9e017a0a41cc45fac0e3819a Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Tue, 11 Apr 2017 11:50:49 +0200 Subject: [PATCH] lock-free access to allSettings list remove duplicated method getSortedConfigurations (already existed in the API method `allSettings` should be used instead) --- .../com/intellij/execution/RunManagerEx.java | 8 +- .../RunConfigurationsComboBoxAction.java | 5 +- .../impl/ModuleRunConfigurationManager.java | 3 +- .../impl/ProjectRunConfigurationManager.java | 1 - .../RunConfigurationBeforeRunProvider.java | 6 +- .../intellij/execution/impl/RunManagerImpl.kt | 278 ++++++++++-------- .../src/com/intellij/mock/MockRunManager.kt | 4 - .../jetbrains/python/PyModuleListener.java | 8 +- 8 files changed, 171 insertions(+), 142 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/execution/RunManagerEx.java b/platform/lang-impl/src/com/intellij/execution/RunManagerEx.java index cba123dcddf7..34a9f409641d 100644 --- a/platform/lang-impl/src/com/intellij/execution/RunManagerEx.java +++ b/platform/lang-impl/src/com/intellij/execution/RunManagerEx.java @@ -97,8 +97,14 @@ public abstract class RunManagerEx extends RunManager { public abstract Icon getConfigurationIcon(@NotNull RunnerAndConfigurationSettings settings, boolean withLiveIndicator); + /** + * @deprecated Use {@link #getAllSettings()} + */ @NotNull - public abstract Collection getSortedConfigurations(); + @Deprecated + public final Collection getSortedConfigurations() { + return getAllSettings(); + } public abstract void removeConfiguration(@Nullable RunnerAndConfigurationSettings settings); diff --git a/platform/lang-impl/src/com/intellij/execution/actions/RunConfigurationsComboBoxAction.java b/platform/lang-impl/src/com/intellij/execution/actions/RunConfigurationsComboBoxAction.java index cf287e5f7f54..0779bb86c6b4 100644 --- a/platform/lang-impl/src/com/intellij/execution/actions/RunConfigurationsComboBoxAction.java +++ b/platform/lang-impl/src/com/intellij/execution/actions/RunConfigurationsComboBoxAction.java @@ -34,6 +34,7 @@ import com.intellij.ui.IdeBorderFactory; import com.intellij.ui.SizedIcon; import com.intellij.ui.components.panels.NonOpaquePanel; import com.intellij.util.IconUtil; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.EmptyIcon; import com.intellij.util.ui.JBUI; import org.jetbrains.annotations.NotNull; @@ -41,7 +42,6 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; -import java.util.Iterator; import java.util.List; import java.util.Map; @@ -225,8 +225,7 @@ public class RunConfigurationsComboBoxAction extends ComboBoxAction implements D if (selectedConfiguration != null && selectedConfiguration.isTemporary()) { return selectedConfiguration; } - Iterator iterator = RunManager.getInstance(project).getTempConfigurationsList().iterator(); - return iterator.hasNext() ? iterator.next() : null; + return ContainerUtil.getFirstItem(RunManager.getInstance(project).getTempConfigurationsList()); } } diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ModuleRunConfigurationManager.java b/platform/lang-impl/src/com/intellij/execution/impl/ModuleRunConfigurationManager.java index 0b853cf4f5e5..f6b47beffc2c 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ModuleRunConfigurationManager.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ModuleRunConfigurationManager.java @@ -99,7 +99,7 @@ public final class ModuleRunConfigurationManager implements PersistentStateCompo @NotNull private Collection getModuleRunConfigurationSettings() { - return ContainerUtil.filter(myManager.getConfigurationSettings(), myModuleConfigCondition); + return ContainerUtil.filter(myManager.getAllSettings(), myModuleConfigCondition); } private boolean usesMyModule(RunConfiguration config) { @@ -152,6 +152,5 @@ public final class ModuleRunConfigurationManager implements PersistentStateCompo // IDEA-60004: configs may never be sorted before write, so call it manually after shared configs read myManager.setOrdered(false); - myManager.getSortedConfigurations(); } } diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ProjectRunConfigurationManager.java b/platform/lang-impl/src/com/intellij/execution/impl/ProjectRunConfigurationManager.java index 8237bdafedad..ef606a5dc6a2 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ProjectRunConfigurationManager.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ProjectRunConfigurationManager.java @@ -90,7 +90,6 @@ public class ProjectRunConfigurationManager implements PersistentStateComponent< // IDEA-60004: configs may never be sorted before write, so call it manually after shared configs read myManager.setOrdered(false); - myManager.getSortedConfigurations(); } static class RunConfigurationStateSplitter extends StateSplitterEx { diff --git a/platform/lang-impl/src/com/intellij/execution/impl/RunConfigurationBeforeRunProvider.java b/platform/lang-impl/src/com/intellij/execution/impl/RunConfigurationBeforeRunProvider.java index 440f12c2d6b6..d8112243eb87 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/RunConfigurationBeforeRunProvider.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/RunConfigurationBeforeRunProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -113,7 +113,7 @@ extends BeforeRunTaskProvider configurations = - RunManagerImpl.getInstanceImpl(runConfiguration.getProject()).getSortedConfigurations(); + RunManagerImpl.getInstanceImpl(runConfiguration.getProject()).getAllSettings(); if (configurations.isEmpty() || (configurations.size() == 1 && configurations.iterator().next().getConfiguration() == runConfiguration)) { return null; @@ -144,7 +144,7 @@ extends BeforeRunTaskProvider configurations = new ArrayList<>(RunManagerImpl.getInstanceImpl(project).getSortedConfigurations()); + List configurations = new ArrayList<>(RunManagerImpl.getInstanceImpl(project).getAllSettings()); String executorId = DefaultRunExecutor.getRunExecutorInstance().getId(); for (Iterator iterator = configurations.iterator(); iterator.hasNext();) { RunnerAndConfigurationSettings settings = iterator.next(); diff --git a/platform/lang-impl/src/com/intellij/execution/impl/RunManagerImpl.kt b/platform/lang-impl/src/com/intellij/execution/impl/RunManagerImpl.kt index 3e1dc6729386..058e9525d75d 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/RunManagerImpl.kt +++ b/platform/lang-impl/src/com/intellij/execution/impl/RunManagerImpl.kt @@ -41,7 +41,6 @@ import com.intellij.openapi.updateSettings.impl.pluginsAdvertisement.UnknownFeat import com.intellij.openapi.util.InvalidDataException import com.intellij.openapi.util.JDOMExternalizableStringList import com.intellij.openapi.util.Key -import com.intellij.openapi.util.Pair import com.intellij.openapi.util.registry.Registry import com.intellij.util.EventDispatcher import com.intellij.util.IconUtil @@ -114,7 +113,14 @@ class RunManagerImpl(internal val project: Project) : RunManagerEx(), Persistent @Suppress("DEPRECATION") private val myOrder = JDOMExternalizableStringList() private val recentlyUsedTemporaries = ArrayList() - private var myOrdered = true + + var isOrdered = true + set(value) { + lock.write { + field = value + immutableSortedSettingsList = null + } + } private val myDispatcher = EventDispatcher.create(RunManagerListener::class.java)!! @@ -212,7 +218,7 @@ class RunManagerImpl(internal val project: Project) : RunManagerEx(), Persistent */ override fun getConfigurationsList(type: ConfigurationType): List { var result: MutableList? = null - for (settings in sortedConfigurations) { + for (settings in allSettings) { val configuration = settings.configuration if (type.id == configuration.type.id) { if (result == null) { @@ -225,16 +231,11 @@ class RunManagerImpl(internal val project: Project) : RunManagerEx(), Persistent } override val allConfigurationsList: List - get() = sortedConfigurations.mapSmart { it.configuration } + get() = allSettings.mapSmart { it.configuration } - override val allSettings: List - get() = sortedConfigurations.toList() + fun getSettings(configuration: RunConfiguration) = allSettings.firstOrNull { it.configuration === configuration } as? RunnerAndConfigurationSettingsImpl - fun getSettings(configuration: RunConfiguration) = sortedConfigurations.firstOrNull { it.configuration === configuration } as? RunnerAndConfigurationSettingsImpl - - override fun getConfigurationSettingsList(type: ConfigurationType) = lock.read { sortedConfigurations.filterSmart { it.type?.id == type.id } } - - fun getConfigurationSettings() = idToSettings.values.toTypedArray() + override fun getConfigurationSettingsList(type: ConfigurationType) = allSettings.filterSmart { it.type?.id == type.id } override fun getStructure(type: ConfigurationType): Map> { val result = LinkedHashMap>() @@ -275,6 +276,8 @@ class RunManagerImpl(internal val project: Project) : RunManagerEx(), Persistent var existingSettings: RunnerAndConfigurationSettings? = null var existingId: String? = null lock.write { + immutableSortedSettingsList = null + existingId = findExistingConfigurationId(settings) existingId?.let { existingSettings = idToSettings.remove(it) @@ -322,25 +325,25 @@ class RunManagerImpl(internal val project: Project) : RunManagerEx(), Persistent fun checkRecentsLimit() { trimUsagesListToLimit() - val removed = SmartList() + var removed: MutableList? = null lock.write { - while (tempConfigurationsList.size > config.recentsLimit) { + while (idToSettings.values.count { it.isTemporary } > config.recentsLimit) { val it = idToSettings.values.iterator() while (it.hasNext()) { val settings = it.next() if (settings.isTemporary && !recentlyUsedTemporaries.contains(settings.configuration)) { - removed.add(settings) + if (removed == null) { + immutableSortedSettingsList = null + removed = SmartList() + } + removed!!.add(settings) it.remove() break } } } } - fireRunConfigurationsRemoved(removed) - } - - fun setOrdered(ordered: Boolean) { - myOrdered = ordered + removed?.let { fireRunConfigurationsRemoved(it) } } fun saveOrder() { @@ -355,12 +358,13 @@ class RunManagerImpl(internal val project: Project) : RunManagerEx(), Persistent myOrder.clear() sorted.mapTo(myOrder) { it.uniqueID} + immutableSortedSettingsList = null } fun setOrder(comparator: Comparator?) { doSaveOrder(comparator) // force recache of configurations list - setOrdered(false) + isOrdered = false } override var selectedConfiguration: RunnerAndConfigurationSettings? @@ -377,75 +381,95 @@ class RunManagerImpl(internal val project: Project) : RunManagerEx(), Persistent private fun setSelectedConfigurationId(id: String?) { selectedConfigurationId = id - if (selectedConfigurationId != null) { + if (id != null) { loadedSelectedConfigurationUniqueName = null } } - override fun getSortedConfigurations(): MutableCollection { - if (myOrdered) { - return idToSettings.values - } + private data class OrderItem(val id: String, val settings: RunnerAndConfigurationSettings) - val order = ArrayList>(idToSettings.size) - val folderNames = SmartList() - for (each in idToSettings.values) { - order.add(Pair.create(each.uniqueID, each)) - val folderName = each.folderName - if (folderName != null && !folderNames.contains(folderName)) { - folderNames.add(folderName) + @Volatile + private var immutableSortedSettingsList: List? = null + + override val allSettings: List + get() { + immutableSortedSettingsList?.let { + return it + } + + lock.write { + immutableSortedSettingsList?.let { + return it + } + + if (isOrdered) { + val result = Collections.unmodifiableList(idToSettings.values.toList()) + immutableSortedSettingsList = result + return result + } + + val order = ArrayList(idToSettings.size) + val folderNames = SmartList() + for (each in idToSettings.values) { + order.add(OrderItem(each.uniqueID, each)) + val folderName = each.folderName + if (folderName != null && !folderNames.contains(folderName)) { + folderNames.add(folderName) + } + } + folderNames.add(null) + idToSettings.clear() + + if (myOrder.isEmpty()) { + // IDEA-63663 Sort run configurations alphabetically if clean checkout + order.sortWith(Comparator { o1, o2 -> + val temporary1 = o1.settings.isTemporary + val temporary2 = o2.settings.isTemporary + when { + temporary1 == temporary2 -> o1.id.compareTo(o2.id) + temporary1 -> 1 + else -> -1 + } + }) + } + else { + order.sortWith(Comparator { o1, o2 -> + val i1 = folderNames.indexOf(o1.settings.folderName) + val i2 = folderNames.indexOf(o2.settings.folderName) + if (i1 != i2) { + return@Comparator i1 - i2 + } + + val temporary1 = o1.settings.isTemporary + val temporary2 = o2.settings.isTemporary + when { + temporary1 == temporary2 -> { + val index1 = myOrder.indexOf(o1.id) + val index2 = myOrder.indexOf(o2.id) + if (index1 == -1 && index2 == -1) { + o1.settings.name.compareTo(o2.settings.name) + } + else { + index1 - index2 + } + } + temporary1 -> 1 + else -> -1 + } + }) + } + + for (each in order) { + val setting = each.settings + idToSettings.put(setting.uniqueID, setting) + } + + val result = Collections.unmodifiableList(idToSettings.values.toList()) + isOrdered = true + immutableSortedSettingsList = result + return result } } - folderNames.add(null) - idToSettings.clear() - - if (myOrder.isEmpty()) { - // IDEA-63663 Sort run configurations alphabetically if clean checkout - order.sortWith(Comparator { o1, o2 -> - val temporary1 = o1.getSecond().isTemporary - val temporary2 = o2.getSecond().isTemporary - when { - temporary1 == temporary2 -> o1.first.compareTo(o2.first) - temporary1 -> 1 - else -> -1 - } - }) - } - else { - order.sortWith(Comparator { o1, o2 -> - val i1 = folderNames.indexOf(o1.getSecond().folderName) - val i2 = folderNames.indexOf(o2.getSecond().folderName) - if (i1 != i2) { - return@Comparator i1 - i2 - } - - val temporary1 = o1.getSecond().isTemporary - val temporary2 = o2.getSecond().isTemporary - when { - temporary1 == temporary2 -> { - val index1 = myOrder.indexOf(o1.first) - val index2 = myOrder.indexOf(o2.first) - if (index1 == -1 && index2 == -1) { - o1.second.name.compareTo(o2.second.name) - } - else { - index1 - index2 - } - } - temporary1 -> 1 - else -> -1 - } - }) - } - - for (each in order) { - val setting = each.second - idToSettings.put(setting.uniqueID, setting) - } - - myOrdered = true - return idToSettings.values - } @Suppress("DEPRECATION") override fun getState(): Element { @@ -502,7 +526,11 @@ class RunManagerImpl(internal val project: Project) : RunManagerEx(), Persistent } fun writeContext(element: Element) { - writeConfigurations(element, lock.read { idToSettings.values.filterSmart { it.isTemporary } }) + for (setting in allSettings) { + if (setting.isTemporary) { + element.addContent((setting as RunnerAndConfigurationSettingsImpl).writeScheme()) + } + } selectedConfiguration?.let { element.setAttribute(SELECTED_ATTR, it.uniqueID) @@ -589,7 +617,7 @@ class RunManagerImpl(internal val project: Project) : RunManagerEx(), Persistent } } } - myOrdered = false + isOrdered = false loadedSelectedConfigurationUniqueName = parentNode.getAttributeValue(SELECTED_ATTR) setSelectedConfigurationId(loadedSelectedConfigurationUniqueName) @@ -648,32 +676,40 @@ class RunManagerImpl(internal val project: Project) : RunManagerEx(), Persistent } private fun clear(allConfigurations: Boolean) { - val configurations: MutableList - if (allConfigurations) { - this.idToSettings.clear() - selectedConfigurationId = null - configurations = ArrayList(this.idToSettings.values) - } - else { - configurations = SmartList() - val iterator = this.idToSettings.values.iterator() - while (iterator.hasNext()) { - val configuration = iterator.next() - if (configuration.isTemporary || !configuration.isShared) { - iterator.remove() + val configurations = lock.write { + immutableSortedSettingsList = null - configurations.add(configuration) - } - } + val configurations = if (allConfigurations) { + val configurations = idToSettings.values.toList() - if (selectedConfigurationId != null && this.idToSettings.containsKey(selectedConfigurationId!!)) { + idToSettings.clear() selectedConfigurationId = null + + configurations } + else { + val configurations = SmartList() + val iterator = idToSettings.values.iterator() + while (iterator.hasNext()) { + val configuration = iterator.next() + if (configuration.isTemporary || !configuration.isShared) { + iterator.remove() + + configurations.add(configuration) + } + } + + if (selectedConfigurationId != null && this.idToSettings.containsKey(selectedConfigurationId!!)) { + selectedConfigurationId = null + } + + configurations + } + + templateIdToConfiguration.clear() + configurations } - lock.write { - templateIdToConfiguration.clear() - } loadedSelectedConfigurationUniqueName = null iconCache.clear() recentlyUsedTemporaries.clear() @@ -763,25 +799,20 @@ class RunManagerImpl(internal val project: Project) : RunManagerEx(), Persistent } fun getSharedConfigurations(): List { - lock.read { - var result: MutableList? = null - for (configuration in idToSettings.values) { - if (configuration.isShared) { - if (result == null) { - result = ArrayList() - } - result.add(configuration) + var result: MutableList? = null + for (configuration in allSettings) { + if (configuration.isShared) { + if (result == null) { + result = ArrayList() } + result.add(configuration) } - return result ?: emptyList() } + return result ?: emptyList() } - internal val configurationSettings: Collection - get() = idToSettings.values - override val tempConfigurationsList: List - get() = idToSettings.values.filter { it.isTemporary } + get() = allSettings.filterSmart { it.isTemporary } override fun makeStable(settings: RunnerAndConfigurationSettings) { settings.isTemporary = false @@ -792,7 +823,7 @@ class RunManagerImpl(internal val project: Project) : RunManagerEx(), Persistent private fun doMakeStable(settings: RunnerAndConfigurationSettings) { recentlyUsedTemporaries.remove(settings.configuration) if (!myOrder.isEmpty()) { - setOrdered(false) + isOrdered = false } } @@ -807,7 +838,7 @@ class RunManagerImpl(internal val project: Project) : RunManagerEx(), Persistent val tasks = SmartList() val checkedTemplates = SmartList() lock.read { - for (settings in idToSettings.values.toTypedArray()) { + for (settings in allSettings) { val configuration = settings.configuration for (task in getBeforeRunTasks(configuration)) { if (task.isEnabled && task.providerId === taskProviderId) { @@ -858,11 +889,11 @@ class RunManagerImpl(internal val project: Project) : RunManagerEx(), Persistent if (name == null) { return null } - return idToSettings.values.firstOrNull { it.name == name } + return allSettings.firstOrNull { it.name == name } } fun findConfigurationByTypeAndName(typeId: String, name: String): RunnerAndConfigurationSettings? { - return sortedConfigurations.firstOrNull { + return allSettings.firstOrNull { val t = it.type t != null && typeId == t.id && name == it.name } @@ -998,7 +1029,7 @@ class RunManagerImpl(internal val project: Project) : RunManagerEx(), Persistent // we must update all existing configuration tasks to ensure that effective tasks (own + template) are the same as before template configuration change // see testTemplates test lock.read { - for (otherSettings in idToSettings.values) { + for (otherSettings in allSettings) { val otherConfiguration = otherSettings.configuration if (otherConfiguration !is WrappingRunConfiguration<*> && otherConfiguration.factory === templateConfiguration.factory) { otherConfiguration.beforeRunTasks = getEffectiveBeforeRunTasks(otherConfiguration, ownIsOnlyEnabled = false, isDisableTemplateTasks = true, newTemplateTasks = tasks) @@ -1028,6 +1059,7 @@ class RunManagerImpl(internal val project: Project) : RunManagerEx(), Persistent val settings = entry.value if (!settings.isTemplate && settings.isShared && !existing.contains(settings.uniqueID)) { if (removed == null) { + immutableSortedSettingsList = null removed = SmartList() } removed!!.add(settings) @@ -1080,6 +1112,8 @@ class RunManagerImpl(internal val project: Project) : RunManagerEx(), Persistent val changedSettings = SmartList() lock.write { + immutableSortedSettingsList = null + val iterator = idToSettings.values.iterator() for (otherSettings in iterator) { if (otherSettings === settings) { diff --git a/platform/testFramework/src/com/intellij/mock/MockRunManager.kt b/platform/testFramework/src/com/intellij/mock/MockRunManager.kt index c4331f987314..12e43bfdadce 100644 --- a/platform/testFramework/src/com/intellij/mock/MockRunManager.kt +++ b/platform/testFramework/src/com/intellij/mock/MockRunManager.kt @@ -107,10 +107,6 @@ class MockRunManager : RunManagerEx() { return null } - override fun getSortedConfigurations(): Collection { - return emptyList() - } - override fun removeConfiguration(settings: RunnerAndConfigurationSettings?) {} override fun addRunManagerListener(listener: RunManagerListener) {} diff --git a/python/ide/src/com/jetbrains/python/PyModuleListener.java b/python/ide/src/com/jetbrains/python/PyModuleListener.java index 2646174ac178..16d76ae65adb 100644 --- a/python/ide/src/com/jetbrains/python/PyModuleListener.java +++ b/python/ide/src/com/jetbrains/python/PyModuleListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,9 +25,6 @@ import com.intellij.util.messages.MessageBus; import com.jetbrains.python.run.AbstractPythonRunConfiguration; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; -import java.util.Collection; - /** * @author yole */ @@ -37,8 +34,7 @@ public class PyModuleListener { @Override public void beforeModuleRemoved(@NotNull Project project, @NotNull Module module) { final RunManagerEx runManager = RunManagerEx.getInstanceEx(project); - final Collection configurations = new ArrayList<>(runManager.getSortedConfigurations()); - for (RunnerAndConfigurationSettings configuration : configurations) { + for (RunnerAndConfigurationSettings configuration : runManager.getAllSettings()) { if (configuration.getConfiguration() instanceof AbstractPythonRunConfiguration) { final Module configModule = ((AbstractPythonRunConfiguration)configuration.getConfiguration()).getModule(); if (configModule == module) {