Merge branch 'master' into develop
diff --git a/common/kotlinx-coroutines-core-common/src/Dispatchers.common.kt b/common/kotlinx-coroutines-core-common/src/Dispatchers.common.kt
index c3eda77..be12640 100644
--- a/common/kotlinx-coroutines-core-common/src/Dispatchers.common.kt
+++ b/common/kotlinx-coroutines-core-common/src/Dispatchers.common.kt
@@ -9,7 +9,7 @@
/**
* Groups various implementations of [CoroutineDispatcher].
*/
-expect object Dispatchers {
+public expect object Dispatchers {
/**
* The default [CoroutineDispatcher] that is used by all standard builders like
* [launch][CoroutineScope.launch], [async][CoroutineScope.async], etc
diff --git a/core/kotlinx-coroutines-core/src/Dispatchers.kt b/core/kotlinx-coroutines-core/src/Dispatchers.kt
index 50396a9..a79a219 100644
--- a/core/kotlinx-coroutines-core/src/Dispatchers.kt
+++ b/core/kotlinx-coroutines-core/src/Dispatchers.kt
@@ -19,15 +19,7 @@
/**
* Groups various implementations of [CoroutineDispatcher].
*/
-actual object Dispatchers {
-
- private val mainDispatcher = loadMainDispatcher()
-
- private fun loadMainDispatcher(): MainCoroutineDispatcher? {
- return MainDispatcherFactory::class.java.let { clz ->
- ServiceLoader.load(clz, clz.classLoader).toList()
- }.maxBy { it.loadPriority }?.createDispatcher()
- }
+public actual object Dispatchers {
/**
* The default [CoroutineDispatcher] that is used by all standard builders like
@@ -59,8 +51,7 @@
* Implementation note: [MainCoroutineDispatcher.immediate] is not supported on Native and JS platforms.
*/
@JvmStatic
- public actual val Main: MainCoroutineDispatcher get() = mainDispatcher ?: error("Module with Main dispatcher is missing. " +
- "Add dependency with required Main dispatcher, e.g. 'kotlinx-coroutines-android'")
+ public actual val Main: MainCoroutineDispatcher get() = MainDispatcherLoader.dispatcher
/**
* A coroutine dispatcher that is not confined to any specific thread.
@@ -97,3 +88,47 @@
@JvmStatic
public val IO: CoroutineDispatcher = DefaultScheduler.IO
}
+
+// Lazy loader for the main dispatcher
+private object MainDispatcherLoader {
+ @JvmField
+ val dispatcher: MainCoroutineDispatcher =
+ MainDispatcherFactory::class.java.let { clz ->
+ ServiceLoader.load(clz, clz.classLoader).toList()
+ }.maxBy { it.loadPriority }?.tryCreateDispatcher() ?: MissingMainCoroutineDispatcher(null)
+
+ /**
+ * If anything goes wrong while trying to create main dispatcher (class not found,
+ * initialization failed, etc), then replace the main dispatcher with a special
+ * stub that throws an error message on any attempt to actually use it.
+ */
+ private fun MainDispatcherFactory.tryCreateDispatcher(): MainCoroutineDispatcher =
+ try {
+ createDispatcher()
+ } catch (cause: Throwable) {
+ MissingMainCoroutineDispatcher(cause)
+ }
+}
+
+private class MissingMainCoroutineDispatcher(val cause: Throwable?) : MainCoroutineDispatcher(), Delay {
+ override val immediate: MainCoroutineDispatcher get() = this
+
+ override fun dispatch(context: CoroutineContext, block: Runnable) =
+ missing()
+
+ override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) =
+ missing()
+
+ private fun missing() {
+ if (cause == null) {
+ throw IllegalStateException(
+ "Module with the Main dispatcher is missing. " +
+ "Add dependency providing the Main dispatcher, e.g. 'kotlinx-coroutines-android'"
+ )
+ } else {
+ throw IllegalStateException("Module with the Main dispatcher had failed to initialize", cause)
+ }
+ }
+
+ override fun toString(): String = "Main[missing${if (cause != null) ", cause=$cause" else ""}]"
+}
\ No newline at end of file
diff --git a/core/kotlinx-coroutines-core/src/ThreadPoolDispatcher.kt b/core/kotlinx-coroutines-core/src/ThreadPoolDispatcher.kt
index 6a56396..37f17b9 100644
--- a/core/kotlinx-coroutines-core/src/ThreadPoolDispatcher.kt
+++ b/core/kotlinx-coroutines-core/src/ThreadPoolDispatcher.kt
@@ -14,8 +14,19 @@
* **NOTE: The resulting [ExecutorCoroutineDispatcher] owns native resources (its thread).
* Resources are reclaimed by [ExecutorCoroutineDispatcher.close].**
*
+ * **NOTE: This API will be replaced in the future**. A different API to create thread-limited thread pools
+ * that is based on a shared thread-pool and does not require the resulting dispatcher to be explicitly closed
+ * will be provided, thus avoiding potential thread leaks and also significantly improving performance, due
+ * to coroutine-oriented scheduling policy and thread-switch minimization.
+ * See [issue #261](https://github.com/Kotlin/kotlinx.coroutines/issues/261) for details.
+ * If you need a completely separate thread-pool with scheduling policy that is based on the standard
+ * JDK executors, use the following expression:
+ * `Executors.newSingleThreadExecutor().asCoroutineDispatcher()`.
+ * See [Executor.asCoroutineDispatcher] for details.
+ *
* @param name the base name of the created thread.
*/
+@ObsoleteCoroutinesApi
fun newSingleThreadContext(name: String): ExecutorCoroutineDispatcher =
newFixedThreadPoolContext(1, name)
@@ -40,9 +51,20 @@
* **NOTE: The resulting [ExecutorCoroutineDispatcher] owns native resources (its threads).
* Resources are reclaimed by [ExecutorCoroutineDispatcher.close].**
*
+ * **NOTE: This API will be replaced in the future**. A different API to create thread-limited thread pools
+ * that is based on a shared thread-pool and does not require the resulting dispatcher to be explicitly closed
+ * will be provided, thus avoiding potential thread leaks and also significantly improving performance, due
+ * to coroutine-oriented scheduling policy and thread-switch minimization.
+ * See [issue #261](https://github.com/Kotlin/kotlinx.coroutines/issues/261) for details.
+ * If you need a completely separate thread-pool with scheduling policy that is based on the standard
+ * JDK executors, use the following expression:
+ * `Executors.newFixedThreadPool().asCoroutineDispatcher()`.
+ * See [Executor.asCoroutineDispatcher] for details.
+ *
* @param nThreads the number of threads.
* @param name the base name of the created threads.
*/
+@ObsoleteCoroutinesApi
fun newFixedThreadPoolContext(nThreads: Int, name: String): ExecutorCoroutineDispatcher {
require(nThreads >= 1) { "Expected at least one thread, but $nThreads specified" }
return ThreadPoolDispatcher(nThreads, name)
diff --git a/core/kotlinx-coroutines-core/src/scheduling/Dispatcher.kt b/core/kotlinx-coroutines-core/src/scheduling/Dispatcher.kt
index 7a718f5..7e359fb 100644
--- a/core/kotlinx-coroutines-core/src/scheduling/Dispatcher.kt
+++ b/core/kotlinx-coroutines-core/src/scheduling/Dispatcher.kt
@@ -20,6 +20,11 @@
override fun close() {
throw UnsupportedOperationException("$DEFAULT_SCHEDULER_NAME cannot be closed")
}
+
+ override fun toString(): String = DEFAULT_SCHEDULER_NAME
+
+ @InternalCoroutinesApi
+ fun toDebugString(): String = super.toString()
}
/**
diff --git a/gradle/atomicfu-common.gradle b/gradle/atomicfu-common.gradle
index 9a55e17..236fbfd 100644
--- a/gradle/atomicfu-common.gradle
+++ b/gradle/atomicfu-common.gradle
@@ -3,5 +3,5 @@
*/
dependencies {
- compile "org.jetbrains.kotlinx:atomicfu-common:$atomicFU_version"
+ compileOnly "org.jetbrains.kotlinx:atomicfu-common:$atomicFU_version"
}
\ No newline at end of file
diff --git a/js/kotlinx-coroutines-core-js/src/Dispatchers.kt b/js/kotlinx-coroutines-core-js/src/Dispatchers.kt
index 8679199..6a0a776 100644
--- a/js/kotlinx-coroutines-core-js/src/Dispatchers.kt
+++ b/js/kotlinx-coroutines-core-js/src/Dispatchers.kt
@@ -6,7 +6,7 @@
import kotlin.coroutines.experimental.*
-actual object Dispatchers {
+public actual object Dispatchers {
public actual val Default: CoroutineDispatcher = createDefaultDispatcher()
diff --git a/native/kotlinx-coroutines-core-native/src/Dispatchers.kt b/native/kotlinx-coroutines-core-native/src/Dispatchers.kt
index 759743c..ba86913 100644
--- a/native/kotlinx-coroutines-core-native/src/Dispatchers.kt
+++ b/native/kotlinx-coroutines-core-native/src/Dispatchers.kt
@@ -6,8 +6,7 @@
import kotlin.coroutines.experimental.*
-
-actual object Dispatchers {
+public actual object Dispatchers {
public actual val Default: CoroutineDispatcher = createDefaultDispatcher()