Remove obsolete CommonPool support (#2916)
* We state that it's deprecated since the initial release of CoroutineScheduler
* It is an additional DEX load for Android
diff --git a/kotlinx-coroutines-core/common/src/CoroutineContext.common.kt b/kotlinx-coroutines-core/common/src/CoroutineContext.common.kt
index 68b4b1a..e178332 100644
--- a/kotlinx-coroutines-core/common/src/CoroutineContext.common.kt
+++ b/kotlinx-coroutines-core/common/src/CoroutineContext.common.kt
@@ -12,8 +12,6 @@
*/
public expect fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext
-internal expect fun createDefaultDispatcher(): CoroutineDispatcher
-
@Suppress("PropertyName")
internal expect val DefaultDelay: Delay
diff --git a/kotlinx-coroutines-core/js/src/CoroutineContext.kt b/kotlinx-coroutines-core/js/src/CoroutineContext.kt
index a98ea97..95cb3c2 100644
--- a/kotlinx-coroutines-core/js/src/CoroutineContext.kt
+++ b/kotlinx-coroutines-core/js/src/CoroutineContext.kt
@@ -12,7 +12,7 @@
private const val UNDEFINED = "undefined"
internal external val process: dynamic
-internal actual fun createDefaultDispatcher(): CoroutineDispatcher = when {
+internal fun createDefaultDispatcher(): CoroutineDispatcher = when {
// Check if we are running under jsdom. WindowDispatcher doesn't work under jsdom because it accesses MessageEvent#source.
// It is not implemented in jsdom, see https://github.com/jsdom/jsdom/blob/master/Changelog.md
// "It's missing a few semantics, especially around origins, as well as MessageEvent source."
diff --git a/kotlinx-coroutines-core/jvm/src/CommonPool.kt b/kotlinx-coroutines-core/jvm/src/CommonPool.kt
deleted file mode 100644
index 502630b..0000000
--- a/kotlinx-coroutines-core/jvm/src/CommonPool.kt
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.coroutines
-
-import java.util.concurrent.*
-import java.util.concurrent.atomic.*
-import kotlin.coroutines.*
-
-/**
- * Represents common pool of shared threads as coroutine dispatcher for compute-intensive tasks.
- *
- * If there isn't a SecurityManager present it uses [java.util.concurrent.ForkJoinPool] when available, which implements
- * efficient work-stealing algorithm for its queues, so every coroutine resumption is dispatched as a separate task even
- * when it already executes inside the pool. When available, it wraps `ForkJoinPool.commonPool` and provides a similar
- * shared pool where not.
- *
- * If there is a SecurityManager present (as would be if running inside a Java Web Start context) then a plain thread
- * pool is created. This is to work around the fact that ForkJoinPool creates threads that cannot perform
- * privileged actions.
- */
-internal object CommonPool : ExecutorCoroutineDispatcher() {
-
- /**
- * Name of the property that controls default parallelism level of [CommonPool].
- * If the property is not specified, `Runtime.getRuntime().availableProcessors() - 1` will be used instead (or `1` for single-core JVM).
- * Note that until Java 10, if an application is run within a container,
- * `Runtime.getRuntime().availableProcessors()` is not aware of container constraints and will return the real number of cores.
- */
- private const val DEFAULT_PARALLELISM_PROPERTY_NAME = "kotlinx.coroutines.default.parallelism"
-
- override val executor: Executor
- get() = pool ?: getOrCreatePoolSync()
-
- // Equals to -1 if not explicitly specified
- private val requestedParallelism = run<Int> {
- val property = Try { System.getProperty(DEFAULT_PARALLELISM_PROPERTY_NAME) } ?: return@run -1
- val parallelism = property.toIntOrNull()
- if (parallelism == null || parallelism < 1) {
- error("Expected positive number in $DEFAULT_PARALLELISM_PROPERTY_NAME, but has $property")
- }
- parallelism
- }
-
- private val parallelism: Int
- get() = requestedParallelism.takeIf { it > 0 }
- ?: (Runtime.getRuntime().availableProcessors() - 1).coerceAtLeast(1)
-
- // For debug and tests
- private var usePrivatePool = false
-
- @Volatile
- private var pool: Executor? = null
-
- private inline fun <T> Try(block: () -> T) = try { block() } catch (e: Throwable) { null }
-
- private fun createPool(): ExecutorService {
- if (System.getSecurityManager() != null) return createPlainPool()
- // Reflection on ForkJoinPool class so that it works on JDK 6 (which is absent there)
- val fjpClass = Try { Class.forName("java.util.concurrent.ForkJoinPool") }
- ?: return createPlainPool() // Fallback to plain thread pool
- // Try to use commonPool unless parallelism was explicitly specified or in debug privatePool mode
- if (!usePrivatePool && requestedParallelism < 0) {
- Try { fjpClass.getMethod("commonPool").invoke(null) as? ExecutorService }
- ?.takeIf { isGoodCommonPool(fjpClass, it) }
- ?.let { return it }
- }
- // Try to create private ForkJoinPool instance
- Try { fjpClass.getConstructor(Int::class.java).newInstance(parallelism) as? ExecutorService }
- ?. let { return it }
- // Fallback to plain thread pool
- return createPlainPool()
- }
-
- /**
- * Checks that this ForkJoinPool's parallelism is at least one to avoid pathological bugs.
- */
- internal fun isGoodCommonPool(fjpClass: Class<*>, executor: ExecutorService): Boolean {
- // We cannot use getParallelism, since it lies to us (always returns at least 1)
- // So we submit a task and check that getPoolSize is at least one after that
- // A broken FJP (that is configured for 0 parallelism) would not execute the task and
- // would report its pool size as zero.
- executor.submit {}
- val actual = Try { fjpClass.getMethod("getPoolSize").invoke(executor) as? Int }
- ?: return false
- return actual >= 1
- }
-
- private fun createPlainPool(): ExecutorService {
- val threadId = AtomicInteger()
- return Executors.newFixedThreadPool(parallelism) {
- Thread(it, "CommonPool-worker-${threadId.incrementAndGet()}").apply { isDaemon = true }
- }
- }
-
- @Synchronized
- private fun getOrCreatePoolSync(): Executor =
- pool ?: createPool().also { pool = it }
-
- override fun dispatch(context: CoroutineContext, block: Runnable) {
- try {
- (pool ?: getOrCreatePoolSync()).execute(wrapTask(block))
- } catch (e: RejectedExecutionException) {
- unTrackTask()
- // CommonPool only rejects execution when it is being closed and this behavior is reserved
- // for testing purposes, so we don't have to worry about cancelling the affected Job here.
- DefaultExecutor.enqueue(block)
- }
- }
-
- // used for tests
- @Synchronized
- internal fun usePrivatePool() {
- shutdown(0)
- usePrivatePool = true
- pool = null
- }
-
- // used for tests
- @Synchronized
- internal fun shutdown(timeout: Long) {
- (pool as? ExecutorService)?.apply {
- shutdown()
- if (timeout > 0)
- awaitTermination(timeout, TimeUnit.MILLISECONDS)
- shutdownNow().forEach { DefaultExecutor.enqueue(it) }
- }
- pool = Executor { throw RejectedExecutionException("CommonPool was shutdown") }
- }
-
- // used for tests
- @Synchronized
- internal fun restore() {
- shutdown(0)
- usePrivatePool = false
- pool = null
- }
-
- override fun toString(): String = "CommonPool"
-
- override fun close(): Unit = error("Close cannot be invoked on CommonPool")
-}
diff --git a/kotlinx-coroutines-core/jvm/src/CoroutineContext.kt b/kotlinx-coroutines-core/jvm/src/CoroutineContext.kt
index e91bb9f..702a0d8 100644
--- a/kotlinx-coroutines-core/jvm/src/CoroutineContext.kt
+++ b/kotlinx-coroutines-core/jvm/src/CoroutineContext.kt
@@ -9,19 +9,6 @@
import kotlin.coroutines.*
import kotlin.coroutines.jvm.internal.CoroutineStackFrame
-internal const val COROUTINES_SCHEDULER_PROPERTY_NAME = "kotlinx.coroutines.scheduler"
-
-internal val useCoroutinesScheduler = systemProp(COROUTINES_SCHEDULER_PROPERTY_NAME).let { value ->
- when (value) {
- null, "", "on" -> true
- "off" -> false
- else -> error("System property '$COROUTINES_SCHEDULER_PROPERTY_NAME' has unrecognized value '$value'")
- }
-}
-
-internal actual fun createDefaultDispatcher(): CoroutineDispatcher =
- if (useCoroutinesScheduler) DefaultScheduler else CommonPool
-
/**
* Creates context for the new coroutine. It installs [Dispatchers.Default] when no other dispatcher nor
* [ContinuationInterceptor] is specified, and adds optional support for debugging facilities (when turned on).
diff --git a/kotlinx-coroutines-core/jvm/src/Dispatchers.kt b/kotlinx-coroutines-core/jvm/src/Dispatchers.kt
index d82598e..7872389 100644
--- a/kotlinx-coroutines-core/jvm/src/Dispatchers.kt
+++ b/kotlinx-coroutines-core/jvm/src/Dispatchers.kt
@@ -29,7 +29,7 @@
* Level of parallelism X guarantees that no more than X tasks can be executed in this dispatcher in parallel.
*/
@JvmStatic
- public actual val Default: CoroutineDispatcher = createDefaultDispatcher()
+ public actual val Default: CoroutineDispatcher = DefaultScheduler
/**
* A coroutine dispatcher that is confined to the Main thread operating with UI objects.
diff --git a/kotlinx-coroutines-core/jvm/test/CommonPoolTest.kt b/kotlinx-coroutines-core/jvm/test/CommonPoolTest.kt
deleted file mode 100644
index 8f9f855..0000000
--- a/kotlinx-coroutines-core/jvm/test/CommonPoolTest.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.coroutines
-
-import org.junit.Test
-import java.lang.reflect.*
-import java.util.concurrent.*
-import kotlin.test.*
-
-@Suppress("DEPRECATION")
-class CommonPoolTest {
- private inline fun <T> Try(block: () -> T) = try { block() } catch (e: Throwable) { null }
-
- @Test
- fun testIsGoodCommonPool() {
- // Test only on JDKs that has all we need
- val fjpClass = Try { Class.forName("java.util.concurrent.ForkJoinPool") } ?: return
- val wtfClass = Try { Class.forName("java.util.concurrent.ForkJoinPool${'$'}ForkJoinWorkerThreadFactory") } ?: return
- val dwtfClass = Try { Class.forName("java.util.concurrent.ForkJoinPool${'$'}DefaultForkJoinWorkerThreadFactory") } ?: return
- // We need private constructor to create "broken" FJP instance
- val fjpCtor = Try { fjpClass.getDeclaredConstructor(
- Int::class.java,
- wtfClass,
- Thread.UncaughtExceptionHandler::class.java,
- Int::class.java,
- String::class.java
- ) } ?: return
- fjpCtor.isAccessible = true
- val dwtfCtor = Try { dwtfClass.getDeclaredConstructor() } ?: return
- dwtfCtor.isAccessible = true
- // Create bad pool
- val fjp0: ExecutorService = createFJP(0, fjpCtor, dwtfCtor) ?: return
- assertFalse(CommonPool.isGoodCommonPool(fjpClass, fjp0))
- fjp0.shutdown()
- // Create good pool
- val fjp1: ExecutorService = createFJP(1, fjpCtor, dwtfCtor) ?: return
- assertTrue(CommonPool.isGoodCommonPool(fjpClass, fjp1))
- fjp1.shutdown()
- }
-
- private fun createFJP(
- parallelism: Int,
- fjpCtor: Constructor<out Any>,
- dwtfCtor: Constructor<out Any>
- ): ExecutorService? = Try {
- fjpCtor.newInstance(
- parallelism,
- dwtfCtor.newInstance(),
- Thread.getDefaultUncaughtExceptionHandler(),
- 0,
- "Worker"
- )
- } as? ExecutorService
-}
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/jvm/test/TestBase.kt b/kotlinx-coroutines-core/jvm/test/TestBase.kt
index 61a2c8b..a8955f3 100644
--- a/kotlinx-coroutines-core/jvm/test/TestBase.kt
+++ b/kotlinx-coroutines-core/jvm/test/TestBase.kt
@@ -199,15 +199,12 @@
}
fun initPoolsBeforeTest() {
- CommonPool.usePrivatePool()
DefaultScheduler.usePrivateScheduler()
}
fun shutdownPoolsAfterTest() {
- CommonPool.shutdown(SHUTDOWN_TIMEOUT)
DefaultScheduler.shutdown(SHUTDOWN_TIMEOUT)
DefaultExecutor.shutdown(SHUTDOWN_TIMEOUT)
- CommonPool.restore()
DefaultScheduler.restore()
}
diff --git a/kotlinx-coroutines-core/jvm/test/knit/TestUtil.kt b/kotlinx-coroutines-core/jvm/test/knit/TestUtil.kt
index 2e61ec6..5e83d7e 100644
--- a/kotlinx-coroutines-core/jvm/test/knit/TestUtil.kt
+++ b/kotlinx-coroutines-core/jvm/test/knit/TestUtil.kt
@@ -26,7 +26,6 @@
fun <R> test(name: String, block: () -> R): List<String> = outputException(name) {
try {
captureOutput(name, stdoutEnabled = OUT_ENABLED) { log ->
- CommonPool.usePrivatePool()
DefaultScheduler.usePrivateScheduler()
DefaultExecutor.shutdown(SHUTDOWN_TIMEOUT)
resetCoroutineId()
@@ -39,7 +38,6 @@
} finally {
// the shutdown
log.println("--- shutting down")
- CommonPool.shutdown(SHUTDOWN_TIMEOUT)
DefaultScheduler.shutdown(SHUTDOWN_TIMEOUT)
shutdownDispatcherPools(SHUTDOWN_TIMEOUT)
DefaultExecutor.shutdown(SHUTDOWN_TIMEOUT) // the last man standing -- cleanup all pending tasks
@@ -47,7 +45,6 @@
checkTestThreads(threadsBefore) // check thread if the main completed successfully
}
} finally {
- CommonPool.restore()
DefaultScheduler.restore()
}
}
diff --git a/kotlinx-coroutines-core/native/src/CoroutineContext.kt b/kotlinx-coroutines-core/native/src/CoroutineContext.kt
index 47afd8a..ac0f439 100644
--- a/kotlinx-coroutines-core/native/src/CoroutineContext.kt
+++ b/kotlinx-coroutines-core/native/src/CoroutineContext.kt
@@ -25,7 +25,7 @@
internal fun loopWasShutDown(): Nothing = error("Cannot execute task because event loop was shut down")
-internal actual fun createDefaultDispatcher(): CoroutineDispatcher =
+internal fun createDefaultDispatcher(): CoroutineDispatcher =
DefaultExecutor
@SharedImmutable