From 58c86b8e0f3210bb669d659815cc371aa29c1928 Mon Sep 17 00:00:00 2001 From: Sergey Evdokimov Date: Fri, 25 Nov 2011 15:42:59 +0300 Subject: [PATCH] Optimize DefaultPicoContainer for test. --- .../com/intellij/util/containers/FList.java | 45 +++++++- .../util/pico/DefaultPicoContainer.java | 107 +++++++++++------- 2 files changed, 112 insertions(+), 40 deletions(-) diff --git a/platform/util/src/com/intellij/util/containers/FList.java b/platform/util/src/com/intellij/util/containers/FList.java index a13b7d26cb8f..f9f110b91b54 100644 --- a/platform/util/src/com/intellij/util/containers/FList.java +++ b/platform/util/src/com/intellij/util/containers/FList.java @@ -15,7 +15,7 @@ */ package com.intellij.util.containers; -import java.util.AbstractList; +import java.util.*; /** * Immutable list in functional style @@ -28,6 +28,8 @@ public class FList extends AbstractList { private FList myTail; private int mySize; + private List myReversedList; + private FList() { } @@ -79,6 +81,35 @@ public class FList extends AbstractList { return this; } + @Override + public Iterator iterator() { + return new Iterator() { + + private FList list = FList.this; + + @Override + public boolean hasNext() { + return list.size() > 0; + } + + @Override + public E next() { + if (list.size() == 0) throw new NoSuchElementException(); + + E res = list.myHead; + list = list.getTail(); + assert list != null; + + return res; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + public FList getTail() { return myTail; } @@ -88,6 +119,18 @@ public class FList extends AbstractList { return mySize; } + public List getReversedList() { + List res = myReversedList; + if (res == null) { + res = new ArrayList(this); + Collections.reverse(res); + + myReversedList = res; + } + + return res; + } + public static FList emptyList() { return (FList)EMPTY_LIST; } diff --git a/platform/util/src/com/intellij/util/pico/DefaultPicoContainer.java b/platform/util/src/com/intellij/util/pico/DefaultPicoContainer.java index 842cd469f4a4..08a8c47ac977 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