diff --git a/platform/util/src/com/intellij/util/pico/DefaultPicoContainer.java b/platform/util/src/com/intellij/util/pico/DefaultPicoContainer.java index 842cd469f4a4..c7dc592bb474 100644 --- a/platform/util/src/com/intellij/util/pico/DefaultPicoContainer.java +++ b/platform/util/src/com/intellij/util/pico/DefaultPicoContainer.java @@ -17,6 +17,7 @@ package com.intellij.util.pico; import com.intellij.util.ReflectionCache; import com.intellij.util.containers.ConcurrentHashMap; +import com.intellij.util.containers.FList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.picocontainer.*; @@ -24,7 +25,6 @@ import org.picocontainer.defaults.*; import java.io.Serializable; import java.util.*; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicReference; public class DefaultPicoContainer implements MutablePicoContainer, Serializable { @@ -34,11 +34,11 @@ public class DefaultPicoContainer implements MutablePicoContainer, Serializable private final Set children = new HashSet(); private final Map componentKeyToAdapterCache = new ConcurrentHashMap(); - private final AtomicReference> componentAdapters = new AtomicReference>(new LinkedHashSet()); + private final LinkedHashSetWrapper componentAdapters = new LinkedHashSetWrapper(); // Keeps track of instantiation order. - private final AtomicReference> orderedComponentAdapters = new AtomicReference>(new LinkedHashSet()); + private final LinkedHashSetWrapper orderedComponentAdapters = new LinkedHashSetWrapper(); private final Map classNameToAdapter = new ConcurrentHashMap(); - private final CopyOnWriteArrayList nonAssignableComponentAdapters = new CopyOnWriteArrayList(); + private final AtomicReference> nonAssignableComponentAdapters = new AtomicReference>(FList.emptyList()); public DefaultPicoContainer(@NotNull ComponentAdapterFactory componentAdapterFactory, PicoContainer parent) { this.componentAdapterFactory = componentAdapterFactory; @@ -50,7 +50,7 @@ public class DefaultPicoContainer implements MutablePicoContainer, Serializable } public Collection getComponentAdapters() { - return Collections.unmodifiableCollection(componentAdapters.get()); + return componentAdapters.getImmutableSet(); } public Map getAssignablesCache() { @@ -59,7 +59,7 @@ public class DefaultPicoContainer implements MutablePicoContainer, Serializable public Collection getNonAssignableAdapters() { - return nonAssignableComponentAdapters; + return nonAssignableComponentAdapters.get().getReversedList(); } @Nullable @@ -141,51 +141,34 @@ public class DefaultPicoContainer implements MutablePicoContainer, Serializable classNameToAdapter.put(classKey, componentAdapter); } else { - nonAssignableComponentAdapters.add(componentAdapter); + do { + FList oldList = nonAssignableComponentAdapters.get(); + FList newList = oldList.prepend(componentAdapter); + if (nonAssignableComponentAdapters.compareAndSet(oldList, newList)) { + break; + } + } while (true); } - addElement(componentAdapters, componentAdapter); + componentAdapters.add(componentAdapter); componentKeyToAdapterCache.put(componentKey, componentAdapter); return componentAdapter; } - private static void addElement(AtomicReference> collectionHolder, T element) { - do { - LinkedHashSet oldCollection = collectionHolder.get(); - if (oldCollection.contains(element)) { - return; - } - - LinkedHashSet newCollection = new LinkedHashSet(oldCollection); - newCollection.add(element); - - if (collectionHolder.compareAndSet(oldCollection, newCollection)) break; - } while (true); - } - - private static void removeElement(AtomicReference> collectionHolder, T element) { - do { - LinkedHashSet oldCollection = collectionHolder.get(); - - LinkedHashSet newCollection = new LinkedHashSet(oldCollection); - newCollection.remove(element); - - if (collectionHolder.compareAndSet(oldCollection, newCollection)) break; - } while (true); - } - public ComponentAdapter unregisterComponent(Object componentKey) { ComponentAdapter adapter = componentKeyToAdapterCache.remove(componentKey); - removeElement(componentAdapters, adapter); - removeElement(orderedComponentAdapters, adapter); + componentAdapters.remove(adapter); + orderedComponentAdapters.remove(adapter); return adapter; } private void addOrderedComponentAdapter(ComponentAdapter componentAdapter) { - addElement(orderedComponentAdapters, componentAdapter); + if (!orderedComponentAdapters.contains(componentAdapter)) { + orderedComponentAdapters.add(componentAdapter); + } } public List getComponentInstances() throws PicoException { @@ -198,7 +181,7 @@ public class DefaultPicoContainer implements MutablePicoContainer, Serializable } Map adapterToInstanceMap = new HashMap(); - for (final ComponentAdapter componentAdapter : componentAdapters.get()) { + for (final ComponentAdapter componentAdapter : componentAdapters.getImmutableSet()) { if (ReflectionCache.isAssignable(componentType, componentAdapter.getComponentImplementation())) { Object componentInstance = getInstance(componentAdapter); adapterToInstanceMap.put(componentAdapter, componentInstance); @@ -210,7 +193,7 @@ public class DefaultPicoContainer implements MutablePicoContainer, Serializable } List result = new ArrayList(); - for (ComponentAdapter componentAdapter : orderedComponentAdapters.get()) { + for (ComponentAdapter componentAdapter : orderedComponentAdapters.getImmutableSet()) { final Object componentInstance = adapterToInstanceMap.get(componentAdapter); if (componentInstance != null) { // may be null in the case of the "implicit" adapter @@ -240,7 +223,7 @@ public class DefaultPicoContainer implements MutablePicoContainer, Serializable @Nullable private Object getInstance(ComponentAdapter componentAdapter) { - final boolean isLocal = componentAdapters.get().contains(componentAdapter); + final boolean isLocal = componentAdapters.contains(componentAdapter); if (isLocal) { return getLocalInstance(componentAdapter); @@ -359,4 +342,50 @@ public class DefaultPicoContainer implements MutablePicoContainer, Serializable public PicoContainer getParent() { return parent; } + + private static class LinkedHashSetWrapper { + + private volatile Set immutableSet; + + private final LinkedHashSet synchronizedSet = new LinkedHashSet(); + + private final ConcurrentHashMap concurrentSet = new ConcurrentHashMap(); + + public boolean contains(@Nullable T element) { + return element != null && concurrentSet.containsKey(element); + } + + public void add(@NotNull T element) { + synchronized (synchronizedSet) { + immutableSet = null; + synchronizedSet.add(element); + concurrentSet.put(element, element); + } + } + + public void remove(@Nullable T element) { + if (element == null) return; + synchronized (synchronizedSet) { + immutableSet = null; + synchronizedSet.remove(element); + concurrentSet.remove(element); + } + } + + @NotNull + public Set getImmutableSet() { + Set res = immutableSet; + if (res == null) { + synchronized (synchronizedSet) { + res = immutableSet; + if (res == null) { + res = Collections.unmodifiableSet((Set)synchronizedSet.clone()); + immutableSet = res; + } + } + } + + return res; + } + } } \ No newline at end of file