From 5d8665f7f4711958e5841e96f70e62f9d88345f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojtek=20Kalici=C5=84ski?= Date: Thu, 30 May 2019 14:17:51 +0200 Subject: [PATCH] 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 #1231 --- .../jvm/src/CoroutineExceptionHandlerImpl.kt | 11 +++++++--- .../jvm/src/internal/FastServiceLoader.kt | 10 --------- .../jvm/src/internal/MainDispatchers.kt | 22 +++++++++++++++++-- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/kotlinx-coroutines-core/jvm/src/CoroutineExceptionHandlerImpl.kt b/kotlinx-coroutines-core/jvm/src/CoroutineExceptionHandlerImpl.kt index cabb8a4cc1..90c6a84980 100644 --- a/kotlinx-coroutines-core/jvm/src/CoroutineExceptionHandlerImpl.kt +++ b/kotlinx-coroutines-core/jvm/src/CoroutineExceptionHandlerImpl.kt @@ -13,10 +13,15 @@ import kotlin.coroutines.* * Note that Android may have dummy [Thread.contextClassLoader] which is used by one-argument [ServiceLoader.load] function, * see (https://stackoverflow.com/questions/13407006/android-class-loader-may-fail-for-processes-that-host-multiple-applications). * So here we explicitly use two-argument `load` with a class-loader of [CoroutineExceptionHandler] class. + * + * We are explicitly using the `ServiceLoader.load(MyClass::class.java, MyClass::class.java.classLoader).iterator()` + * form of the ServiceLoader call to enable R8 optimization when compiled on Android. */ -private val handlers: List = CoroutineExceptionHandler::class.java.let { serviceClass -> - ServiceLoader.load(serviceClass, serviceClass.classLoader).toList() -} +private val handlers: List = ServiceLoader.load( + CoroutineExceptionHandler::class.java, + CoroutineExceptionHandler::class.java.classLoader +).iterator().asSequence().toList() + internal actual fun handleCoroutineExceptionImpl(context: CoroutineContext, exception: Throwable) { // use additional extension handlers diff --git a/kotlinx-coroutines-core/jvm/src/internal/FastServiceLoader.kt b/kotlinx-coroutines-core/jvm/src/internal/FastServiceLoader.kt index 6cf1b4750b..ecb2213e4a 100644 --- a/kotlinx-coroutines-core/jvm/src/internal/FastServiceLoader.kt +++ b/kotlinx-coroutines-core/jvm/src/internal/FastServiceLoader.kt @@ -6,11 +6,6 @@ import java.util.* import java.util.jar.* import java.util.zip.* -/** - * Name of the boolean property that enables using of [FastServiceLoader]. - */ -private const val FAST_SERVICE_LOADER_PROPERTY_NAME = "kotlinx.coroutines.fast.service.loader" - /** * A simplified version of [ServiceLoader]. * 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 internal object FastServiceLoader { private const val PREFIX: String = "META-INF/services/" - private val FAST_SERVICE_LOADER_ENABLED = systemProp(FAST_SERVICE_LOADER_PROPERTY_NAME, true) - internal fun load(service: Class, loader: ClassLoader): List { - if (!FAST_SERVICE_LOADER_ENABLED) { - return ServiceLoader.load(service, loader).toList() - } return try { loadProviders(service, loader) } catch (e: Throwable) { diff --git a/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt b/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt index f2f0af7aa8..63e38cb084 100644 --- a/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt +++ b/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt @@ -4,15 +4,33 @@ import kotlinx.coroutines.* import java.util.* import kotlin.coroutines.* +/** + * Name of the boolean property that enables using of [FastServiceLoader]. + */ +private const val FAST_SERVICE_LOADER_PROPERTY_NAME = "kotlinx.coroutines.fast.service.loader" + // Lazy loader for the main dispatcher internal object MainDispatcherLoader { + + private val FAST_SERVICE_LOADER_ENABLED = systemProp(FAST_SERVICE_LOADER_PROPERTY_NAME, true) + @JvmField val dispatcher: MainCoroutineDispatcher = loadMainDispatcher() private fun loadMainDispatcher(): MainCoroutineDispatcher { return try { - val factories = MainDispatcherFactory::class.java.let { clz -> - FastServiceLoader.load(clz, clz.classLoader) + val factories = if (FAST_SERVICE_LOADER_ENABLED) { + MainDispatcherFactory::class.java.let { clz -> + FastServiceLoader.load(clz, clz.classLoader) + } + } else { + //We are explicitly using the + //`ServiceLoader.load(MyClass::class.java, MyClass::class.java.classLoader).iterator()` + //form of the ServiceLoader call to enable R8 optimization when compiled on Android. + ServiceLoader.load( + MainDispatcherFactory::class.java, + MainDispatcherFactory::class.java.classLoader + ).iterator().asSequence().toList() } factories.maxBy { it.loadPriority }?.tryCreateDispatcher(factories) ?: MissingMainCoroutineDispatcher(null)