Skip to content

Commit 5d8665f

Browse files
Enable R8 optimization of Dispatchers.Main loading
The developer still has to disable the FastServiceLoader manually if they're using R8. R8 is then able to optimize away the ServiceLoader and reflection entirely, resulting in direct class instantiation and no extra I/O on calling thread. Fixes Kotlin#1231
1 parent 15ee8a3 commit 5d8665f

File tree

3 files changed

+28
-15
lines changed

3 files changed

+28
-15
lines changed

kotlinx-coroutines-core/jvm/src/CoroutineExceptionHandlerImpl.kt

+8-3
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,15 @@ import kotlin.coroutines.*
1313
* Note that Android may have dummy [Thread.contextClassLoader] which is used by one-argument [ServiceLoader.load] function,
1414
* see (https://stackoverflow.com/questions/13407006/android-class-loader-may-fail-for-processes-that-host-multiple-applications).
1515
* So here we explicitly use two-argument `load` with a class-loader of [CoroutineExceptionHandler] class.
16+
*
17+
* We are explicitly using the `ServiceLoader.load(MyClass::class.java, MyClass::class.java.classLoader).iterator()`
18+
* form of the ServiceLoader call to enable R8 optimization when compiled on Android.
1619
*/
17-
private val handlers: List<CoroutineExceptionHandler> = CoroutineExceptionHandler::class.java.let { serviceClass ->
18-
ServiceLoader.load(serviceClass, serviceClass.classLoader).toList()
19-
}
20+
private val handlers: List<CoroutineExceptionHandler> = ServiceLoader.load(
21+
CoroutineExceptionHandler::class.java,
22+
CoroutineExceptionHandler::class.java.classLoader
23+
).iterator().asSequence().toList()
24+
2025

2126
internal actual fun handleCoroutineExceptionImpl(context: CoroutineContext, exception: Throwable) {
2227
// use additional extension handlers

kotlinx-coroutines-core/jvm/src/internal/FastServiceLoader.kt

-10
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@ import java.util.*
66
import java.util.jar.*
77
import java.util.zip.*
88

9-
/**
10-
* Name of the boolean property that enables using of [FastServiceLoader].
11-
*/
12-
private const val FAST_SERVICE_LOADER_PROPERTY_NAME = "kotlinx.coroutines.fast.service.loader"
13-
149
/**
1510
* A simplified version of [ServiceLoader].
1611
* FastServiceLoader locates and instantiates all service providers named in configuration
@@ -25,12 +20,7 @@ private const val FAST_SERVICE_LOADER_PROPERTY_NAME = "kotlinx.coroutines.fast.s
2520
internal object FastServiceLoader {
2621
private const val PREFIX: String = "META-INF/services/"
2722

28-
private val FAST_SERVICE_LOADER_ENABLED = systemProp(FAST_SERVICE_LOADER_PROPERTY_NAME, true)
29-
3023
internal fun <S> load(service: Class<S>, loader: ClassLoader): List<S> {
31-
if (!FAST_SERVICE_LOADER_ENABLED) {
32-
return ServiceLoader.load(service, loader).toList()
33-
}
3424
return try {
3525
loadProviders(service, loader)
3626
} catch (e: Throwable) {

kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt

+20-2
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,33 @@ import kotlinx.coroutines.*
44
import java.util.*
55
import kotlin.coroutines.*
66

7+
/**
8+
* Name of the boolean property that enables using of [FastServiceLoader].
9+
*/
10+
private const val FAST_SERVICE_LOADER_PROPERTY_NAME = "kotlinx.coroutines.fast.service.loader"
11+
712
// Lazy loader for the main dispatcher
813
internal object MainDispatcherLoader {
14+
15+
private val FAST_SERVICE_LOADER_ENABLED = systemProp(FAST_SERVICE_LOADER_PROPERTY_NAME, true)
16+
917
@JvmField
1018
val dispatcher: MainCoroutineDispatcher = loadMainDispatcher()
1119

1220
private fun loadMainDispatcher(): MainCoroutineDispatcher {
1321
return try {
14-
val factories = MainDispatcherFactory::class.java.let { clz ->
15-
FastServiceLoader.load(clz, clz.classLoader)
22+
val factories = if (FAST_SERVICE_LOADER_ENABLED) {
23+
MainDispatcherFactory::class.java.let { clz ->
24+
FastServiceLoader.load(clz, clz.classLoader)
25+
}
26+
} else {
27+
//We are explicitly using the
28+
//`ServiceLoader.load(MyClass::class.java, MyClass::class.java.classLoader).iterator()`
29+
//form of the ServiceLoader call to enable R8 optimization when compiled on Android.
30+
ServiceLoader.load(
31+
MainDispatcherFactory::class.java,
32+
MainDispatcherFactory::class.java.classLoader
33+
).iterator().asSequence().toList()
1634
}
1735
factories.maxBy { it.loadPriority }?.tryCreateDispatcher(factories)
1836
?: MissingMainCoroutineDispatcher(null)

0 commit comments

Comments
 (0)