Skip to content

Commit

Permalink
Convert RealProviderMultimapProvider to use InternalFactory interfaces.
Browse files Browse the repository at this point in the history
This is a performance improvement CL. The goal is to avoid overhead of
ProviderInstanceBinding + ProviderLookups, and instead use the
InternalFactory interfaces directly. This will be one in a series of
CLs to migrate all of RealMapBinder.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=131848624
  • Loading branch information
paulcreates authored and sameb committed Sep 7, 2016
1 parent 713c06c commit 0b8e8ef
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 42 deletions.
147 changes: 105 additions & 42 deletions core/src/com/google/inject/internal/RealMapBinder.java
Expand Up @@ -29,6 +29,7 @@
import com.google.inject.TypeLiteral;
import com.google.inject.binder.LinkedBindingBuilder;
import com.google.inject.internal.Indexer.IndexedBinding;
import com.google.inject.internal.InternalProviderInstanceBindingImpl.InitializationTiming;
import com.google.inject.multibindings.MapBinderBinding;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.multibindings.MultibindingsTargetVisitor;
Expand Down Expand Up @@ -251,7 +252,8 @@ void updateDuplicateKeyMessage(K k, String errMsg) {
public void permitDuplicates() {
entrySetBinder.permitDuplicates();
binder.install(new MultimapBinder<K, V>(
multimapKey, providerSetMultimapKey, javaxProviderSetMultimapKey, providerCollectionMultimapKey, javaxProviderCollectionMultimapKey, entrySetBinder.getSetKey()));
multimapKey, providerSetMultimapKey, javaxProviderSetMultimapKey,
providerCollectionMultimapKey, javaxProviderCollectionMultimapKey, entrySetBinder));
}

/**
Expand Down Expand Up @@ -532,51 +534,54 @@ static final class MultimapBinder<K, V> implements Module {
private final Key<Map<K, Set<V>>> multimapKey;
private final Key<Map<K, Set<Provider<V>>>> providerMultimapKey;
private final Key<Map<K, Set<javax.inject.Provider<V>>>> javaxProviderMultimapKey;
private final Key<Set<Entry<K,Provider<V>>>> entrySetKey;
private final Key<Map<K, Collection<javax.inject.Provider<V>>>> javaxProviderCollectionMultimapKey;
private final Key<Map<K, Collection<Provider<V>>>> providerCollectionMultimapKey;
private final RealMultibinder<Map.Entry<K, Provider<V>>> entrySetBinder;

public MultimapBinder(
Key<Map<K, Set<V>>> multimapKey,
Key<Map<K, Set<Provider<V>>>> providerSetMultimapKey,
Key<Map<K, Set<javax.inject.Provider<V>>>> javaxProviderSetMultimapKey,
Key<Map<K, Collection<Provider<V>>>> providerCollectionMultimapKey,
Key<Map<K, Collection<javax.inject.Provider<V>>>> javaxProviderCollectionMultimapKey,
Key<Set<Entry<K,Provider<V>>>> entrySetKey) {
RealMultibinder<Map.Entry<K, Provider<V>>> entrySetBinder) {
this.multimapKey = multimapKey;
this.providerMultimapKey = providerSetMultimapKey;
this.javaxProviderMultimapKey = javaxProviderSetMultimapKey;
this.providerCollectionMultimapKey = providerCollectionMultimapKey;
this.javaxProviderCollectionMultimapKey = javaxProviderCollectionMultimapKey;
this.entrySetKey = entrySetKey;
this.entrySetBinder = entrySetBinder;
}

@Override public void configure(Binder binder) {
ImmutableSet<Dependency<?>> dependencies
= ImmutableSet.<Dependency<?>>of(Dependency.get(entrySetKey));

Provider<Set<Entry<K, Provider<V>>>> entrySetProvider =
binder.getProvider(entrySetKey);
// Binds a Map<K, Set<Provider<V>>> from a collection of Map<Entry<K, Provider<V>> if
// permitDuplicates was called.
binder.bind(providerMultimapKey).toProvider(
new RealProviderMultimapProvider(dependencies, entrySetProvider));
= ImmutableSet.<Dependency<?>>of(Dependency.get(entrySetBinder.getSetKey()));

// Binds a Map<K, Set<Provider<V>>>
Provider<Map<K, Set<Provider<V>>>> multimapProvider =
binder.getProvider(providerMultimapKey);
new RealProviderMultimapProvider<K, V>(entrySetBinder, multimapKey);
binder.bind(providerMultimapKey).toProvider(multimapProvider);

// Provide links from a few different public keys to the providerMultimapKey.
// The collection this exposes is internally an ImmutableMap, so it's OK to massage
// the guice Provider to javax Provider in the value (since the guice Provider implements
// javax Provider).
@SuppressWarnings("unchecked")
Provider<Map<K, Set<javax.inject.Provider<V>>>> javaxProvider = (Provider) multimapProvider;
binder.bind(javaxProviderMultimapKey).toProvider(javaxProvider);

@SuppressWarnings("unchecked")
Provider<Map<K, Collection<Provider<V>>>> collectionProvider = (Provider) multimapProvider;
binder.bind(providerCollectionMultimapKey).toProvider(collectionProvider);

@SuppressWarnings("unchecked")
Provider<Map<K, Collection<javax.inject.Provider<V>>>> collectionJavaxProvider =
(Provider) multimapProvider;
binder.bind(javaxProviderCollectionMultimapKey).toProvider(collectionJavaxProvider);

// Binds a Map<K, Set<V>>
binder.bind(multimapKey).toProvider(
new RealMultimapProvider(dependencies, multimapProvider));

linkKeys(binder);
}

// Provide links from a few different public keys to the providerMultimapKey. In each case
// this is safe because the Map and Set instances are unmodifiable by consumers.
@SuppressWarnings("unchecked")
private void linkKeys(Binder binder) {
binder.bind(javaxProviderMultimapKey).to((Key) providerMultimapKey);
binder.bind(javaxProviderCollectionMultimapKey).to((Key) providerMultimapKey);
binder.bind(providerCollectionMultimapKey).to((Key) providerMultimapKey);
}

@Override public int hashCode() {
Expand All @@ -588,47 +593,105 @@ private void linkKeys(Binder binder) {
&& ((MultimapBinder<?, ?>) o).multimapKey.equals(multimapKey);
}

final class RealProviderMultimapProvider
extends RealMapBinderProviderWithDependencies<Map<K, Set<Provider<V>>>> {
private final ImmutableSet<Dependency<?>> dependencies;
private final Provider<Set<Entry<K, Provider<V>>>> entrySetProvider;
private Map<K, Set<Provider<V>>> providerMultimap;
private static final class RealProviderMultimapProvider<K, V>
extends InternalProviderInstanceBindingImpl.Factory<Map<K, Set<Provider<V>>>> {
// prior to initialization we declare just a dependency on the injector, but as soon as we are
// initialized we swap to dependencies on the elements.
private static final ImmutableSet<Dependency<?>> MODULE_DEPENDENCIES =
ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class)));

private RealProviderMultimapProvider(ImmutableSet<Dependency<?>> dependencies,
Provider<Set<Entry<K, Provider<V>>>> entrySetProvider) {
super(multimapKey);
this.dependencies = dependencies;
this.entrySetProvider = entrySetProvider;
// Starts out as Injector and gets set up properly after initialization
private ImmutableSet<Dependency<?>> dependencies = MODULE_DEPENDENCIES;
private Map<K, Set<Provider<V>>> providerMultimap;

private final RealMultibinder<Map.Entry<K, Provider<V>>> entrySetBinder;
private final Key<Map<K, Set<V>>> multimapKey;

RealProviderMultimapProvider(
RealMultibinder<Map.Entry<K, Provider<V>>> entrySetBinder,
Key<Map<K, Set<V>>> multimapKey) {
// While MapBinders only depend on bindings created in modules so we could theoretically
// initialize eagerly, they also depend on
// 1. findBindingsByType returning results
// 2. being able to call BindingImpl.acceptTargetVisitor
// neither of those is available during eager initialization, so we use DELAYED
super(InitializationTiming.DELAYED);

this.entrySetBinder = entrySetBinder;
this.multimapKey = multimapKey;
}

@SuppressWarnings("unused")
@Inject void initialize(Injector injector) {
@Override void initialize(InjectorImpl injector, Errors errors) {
// We now build the Map<K, Set<Provider<V>>> from the entrySetBinder.
// The entrySetBinder contains all of the ProviderMapEntrys, and once
// we have those, it's easy to iterate through them to organize them by K.
Map<K, ImmutableSet.Builder<Provider<V>>> providerMultimapMutable =
new LinkedHashMap<K, ImmutableSet.Builder<Provider<V>>>();
for (Entry<K, Provider<V>> entry : entrySetProvider.get()) {
if (!providerMultimapMutable.containsKey(entry.getKey())) {
providerMultimapMutable.put(
entry.getKey(), ImmutableSet.<Provider<V>>builder());
List<Dependency<?>> dependencies = Lists.newArrayList();
Set<Indexer.IndexedBinding> index = Sets.newHashSet();
Indexer indexer = new Indexer(injector);
TypeLiteral<?> elementType = entrySetBinder.getElementTypeLiteral();
// We get all of the Bindings that were put into the entrySetBinder
for (Binding<?> binding : injector.findBindingsByType(elementType)) {
if (entrySetBinder.containsElement(binding)) {
@SuppressWarnings("unchecked") // protected by findBindingsByType()
ProviderInstanceBinding<ProviderMapEntry<K, V>> entryBinding =
(ProviderInstanceBinding<ProviderMapEntry<K, V>>) binding;
// Use the indexer to de-dupe user bindings. This is needed because of the
// uniqueId in RealElement. The uniqueId intentionally circumvents the regular
// Guice deduplication, so we need to re-implement our own here, ignoring
// uniqueId.
if (index.add(entryBinding.acceptTargetVisitor(indexer))) {
@SuppressWarnings("unchecked")
// We added all these bindings initially, so we know they are ProviderMapEntrys
ProviderMapEntry<K, V> entry =
(ProviderMapEntry) entryBinding.getUserSuppliedProvider();

// Create a set builder for this key if it's the first time we've seen it
if (!providerMultimapMutable.containsKey(entry.getKey())) {
providerMultimapMutable.put(
entry.getKey(), ImmutableSet.<Provider<V>>builder());
}
// Add a dependency for this element
dependencies.add(Dependency.get(entryBinding.getKey()));

// Add the Provider<V>
providerMultimapMutable.get(entry.getKey()).add(entry.getValue());
}
}
providerMultimapMutable.get(entry.getKey()).add(entry.getValue());
}

// Build all of the ImmutableSet.Builders,
// transforming from Map<K, ImmutableSet.Builder<Provider<V>>> to
// ImmutableMap<K, Set<Provider<V>>>
ImmutableMap.Builder<K, Set<Provider<V>>> providerMultimapBuilder =
ImmutableMap.builder();
for (Entry<K, ImmutableSet.Builder<Provider<V>>> entry
: providerMultimapMutable.entrySet()) {
providerMultimapBuilder.put(entry.getKey(), entry.getValue().build());
}
providerMultimap = providerMultimapBuilder.build();

this.dependencies = ImmutableSet.copyOf(dependencies);
}

@Override public Map<K, Set<Provider<V>>> get() {
@Override protected Map<K, Set<Provider<V>>> doProvision(
Errors errors, InternalContext context, Dependency<?> dependency) {
return providerMultimap;
}

@Override public Set<Dependency<?>> getDependencies() {
return dependencies;
}

@Override public boolean equals(Object obj) {
return obj != null && this.getClass() == obj.getClass()
&& multimapKey.equals(((RealProviderMultimapProvider<?, ?>) obj).multimapKey);
}

@Override public int hashCode() {
return multimapKey.hashCode();
}
}

final class RealMultimapProvider
Expand Down
4 changes: 4 additions & 0 deletions core/src/com/google/inject/internal/RealMultibinder.java
Expand Up @@ -139,6 +139,10 @@ Key<Set<T>> getSetKey() {
return bindingSelection.getSetKey();
}

public TypeLiteral<?> getElementTypeLiteral() {
return bindingSelection.getElementTypeLiteral();
}

String getSetName() {
return bindingSelection.getSetName();
}
Expand Down

0 comments on commit 0b8e8ef

Please sign in to comment.