diff --git a/platform/extensions/src/com/intellij/openapi/extensions/SmartExtensionPoint.java b/platform/extensions/src/com/intellij/openapi/extensions/SmartExtensionPoint.java index 2cc37ffcb41a..f89b751db406 100644 --- a/platform/extensions/src/com/intellij/openapi/extensions/SmartExtensionPoint.java +++ b/platform/extensions/src/com/intellij/openapi/extensions/SmartExtensionPoint.java @@ -14,8 +14,9 @@ import java.util.List; */ public abstract class SmartExtensionPoint { private final Collection myExplicitExtensions; + @SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized") private ExtensionPoint myExtensionPoint; - private List myCache; + private volatile List myCache; protected SmartExtensionPoint(@NotNull final Collection explicitExtensions) { myExplicitExtensions = explicitExtensions; @@ -43,44 +44,61 @@ public abstract class SmartExtensionPoint { @NotNull public final List getExtensions() { + List result = myCache; + if (result != null) { + return result; + } + + // it is ok to call getExtensionPoint several times - call is cheap is thread-safe + ExtensionPoint extensionPoint = myExtensionPoint; + if (extensionPoint == null) { + extensionPoint = getExtensionPoint(); + myExtensionPoint = extensionPoint; + } + + List registeredExtensions = ContainerUtilRt.mapNotNull(extensionPoint.getExtensionList(), this::getExtension); synchronized (myExplicitExtensions) { - List result = myCache; - if (result == null) { - myExtensionPoint = getExtensionPoint(); - // EP will not add duplicated listener, so, it is safe to not care about is already added - myExtensionPoint.addExtensionPointListener(new ExtensionPointAndAreaListener() { - @Override - public void areaReplaced(@NotNull ExtensionsArea oldArea) { - dropCache(); + result = myCache; + if (result != null) { + return result; + } + + // EP will not add duplicated listener, so, it is safe to not care about is already added + extensionPoint.addExtensionPointListener(new ExtensionPointAndAreaListener() { + @Override + public void areaReplaced(@NotNull ExtensionsArea oldArea) { + dropCache(); + } + + @Override + public final void extensionRemoved(@NotNull final Extension extension, @Nullable final PluginDescriptor pluginDescriptor) { + dropCache(); + } + + @Override + public final void extensionAdded(@NotNull final Extension extension, @Nullable final PluginDescriptor pluginDescriptor) { + dropCache(); + } + + private void dropCache() { + if (myCache == null) { + return; } - @Override - public final void extensionRemoved(@NotNull final Extension extension, @Nullable final PluginDescriptor pluginDescriptor) { - dropCache(); - } - - @Override - public final void extensionAdded(@NotNull final Extension extension, @Nullable final PluginDescriptor pluginDescriptor) { - dropCache(); - } - - private void dropCache() { - synchronized (myExplicitExtensions) { - if (myCache != null) { - myCache = null; - myExtensionPoint.removeExtensionPointListener(this); - myExtensionPoint = null; - } + synchronized (myExplicitExtensions) { + if (myCache != null) { + myCache = null; + myExtensionPoint.removeExtensionPointListener(this); + myExtensionPoint = null; } } - }, false, null); + } + }, false, null); - List registeredExtensions = ContainerUtilRt.mapNotNull(myExtensionPoint.getExtensionList(), this::getExtension); - result = new ArrayList<>(myExplicitExtensions.size() + registeredExtensions.size()); - result.addAll(myExplicitExtensions); - result.addAll(registeredExtensions); - myCache = result; - } + result = new ArrayList<>(myExplicitExtensions.size() + registeredExtensions.size()); + result.addAll(myExplicitExtensions); + result.addAll(registeredExtensions); + myCache = result; return result; } }