Skip to content

Commit 905aeb7

Browse files
committed
Trigger R8's ServiceLoader optimization
This simplifies R8 Full Mode's configuration when paired with R8 optimizations (which is required in AGP 9), as when the optimization is triggered Full Mode will automatically keep the constructor for the relevant classes. android-interop-test doesn't currently enable R8 optimizations, so it doesn't actually demonstrate the benefit, but I have manually confirmed that enabling proguard-android-optimize.txt does cause the ServiceLoader optimization in android-interop-test. Note that full mode is a separate configuration and not necessary to get the ServiceLoader optimization.
1 parent bc1f146 commit 905aeb7

File tree

8 files changed

+87
-26
lines changed

8 files changed

+87
-26
lines changed

api/src/main/java/io/grpc/InternalServiceProviders.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
package io.grpc;
1818

1919
import com.google.common.annotations.VisibleForTesting;
20+
import java.util.Iterator;
2021
import java.util.List;
22+
import java.util.ServiceLoader;
2123

2224
@Internal
2325
public final class InternalServiceProviders {
@@ -27,12 +29,28 @@ private InternalServiceProviders() {
2729
/**
2830
* Accessor for method.
2931
*/
32+
@Deprecated
3033
public static <T> List<T> loadAll(
3134
Class<T> klass,
3235
Iterable<Class<?>> hardCodedClasses,
3336
ClassLoader classLoader,
3437
PriorityAccessor<T> priorityAccessor) {
35-
return ServiceProviders.loadAll(klass, hardCodedClasses, classLoader, priorityAccessor);
38+
return loadAll(
39+
klass,
40+
ServiceLoader.load(klass, classLoader).iterator(),
41+
hardCodedClasses,
42+
priorityAccessor);
43+
}
44+
45+
/**
46+
* Accessor for method.
47+
*/
48+
public static <T> List<T> loadAll(
49+
Class<T> klass,
50+
Iterator<T> serviceLoader,
51+
Iterable<Class<?>> hardCodedClasses,
52+
PriorityAccessor<T> priorityAccessor) {
53+
return ServiceProviders.loadAll(klass, serviceLoader, hardCodedClasses, priorityAccessor);
3654
}
3755

3856
/**

api/src/main/java/io/grpc/LoadBalancerRegistry.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.LinkedHashSet;
2727
import java.util.List;
2828
import java.util.Map;
29+
import java.util.ServiceLoader;
2930
import java.util.logging.Level;
3031
import java.util.logging.Logger;
3132
import javax.annotation.Nullable;
@@ -101,8 +102,10 @@ public static synchronized LoadBalancerRegistry getDefaultRegistry() {
101102
if (instance == null) {
102103
List<LoadBalancerProvider> providerList = ServiceProviders.loadAll(
103104
LoadBalancerProvider.class,
105+
ServiceLoader
106+
.load(LoadBalancerProvider.class, LoadBalancerProvider.class.getClassLoader())
107+
.iterator(),
104108
HARDCODED_CLASSES,
105-
LoadBalancerProvider.class.getClassLoader(),
106109
new LoadBalancerPriorityAccessor());
107110
instance = new LoadBalancerRegistry();
108111
for (LoadBalancerProvider provider : providerList) {

api/src/main/java/io/grpc/ManagedChannelRegistry.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.Comparator;
3030
import java.util.LinkedHashSet;
3131
import java.util.List;
32+
import java.util.ServiceLoader;
3233
import java.util.logging.Level;
3334
import java.util.logging.Logger;
3435
import javax.annotation.concurrent.ThreadSafe;
@@ -100,8 +101,10 @@ public static synchronized ManagedChannelRegistry getDefaultRegistry() {
100101
if (instance == null) {
101102
List<ManagedChannelProvider> providerList = ServiceProviders.loadAll(
102103
ManagedChannelProvider.class,
104+
ServiceLoader
105+
.load(ManagedChannelProvider.class, ManagedChannelProvider.class.getClassLoader())
106+
.iterator(),
103107
getHardCodedClasses(),
104-
ManagedChannelProvider.class.getClassLoader(),
105108
new ManagedChannelPriorityAccessor());
106109
instance = new ManagedChannelRegistry();
107110
for (ManagedChannelProvider provider : providerList) {

api/src/main/java/io/grpc/NameResolverRegistry.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.List;
3030
import java.util.Locale;
3131
import java.util.Map;
32+
import java.util.ServiceLoader;
3233
import java.util.logging.Level;
3334
import java.util.logging.Logger;
3435
import javax.annotation.Nullable;
@@ -125,8 +126,10 @@ public static synchronized NameResolverRegistry getDefaultRegistry() {
125126
if (instance == null) {
126127
List<NameResolverProvider> providerList = ServiceProviders.loadAll(
127128
NameResolverProvider.class,
129+
ServiceLoader
130+
.load(NameResolverProvider.class, NameResolverProvider.class.getClassLoader())
131+
.iterator(),
128132
getHardCodedClasses(),
129-
NameResolverProvider.class.getClassLoader(),
130133
new NameResolverPriorityAccessor());
131134
if (providerList.isEmpty()) {
132135
logger.warning("No NameResolverProviders found via ServiceLoader, including for DNS. This "

api/src/main/java/io/grpc/ServerRegistry.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.Comparator;
2525
import java.util.LinkedHashSet;
2626
import java.util.List;
27+
import java.util.ServiceLoader;
2728
import java.util.logging.Level;
2829
import java.util.logging.Logger;
2930
import javax.annotation.concurrent.ThreadSafe;
@@ -93,8 +94,9 @@ public static synchronized ServerRegistry getDefaultRegistry() {
9394
if (instance == null) {
9495
List<ServerProvider> providerList = ServiceProviders.loadAll(
9596
ServerProvider.class,
97+
ServiceLoader.load(ServerProvider.class, ServerProvider.class.getClassLoader())
98+
.iterator(),
9699
getHardCodedClasses(),
97-
ServerProvider.class.getClassLoader(),
98100
new ServerPriorityAccessor());
99101
instance = new ServerRegistry();
100102
for (ServerProvider provider : providerList) {

api/src/main/java/io/grpc/ServiceProviders.java

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
import java.util.ArrayList;
2121
import java.util.Collections;
2222
import java.util.Comparator;
23+
import java.util.Iterator;
2324
import java.util.List;
25+
import java.util.ListIterator;
2426
import java.util.ServiceConfigurationError;
2527
import java.util.ServiceLoader;
2628

@@ -34,20 +36,39 @@ private ServiceProviders() {
3436
* {@link ServiceLoader}.
3537
* If this is Android, returns all available implementations in {@code hardcoded}.
3638
* The list is sorted in descending priority order.
39+
*
40+
* <p>{@code serviceLoader} should be created with {@code ServiceLoader.load(MyClass.class,
41+
* MyClass.class.getClassLoader()).iterator()} in order to be detected by R8 so that R8 full mode
42+
* will keep the constructors for the provider classes.
3743
*/
3844
public static <T> List<T> loadAll(
3945
Class<T> klass,
46+
Iterator<T> serviceLoader,
4047
Iterable<Class<?>> hardcoded,
41-
ClassLoader cl,
4248
final PriorityAccessor<T> priorityAccessor) {
43-
Iterable<T> candidates;
44-
if (isAndroid(cl)) {
45-
candidates = getCandidatesViaHardCoded(klass, hardcoded);
49+
Iterator<T> candidates;
50+
if (serviceLoader instanceof ListIterator) {
51+
// A rewriting tool has replaced the ServiceLoader with a List of some sort (R8 uses
52+
// ArrayList, AppReduce uses singletonList). We prefer to use such iterators on Android as
53+
// they won't need reflection like the hard-coded list does. In addition, the provider
54+
// instances will have already been created, so it seems we should use them.
55+
//
56+
// R8: https://r8.googlesource.com/r8/+/490bc53d9310d4cc2a5084c05df4aadaec8c885d/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
57+
// AppReduce: service_loader_pass.cc
58+
candidates = serviceLoader;
59+
} else if (isAndroid(klass.getClassLoader())) {
60+
// Avoid getResource() on Android, which must read from a zip which uses a lot of memory
61+
candidates = getCandidatesViaHardCoded(klass, hardcoded).iterator();
62+
} else if (!serviceLoader.hasNext()) {
63+
// Attempt to load using the context class loader and ServiceLoader.
64+
// This allows frameworks like http://aries.apache.org/modules/spi-fly.html to plug in.
65+
candidates = ServiceLoader.load(klass).iterator();
4666
} else {
47-
candidates = getCandidatesViaServiceLoader(klass, cl);
67+
candidates = serviceLoader;
4868
}
4969
List<T> list = new ArrayList<>();
50-
for (T current: candidates) {
70+
while (candidates.hasNext()) {
71+
T current = candidates.next();
5172
if (!priorityAccessor.isAvailable(current)) {
5273
continue;
5374
}
@@ -84,15 +105,14 @@ static boolean isAndroid(ClassLoader cl) {
84105
}
85106

86107
/**
87-
* Loads service providers for the {@code klass} service using {@link ServiceLoader}.
108+
* For testing only: Loads service providers for the {@code klass} service using {@link
109+
* ServiceLoader}. Does not support spi-fly and related tricks.
88110
*/
89111
@VisibleForTesting
90112
public static <T> Iterable<T> getCandidatesViaServiceLoader(Class<T> klass, ClassLoader cl) {
91113
Iterable<T> i = ServiceLoader.load(klass, cl);
92-
// Attempt to load using the context class loader and ServiceLoader.
93-
// This allows frameworks like http://aries.apache.org/modules/spi-fly.html to plug in.
94114
if (!i.iterator().hasNext()) {
95-
i = ServiceLoader.load(klass);
115+
return null;
96116
}
97117
return i;
98118
}

api/src/test/java/io/grpc/ServiceProvidersTest.java

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.Iterator;
3030
import java.util.List;
3131
import java.util.ServiceConfigurationError;
32+
import java.util.ServiceLoader;
3233
import org.junit.Test;
3334
import org.junit.runner.RunWith;
3435
import org.junit.runners.JUnit4;
@@ -98,7 +99,7 @@ public void multipleProvider() throws Exception {
9899
Available7Provider.class,
99100
load(ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR).getClass());
100101

101-
List<ServiceProvidersTestAbstractProvider> providers = ServiceProviders.loadAll(
102+
List<ServiceProvidersTestAbstractProvider> providers = loadAll(
102103
ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR);
103104
assertEquals(3, providers.size());
104105
assertEquals(Available7Provider.class, providers.get(0).getClass());
@@ -121,8 +122,7 @@ public void unknownClassProvider() {
121122
ClassLoader cl = new ReplacingClassLoader(getClass().getClassLoader(), serviceFile,
122123
"io/grpc/ServiceProvidersTestAbstractProvider-unknownClassProvider.txt");
123124
try {
124-
ServiceProviders.loadAll(
125-
ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR);
125+
loadAll(ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR);
126126
fail("Exception expected");
127127
} catch (ServiceConfigurationError e) {
128128
// noop
@@ -136,8 +136,7 @@ public void exceptionSurfacedToCaller_failAtInit() {
136136
try {
137137
// Even though there is a working provider, if any providers fail then we should fail
138138
// completely to avoid returning something unexpected.
139-
ServiceProviders.loadAll(
140-
ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR);
139+
loadAll(ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR);
141140
fail("Expected exception");
142141
} catch (ServiceConfigurationError expected) {
143142
// noop
@@ -150,8 +149,7 @@ public void exceptionSurfacedToCaller_failAtPriority() {
150149
"io/grpc/ServiceProvidersTestAbstractProvider-failAtPriorityProvider.txt");
151150
try {
152151
// The exception should be surfaced to the caller
153-
ServiceProviders.loadAll(
154-
ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR);
152+
loadAll(ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR);
155153
fail("Expected exception");
156154
} catch (FailAtPriorityProvider.PriorityException expected) {
157155
// noop
@@ -164,8 +162,7 @@ public void exceptionSurfacedToCaller_failAtAvailable() {
164162
"io/grpc/ServiceProvidersTestAbstractProvider-failAtAvailableProvider.txt");
165163
try {
166164
// The exception should be surfaced to the caller
167-
ServiceProviders.loadAll(
168-
ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR);
165+
loadAll(ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR);
169166
fail("Expected exception");
170167
} catch (FailAtAvailableProvider.AvailableException expected) {
171168
// noop
@@ -245,13 +242,25 @@ private static <T> T load(
245242
Iterable<Class<?>> hardcoded,
246243
ClassLoader cl,
247244
PriorityAccessor<T> priorityAccessor) {
248-
List<T> candidates = ServiceProviders.loadAll(klass, hardcoded, cl, priorityAccessor);
245+
List<T> candidates = loadAll(klass, hardcoded, cl, priorityAccessor);
249246
if (candidates.isEmpty()) {
250247
return null;
251248
}
252249
return candidates.get(0);
253250
}
254251

252+
private static <T> List<T> loadAll(
253+
Class<T> klass,
254+
Iterable<Class<?>> hardcoded,
255+
ClassLoader classLoader,
256+
PriorityAccessor<T> priorityAccessor) {
257+
return ServiceProviders.loadAll(
258+
klass,
259+
ServiceLoader.load(klass, classLoader).iterator(),
260+
hardcoded,
261+
priorityAccessor);
262+
}
263+
255264
private static class BaseProvider extends ServiceProvidersTestAbstractProvider {
256265
private final boolean isAvailable;
257266
private final int priority;

xds/src/main/java/io/grpc/xds/XdsCredentialsRegistry.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.LinkedHashSet;
3030
import java.util.List;
3131
import java.util.Map;
32+
import java.util.ServiceLoader;
3233
import java.util.logging.Level;
3334
import java.util.logging.Logger;
3435
import javax.annotation.Nullable;
@@ -109,8 +110,10 @@ public static synchronized XdsCredentialsRegistry getDefaultRegistry() {
109110
if (instance == null) {
110111
List<XdsCredentialsProvider> providerList = InternalServiceProviders.loadAll(
111112
XdsCredentialsProvider.class,
113+
ServiceLoader
114+
.load(XdsCredentialsProvider.class, XdsCredentialsProvider.class.getClassLoader())
115+
.iterator(),
112116
getHardCodedClasses(),
113-
XdsCredentialsProvider.class.getClassLoader(),
114117
new XdsCredentialsProviderPriorityAccessor());
115118
if (providerList.isEmpty()) {
116119
logger.warning("No XdsCredsRegistry found via ServiceLoader, including for GoogleDefault, "

0 commit comments

Comments
 (0)