IDEA-208163 call getExtensionList not under our lock

This commit is contained in:
Vladimir Krivosheev
2019-02-28 19:18:39 +01:00
parent 7bb8fd224b
commit d0ca1bf26e
@@ -14,8 +14,9 @@ import java.util.List;
*/
public abstract class SmartExtensionPoint<Extension, V> {
private final Collection<V> myExplicitExtensions;
@SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized")
private ExtensionPoint<Extension> myExtensionPoint;
private List<V> myCache;
private volatile List<V> myCache;
protected SmartExtensionPoint(@NotNull final Collection<V> explicitExtensions) {
myExplicitExtensions = explicitExtensions;
@@ -43,44 +44,61 @@ public abstract class SmartExtensionPoint<Extension, V> {
@NotNull
public final List<V> getExtensions() {
List<V> result = myCache;
if (result != null) {
return result;
}
// it is ok to call getExtensionPoint several times - call is cheap is thread-safe
ExtensionPoint<Extension> extensionPoint = myExtensionPoint;
if (extensionPoint == null) {
extensionPoint = getExtensionPoint();
myExtensionPoint = extensionPoint;
}
List<V> registeredExtensions = ContainerUtilRt.mapNotNull(extensionPoint.getExtensionList(), this::getExtension);
synchronized (myExplicitExtensions) {
List<V> 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<Extension>() {
@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<Extension>() {
@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<V> 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;
}
}