From 2717bd52ea9a6a9b3fb867cc6d314cc8cc05515e Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Mon, 1 Oct 2018 17:15:04 +0300 Subject: [PATCH 1/3] Main thread dispatchers are grouped under Dispatchers object and are discoverable via ServiceLoader * Dispatchers.Main can be consistently used and easily discovered, its implementation is discovered with ServiceLoader * It allows us to add iOS Dispatchers.Main implementation in the future thus opening the door for multiplatform UI-dispatched code * Workaround for #626, now Android users can start migration to RC coroutines Fixed #626 --- .../kotlinx-coroutines-android.txt | 5 +- .../kotlinx-coroutines-core.txt | 12 ++- .../kotlinx-coroutines-javafx.txt | 8 +- .../kotlinx-coroutines-swing.txt | 8 +- .../src/Dispatchers.common.kt | 33 +++++-- .../src/MainCoroutineDispatcher.kt | 26 +++++ .../src/internal/MainDispatcherFactory.kt | 14 +++ .../src/Dispatchers.kt | 94 ++++++++++++++++--- .../src/Dispatchers.kt | 30 ++++++ .../src/Dispatchers.kt | 31 ++++++ ...xperimental.internal.MainDispatcherFactory | 1 + .../src/HandlerDispatcher.kt | 14 ++- ...xperimental.internal.MainDispatcherFactory | 1 + .../src/JavaFxDispatcher.kt | 67 ++++++++----- ...xperimental.internal.MainDispatcherFactory | 1 + .../src/SwingDispatcher.kt | 45 ++++++--- 16 files changed, 318 insertions(+), 72 deletions(-) create mode 100644 common/kotlinx-coroutines-core-common/src/MainCoroutineDispatcher.kt create mode 100644 common/kotlinx-coroutines-core-common/src/internal/MainDispatcherFactory.kt create mode 100644 js/kotlinx-coroutines-core-js/src/Dispatchers.kt create mode 100644 native/kotlinx-coroutines-core-native/src/Dispatchers.kt create mode 100644 ui/kotlinx-coroutines-android/resources/META-INF/services/kotlinx.coroutines.experimental.internal.MainDispatcherFactory create mode 100644 ui/kotlinx-coroutines-javafx/resources/META-INF/services/kotlinx.coroutines.experimental.internal.MainDispatcherFactory create mode 100644 ui/kotlinx-coroutines-swing/resources/META-INF/services/kotlinx.coroutines.experimental.internal.MainDispatcherFactory diff --git a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-android.txt b/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-android.txt index baef16558b..93aea1ce7d 100644 --- a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-android.txt +++ b/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-android.txt @@ -4,6 +4,7 @@ public final class kotlinx/coroutines/experimental/android/HandlerContext : kotl public final fun awaitFrame (Lkotlin/coroutines/experimental/Continuation;)Ljava/lang/Object; public fun dispatch (Lkotlin/coroutines/experimental/CoroutineContext;Ljava/lang/Runnable;)V public fun equals (Ljava/lang/Object;)Z + public synthetic fun getImmediate ()Lkotlinx/coroutines/experimental/MainCoroutineDispatcher; public fun getImmediate ()Lkotlinx/coroutines/experimental/android/HandlerContext; public synthetic fun getImmediate ()Lkotlinx/coroutines/experimental/android/HandlerDispatcher; public fun hashCode ()I @@ -18,7 +19,7 @@ public final class kotlinx/coroutines/experimental/android/HandlerContextKt { public static final fun getUI ()Lkotlinx/coroutines/experimental/android/HandlerContext; } -public abstract class kotlinx/coroutines/experimental/android/HandlerDispatcher : kotlinx/coroutines/experimental/CoroutineDispatcher, kotlinx/coroutines/experimental/Delay { +public abstract class kotlinx/coroutines/experimental/android/HandlerDispatcher : kotlinx/coroutines/experimental/MainCoroutineDispatcher, kotlinx/coroutines/experimental/Delay { public synthetic fun delay (JLjava/util/concurrent/TimeUnit;Lkotlin/coroutines/experimental/Continuation;)Ljava/lang/Object; public fun delay (JLkotlin/coroutines/experimental/Continuation;)Ljava/lang/Object; public abstract fun getImmediate ()Lkotlinx/coroutines/experimental/android/HandlerDispatcher; @@ -32,6 +33,6 @@ public final class kotlinx/coroutines/experimental/android/HandlerDispatcherKt { public static final fun from (Landroid/os/Handler;)Lkotlinx/coroutines/experimental/android/HandlerDispatcher; public static final fun from (Landroid/os/Handler;Ljava/lang/String;)Lkotlinx/coroutines/experimental/android/HandlerDispatcher; public static synthetic fun from$default (Landroid/os/Handler;Ljava/lang/String;ILjava/lang/Object;)Lkotlinx/coroutines/experimental/android/HandlerDispatcher; - public static final fun getMain (Lkotlinx/coroutines/experimental/Dispatchers;)Lkotlinx/coroutines/experimental/android/HandlerDispatcher; + public static final synthetic fun getMain (Lkotlinx/coroutines/experimental/Dispatchers;)Lkotlinx/coroutines/experimental/android/HandlerDispatcher; } diff --git a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-core.txt b/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-core.txt index b84c4b6d05..53b858f3a2 100644 --- a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-core.txt +++ b/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-core.txt @@ -314,14 +314,15 @@ public final class kotlinx/coroutines/experimental/DispatchedTask$DefaultImpls { } public final class kotlinx/coroutines/experimental/Dispatchers { - public static final field Default Lkotlinx/coroutines/experimental/CoroutineDispatcher; public static final field INSTANCE Lkotlinx/coroutines/experimental/Dispatchers; - public static final field Unconfined Lkotlinx/coroutines/experimental/CoroutineDispatcher; + public static final fun getDefault ()Lkotlinx/coroutines/experimental/CoroutineDispatcher; + public static final fun getIO ()Lkotlinx/coroutines/experimental/CoroutineDispatcher; + public static final fun getMain ()Lkotlinx/coroutines/experimental/MainCoroutineDispatcher; + public static final fun getUnconfined ()Lkotlinx/coroutines/experimental/CoroutineDispatcher; } public final class kotlinx/coroutines/experimental/DispatchersKt { public static final field IO_PARALLELISM_PROPERTY_NAME Ljava/lang/String; - public static final fun getIO (Lkotlinx/coroutines/experimental/Dispatchers;)Lkotlinx/coroutines/experimental/CoroutineDispatcher; } public abstract interface class kotlinx/coroutines/experimental/DisposableHandle { @@ -468,6 +469,11 @@ public final class kotlinx/coroutines/experimental/LazyDeferredKt { public static final fun lazyDefer (Lkotlin/coroutines/experimental/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/experimental/Deferred; } +public abstract class kotlinx/coroutines/experimental/MainCoroutineDispatcher : kotlinx/coroutines/experimental/CoroutineDispatcher { + public fun ()V + public abstract fun getImmediate ()Lkotlinx/coroutines/experimental/MainCoroutineDispatcher; +} + public final class kotlinx/coroutines/experimental/NonCancellable : kotlin/coroutines/experimental/AbstractCoroutineContextElement, kotlinx/coroutines/experimental/Job { public static final field INSTANCE Lkotlinx/coroutines/experimental/NonCancellable; public fun attachChild (Lkotlinx/coroutines/experimental/ChildJob;)Lkotlinx/coroutines/experimental/ChildHandle; diff --git a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-javafx.txt b/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-javafx.txt index 69c98bb78f..341f038b0e 100644 --- a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-javafx.txt +++ b/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-javafx.txt @@ -1,18 +1,18 @@ public final class kotlinx/coroutines/experimental/javafx/JavaFx : kotlinx/coroutines/experimental/javafx/JavaFxDispatcher { public static final field INSTANCE Lkotlinx/coroutines/experimental/javafx/JavaFx; public final fun awaitPulse (Lkotlin/coroutines/experimental/Continuation;)Ljava/lang/Object; - public fun dispatch (Lkotlin/coroutines/experimental/CoroutineContext;Ljava/lang/Runnable;)V - public fun invokeOnTimeout (JLjava/lang/Runnable;)Lkotlinx/coroutines/experimental/DisposableHandle; - public fun scheduleResumeAfterDelay (JLkotlinx/coroutines/experimental/CancellableContinuation;)V + public fun getImmediate ()Lkotlinx/coroutines/experimental/MainCoroutineDispatcher; public fun toString ()Ljava/lang/String; } -public abstract class kotlinx/coroutines/experimental/javafx/JavaFxDispatcher : kotlinx/coroutines/experimental/CoroutineDispatcher, kotlinx/coroutines/experimental/Delay { +public abstract class kotlinx/coroutines/experimental/javafx/JavaFxDispatcher : kotlinx/coroutines/experimental/MainCoroutineDispatcher, kotlinx/coroutines/experimental/Delay { public synthetic fun delay (JLjava/util/concurrent/TimeUnit;Lkotlin/coroutines/experimental/Continuation;)Ljava/lang/Object; public fun delay (JLkotlin/coroutines/experimental/Continuation;)Ljava/lang/Object; + public fun dispatch (Lkotlin/coroutines/experimental/CoroutineContext;Ljava/lang/Runnable;)V public fun invokeOnTimeout (JLjava/lang/Runnable;)Lkotlinx/coroutines/experimental/DisposableHandle; public synthetic fun invokeOnTimeout (JLjava/util/concurrent/TimeUnit;Ljava/lang/Runnable;)Lkotlinx/coroutines/experimental/DisposableHandle; public synthetic fun scheduleResumeAfterDelay (JLjava/util/concurrent/TimeUnit;Lkotlinx/coroutines/experimental/CancellableContinuation;)V + public fun scheduleResumeAfterDelay (JLkotlinx/coroutines/experimental/CancellableContinuation;)V } public final class kotlinx/coroutines/experimental/javafx/JavaFxDispatcherKt { diff --git a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-swing.txt b/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-swing.txt index dcb7f3bb3b..14be922b88 100644 --- a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-swing.txt +++ b/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-swing.txt @@ -1,17 +1,17 @@ public final class kotlinx/coroutines/experimental/swing/Swing : kotlinx/coroutines/experimental/swing/SwingDispatcher { public static final field INSTANCE Lkotlinx/coroutines/experimental/swing/Swing; - public fun dispatch (Lkotlin/coroutines/experimental/CoroutineContext;Ljava/lang/Runnable;)V - public fun invokeOnTimeout (JLjava/lang/Runnable;)Lkotlinx/coroutines/experimental/DisposableHandle; - public fun scheduleResumeAfterDelay (JLkotlinx/coroutines/experimental/CancellableContinuation;)V + public fun getImmediate ()Lkotlinx/coroutines/experimental/MainCoroutineDispatcher; public fun toString ()Ljava/lang/String; } -public abstract class kotlinx/coroutines/experimental/swing/SwingDispatcher : kotlinx/coroutines/experimental/CoroutineDispatcher, kotlinx/coroutines/experimental/Delay { +public abstract class kotlinx/coroutines/experimental/swing/SwingDispatcher : kotlinx/coroutines/experimental/MainCoroutineDispatcher, kotlinx/coroutines/experimental/Delay { public synthetic fun delay (JLjava/util/concurrent/TimeUnit;Lkotlin/coroutines/experimental/Continuation;)Ljava/lang/Object; public fun delay (JLkotlin/coroutines/experimental/Continuation;)Ljava/lang/Object; + public fun dispatch (Lkotlin/coroutines/experimental/CoroutineContext;Ljava/lang/Runnable;)V public fun invokeOnTimeout (JLjava/lang/Runnable;)Lkotlinx/coroutines/experimental/DisposableHandle; public synthetic fun invokeOnTimeout (JLjava/util/concurrent/TimeUnit;Ljava/lang/Runnable;)Lkotlinx/coroutines/experimental/DisposableHandle; public synthetic fun scheduleResumeAfterDelay (JLjava/util/concurrent/TimeUnit;Lkotlinx/coroutines/experimental/CancellableContinuation;)V + public fun scheduleResumeAfterDelay (JLkotlinx/coroutines/experimental/CancellableContinuation;)V } public final class kotlinx/coroutines/experimental/swing/SwingDispatcherKt { diff --git a/common/kotlinx-coroutines-core-common/src/Dispatchers.common.kt b/common/kotlinx-coroutines-core-common/src/Dispatchers.common.kt index 4e723eddd4..c3eda77902 100644 --- a/common/kotlinx-coroutines-core-common/src/Dispatchers.common.kt +++ b/common/kotlinx-coroutines-core-common/src/Dispatchers.common.kt @@ -4,13 +4,12 @@ package kotlinx.coroutines.experimental -import kotlinx.coroutines.experimental.internal.* import kotlin.coroutines.experimental.* /** * Groups various implementations of [CoroutineDispatcher]. */ -public object Dispatchers { +expect object Dispatchers { /** * The default [CoroutineDispatcher] that is used by all standard builders like * [launch][CoroutineScope.launch], [async][CoroutineScope.async], etc @@ -19,9 +18,27 @@ public object Dispatchers { * It is backed by a shared pool of threads on JVM. By default, the maximal number of threads used * by this dispatcher is equal to the number CPU cores, but is at least two. */ - @JvmField - public val Default: CoroutineDispatcher = - createDefaultDispatcher() + public val Default: CoroutineDispatcher + + /** + * A coroutine dispatcher that is confined to the Main thread operating with UI objects. + * Usually such dispatcher is single-threaded. + * + * Access to this property may throw [IllegalStateException] if no main dispatchers are present in the classpath. + * + * Depending on platform and classpath it can be mapped to different dispatchers: + * - On JS and Native it is equivalent of [Default] dispatcher. + * - On JVM it either Android main thread dispatcher, JavaFx or Swing EDT dispatcher. It is chosen by + * [`ServiceLoader`](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html). + * + * In order to work with `Main` dispatcher, following artifact should be added to project runtime dependencies: + * - `kotlinx-coroutines-android` for Android Main thread dispatcher + * - `kotlinx-coroutines-javafx` for JavaFx Application thread dispatcher + * - `kotlinx-coroutines-swing` for Swing EDT dispatcher + * + * Implementation note: [MainCoroutineDispatcher.immediate] is not supported on Native and JS platforms. + */ + public val Main: MainCoroutineDispatcher /** * A coroutine dispatcher that is not confined to any specific thread. @@ -39,8 +56,6 @@ public object Dispatchers { * **Note: This is an experimental api.** * Semantics, order of execution, and particular implementation details of this dispatcher may change in the future. */ - @JvmField @ExperimentalCoroutinesApi - public val Unconfined: CoroutineDispatcher = - kotlinx.coroutines.experimental.Unconfined -} \ No newline at end of file + public val Unconfined: CoroutineDispatcher +} diff --git a/common/kotlinx-coroutines-core-common/src/MainCoroutineDispatcher.kt b/common/kotlinx-coroutines-core-common/src/MainCoroutineDispatcher.kt new file mode 100644 index 0000000000..b0da263d29 --- /dev/null +++ b/common/kotlinx-coroutines-core-common/src/MainCoroutineDispatcher.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.experimental + +/** + * Base class for special [CoroutineDispatcher] which is confined to application "Main" or "UI" thread + * and used for any UI-based activities. Instance of `MainDispatcher` can be obtained by [Dispatchers.Main]. + * + * Platform may or may not provide instance of `MainDispatcher`, see documentation to [Dispatchers.Main] + */ +public abstract class MainCoroutineDispatcher : CoroutineDispatcher() { + + /** + * Returns dispatcher that executes coroutines immediately when it is already in the right context + * (e.g. current looper is the same as this handler's looper). See [isDispatchNeeded] documentation on + * why this should not be done by default. + * Method may throw [UnsupportedOperationException] if immediate dispatching is not supported by current dispatcher, + * please refer to specific dispatcher documentation. + * + * **Note: This is an experimental api.** Semantics of this dispatcher may change in the future. + */ + @ExperimentalCoroutinesApi + public abstract val immediate: MainCoroutineDispatcher +} diff --git a/common/kotlinx-coroutines-core-common/src/internal/MainDispatcherFactory.kt b/common/kotlinx-coroutines-core-common/src/internal/MainDispatcherFactory.kt new file mode 100644 index 0000000000..253804b75b --- /dev/null +++ b/common/kotlinx-coroutines-core-common/src/internal/MainDispatcherFactory.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.experimental.internal + +import kotlinx.coroutines.experimental.* + +@InternalCoroutinesApi // Emulating DI for Kotlin object's +public interface MainDispatcherFactory { + val loadPriority: Int get() = 0 // higher priority wins + + fun createDispatcher(): MainCoroutineDispatcher +} diff --git a/core/kotlinx-coroutines-core/src/Dispatchers.kt b/core/kotlinx-coroutines-core/src/Dispatchers.kt index c689b7e738..50396a9c50 100644 --- a/core/kotlinx-coroutines-core/src/Dispatchers.kt +++ b/core/kotlinx-coroutines-core/src/Dispatchers.kt @@ -6,7 +6,10 @@ package kotlinx.coroutines.experimental +import kotlinx.coroutines.experimental.internal.* import kotlinx.coroutines.experimental.scheduling.* +import java.util.* +import kotlin.coroutines.experimental.* /** * Name of the property that defines the maximal number of threads that are used by [Dispatchers.IO] coroutines dispatcher. @@ -14,16 +17,83 @@ import kotlinx.coroutines.experimental.scheduling.* public const val IO_PARALLELISM_PROPERTY_NAME = "kotlinx.coroutines.io.parallelism" /** - * The [CoroutineDispatcher] that is designed for offloading blocking IO tasks to a shared pool of threads. - * - * Additional threads in this pool are created and are shutdown on demand. - * The number of threads used by this dispatcher is limited by the value of - * "`kotlinx.coroutines.io.parallelism`" ([IO_PARALLELISM_PROPERTY_NAME]) system property. - * It defaults to the limit of 64 threads or the number of cores (whichever is larger). - * - * This dispatcher shares threads with a [Default][Dispatchers.Default] dispatcher, so using - * `withContext(Dispatchers.IO) { ... }` does not lead to an actual switching to another thread — - * typically execution continues in the same thread. + * Groups various implementations of [CoroutineDispatcher]. */ -public val Dispatchers.IO: CoroutineDispatcher - get() = DefaultScheduler.IO +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() + } + + /** + * The default [CoroutineDispatcher] that is used by all standard builders like + * [launch][CoroutineScope.launch], [async][CoroutineScope.async], etc + * if no dispatcher nor any other [ContinuationInterceptor] is specified in their context. + * + * It is backed by a shared pool of threads on JVM. By default, the maximal number of threads used + * by this dispatcher is equal to the number CPU cores, but is at least two. + */ + @JvmStatic + public actual val Default: CoroutineDispatcher = createDefaultDispatcher() + + /** + * A coroutine dispatcher that is confined to the Main thread operating with UI objects. + * Usually such dispatcher is single-threaded. + * + * Access to this property may throw [IllegalStateException] if no main thread dispatchers are present in the classpath. + * + * Depending on platform and classpath it can be mapped to different dispatchers: + * - On JS and Native it is equivalent of [Default] dispatcher. + * - On JVM it either Android main thread dispatcher, JavaFx or Swing EDT dispatcher. It is chosen by + * [`ServiceLoader`](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html). + * + * In order to work with `Main` dispatcher, following artifact should be added to project runtime dependencies: + * - `kotlinx-coroutines-android` for Android Main thread dispatcher + * - `kotlinx-coroutines-javafx` for JavaFx Application thread dispatcher + * - `kotlinx-coroutines-swing` for Swing EDT dispatcher + * + * 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'") + + /** + * A coroutine dispatcher that is not confined to any specific thread. + * It executes initial continuation of the coroutine _immediately_ in the current call-frame + * and lets the coroutine resume in whatever thread that is used by the corresponding suspending function, without + * mandating any specific threading policy. + * **Note: use with extreme caution, not for general code**. + * + * Note, that if you need your coroutine to be confined to a particular thread or a thread-pool after resumption, + * but still want to execute it in the current call-frame until its first suspension, then you can use + * an optional [CoroutineStart] parameter in coroutine builders like + * [launch][CoroutineScope.launch] and [async][CoroutineScope.async] setting it to the + * the value of [CoroutineStart.UNDISPATCHED]. + * + * **Note: This is an experimental api.** + * Semantics, order of execution, and particular implementation details of this dispatcher may change in the future. + */ + @JvmStatic + @ExperimentalCoroutinesApi + public actual val Unconfined: CoroutineDispatcher = kotlinx.coroutines.experimental.Unconfined + + /** + * The [CoroutineDispatcher] that is designed for offloading blocking IO tasks to a shared pool of threads. + * + * Additional threads in this pool are created and are shutdown on demand. + * The number of threads used by this dispatcher is limited by the value of + * "`kotlinx.coroutines.io.parallelism`" ([IO_PARALLELISM_PROPERTY_NAME]) system property. + * It defaults to the limit of 64 threads or the number of cores (whichever is larger). + * + * This dispatcher shares threads with a [Default][Dispatchers.Default] dispatcher, so using + * `withContext(Dispatchers.IO) { ... }` does not lead to an actual switching to another thread — + * typically execution continues in the same thread. + */ + @JvmStatic + public val IO: CoroutineDispatcher = DefaultScheduler.IO +} diff --git a/js/kotlinx-coroutines-core-js/src/Dispatchers.kt b/js/kotlinx-coroutines-core-js/src/Dispatchers.kt new file mode 100644 index 0000000000..86791995f1 --- /dev/null +++ b/js/kotlinx-coroutines-core-js/src/Dispatchers.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.experimental + +import kotlin.coroutines.experimental.* + +actual object Dispatchers { + + public actual val Default: CoroutineDispatcher = createDefaultDispatcher() + + public actual val Main: MainCoroutineDispatcher = JsMainDispatcher(Default) + + public actual val Unconfined: CoroutineDispatcher = kotlinx.coroutines.experimental.Unconfined +} + +private class JsMainDispatcher(val delegate: CoroutineDispatcher) : MainCoroutineDispatcher() { + + override val immediate: MainCoroutineDispatcher + get() = throw UnsupportedOperationException("Immediate dispatching is not supported on JS") + + override fun dispatch(context: CoroutineContext, block: Runnable) = delegate.dispatch(context, block) + + override fun isDispatchNeeded(context: CoroutineContext): Boolean = delegate.isDispatchNeeded(context) + + override fun dispatchYield(context: CoroutineContext, block: Runnable) = delegate.dispatchYield(context, block) + + override fun toString(): String = delegate.toString() +} diff --git a/native/kotlinx-coroutines-core-native/src/Dispatchers.kt b/native/kotlinx-coroutines-core-native/src/Dispatchers.kt new file mode 100644 index 0000000000..759743c895 --- /dev/null +++ b/native/kotlinx-coroutines-core-native/src/Dispatchers.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.experimental + +import kotlin.coroutines.experimental.* + + +actual object Dispatchers { + + public actual val Default: CoroutineDispatcher = createDefaultDispatcher() + + public actual val Main: MainCoroutineDispatcher = NativeMainDispatcher(Default) + + public actual val Unconfined: CoroutineDispatcher = kotlinx.coroutines.experimental.Unconfined +} + +private class NativeMainDispatcher(val delegate: CoroutineDispatcher) : MainCoroutineDispatcher() { + + override val immediate: MainCoroutineDispatcher + get() = throw UnsupportedOperationException("Immediate dispatching is not supported on Native") + + override fun dispatch(context: CoroutineContext, block: Runnable) = delegate.dispatch(context, block) + + override fun isDispatchNeeded(context: CoroutineContext): Boolean = delegate.isDispatchNeeded(context) + + override fun dispatchYield(context: CoroutineContext, block: Runnable) = delegate.dispatchYield(context, block) + + override fun toString(): String = delegate.toString() +} diff --git a/ui/kotlinx-coroutines-android/resources/META-INF/services/kotlinx.coroutines.experimental.internal.MainDispatcherFactory b/ui/kotlinx-coroutines-android/resources/META-INF/services/kotlinx.coroutines.experimental.internal.MainDispatcherFactory new file mode 100644 index 0000000000..b1b6654c67 --- /dev/null +++ b/ui/kotlinx-coroutines-android/resources/META-INF/services/kotlinx.coroutines.experimental.internal.MainDispatcherFactory @@ -0,0 +1 @@ +kotlinx.coroutines.experimental.android.AndroidDispatcherFactory diff --git a/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt b/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt index 1b2c4b1935..072cf484ce 100644 --- a/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt +++ b/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt @@ -10,21 +10,23 @@ import android.os.* import android.support.annotation.VisibleForTesting import android.view.* import kotlinx.coroutines.experimental.* +import kotlinx.coroutines.experimental.internal.MainDispatcherFactory import java.lang.reflect.Constructor import kotlin.coroutines.experimental.* /** * Dispatches execution onto Android main thread and provides native [delay][Delay.delay] support. */ +@Deprecated(level = DeprecationLevel.HIDDEN, message = "Deprecated in favor of Dispatchers property") public val Dispatchers.Main: HandlerDispatcher - get() = kotlinx.coroutines.experimental.android.Main + get() = MainDispatcher /** * Dispatches execution onto Android [Handler]. * * This class provides type-safety and a point for future extensions. */ -public sealed class HandlerDispatcher : CoroutineDispatcher(), Delay { +public sealed class HandlerDispatcher : MainCoroutineDispatcher(), Delay { /** * Returns dispatcher that executes coroutines immediately when it is already in the right handler context * (current looper is the same as this handler's looper). See [isDispatchNeeded] documentation on @@ -33,7 +35,11 @@ public sealed class HandlerDispatcher : CoroutineDispatcher(), Delay { * **Note: This is an experimental api.** Semantics of this dispatcher may change in the future. */ @ExperimentalCoroutinesApi - public abstract val immediate: HandlerDispatcher + public abstract override val immediate: HandlerDispatcher +} + +internal class AndroidDispatcherFactory : MainDispatcherFactory { + override fun createDispatcher(): MainCoroutineDispatcher = Main } /** @@ -76,6 +82,8 @@ internal fun Looper.asHandler(async: Boolean): Handler { @JvmField // this is for a nice Java API, see issue #255 internal val Main: HandlerDispatcher = HandlerContext(mainHandler, "Main") +private val MainDispatcher: HandlerDispatcher = Main // Alias + /** * Implements [CoroutineDispatcher] on top of an arbitrary Android [Handler]. * @suppress **Deprecated**: Use [HandlerDispatcher]. diff --git a/ui/kotlinx-coroutines-javafx/resources/META-INF/services/kotlinx.coroutines.experimental.internal.MainDispatcherFactory b/ui/kotlinx-coroutines-javafx/resources/META-INF/services/kotlinx.coroutines.experimental.internal.MainDispatcherFactory new file mode 100644 index 0000000000..b590694818 --- /dev/null +++ b/ui/kotlinx-coroutines-javafx/resources/META-INF/services/kotlinx.coroutines.experimental.internal.MainDispatcherFactory @@ -0,0 +1 @@ +kotlinx.coroutines.experimental.javafx.JavaFxDispatcherFactory diff --git a/ui/kotlinx-coroutines-javafx/src/JavaFxDispatcher.kt b/ui/kotlinx-coroutines-javafx/src/JavaFxDispatcher.kt index cc5ddf05a1..23c74cb19a 100644 --- a/ui/kotlinx-coroutines-javafx/src/JavaFxDispatcher.kt +++ b/ui/kotlinx-coroutines-javafx/src/JavaFxDispatcher.kt @@ -9,12 +9,14 @@ import javafx.application.* import javafx.event.* import javafx.util.* import kotlinx.coroutines.experimental.* +import kotlinx.coroutines.experimental.internal.* import java.util.concurrent.* import kotlin.coroutines.experimental.* /** * Dispatches execution onto JavaFx application thread and provides native [delay] support. */ +@Suppress("unused") public val Dispatchers.JavaFx: JavaFxDispatcher get() = kotlinx.coroutines.experimental.javafx.JavaFx @@ -23,7 +25,47 @@ public val Dispatchers.JavaFx: JavaFxDispatcher * * This class provides type-safety and a point for future extensions. */ -public sealed class JavaFxDispatcher : CoroutineDispatcher(), Delay +public sealed class JavaFxDispatcher : MainCoroutineDispatcher(), Delay { + + override fun dispatch(context: CoroutineContext, block: Runnable) = Platform.runLater(block) + + override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation) { + val timeline = schedule(timeMillis, TimeUnit.MILLISECONDS, EventHandler { + with(continuation) { resumeUndispatched(Unit) } + }) + continuation.invokeOnCancellation { timeline.stop() } + } + + override fun invokeOnTimeout(timeMillis: Long, block: Runnable): DisposableHandle { + val timeline = schedule(timeMillis, TimeUnit.MILLISECONDS, EventHandler { + block.run() + }) + return object : DisposableHandle { + override fun dispose() { + timeline.stop() + } + } + } + + private fun schedule(time: Long, unit: TimeUnit, handler: EventHandler): Timeline = + Timeline(KeyFrame(Duration.millis(unit.toMillis(time).toDouble()), handler)).apply { play() } +} + +internal class JavaFxDispatcherFactory : MainDispatcherFactory { + override fun createDispatcher(): MainCoroutineDispatcher = JavaFx + + override val loadPriority: Int + get() = 1 // Swing has 0 +} + +private object ImmediateJavaFxDispatcher : JavaFxDispatcher() { + override val immediate: MainCoroutineDispatcher + get() = this + + override fun isDispatchNeeded(context: CoroutineContext): Boolean = !Platform.isFxApplicationThread() + + override fun toString() = "JavaFx [immediate]" +} /** * Dispatches execution onto JavaFx application thread and provides native [delay] support. @@ -41,7 +83,8 @@ object JavaFx : JavaFxDispatcher() { initPlatform() } - override fun dispatch(context: CoroutineContext, block: Runnable) = Platform.runLater(block) + override val immediate: MainCoroutineDispatcher + get() = ImmediateJavaFxDispatcher /** * Suspends coroutine until next JavaFx pulse and returns time of the pulse on resumption. @@ -57,26 +100,6 @@ object JavaFx : JavaFxDispatcher() { suspend fun awaitPulse(): Long = kotlinx.coroutines.experimental.javafx.awaitPulse() - override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation) { - val timeline = schedule(timeMillis, TimeUnit.MILLISECONDS, EventHandler { - with(continuation) { resumeUndispatched(Unit) } - }) - continuation.invokeOnCancellation { timeline.stop() } - } - - override fun invokeOnTimeout(timeMillis: Long, block: Runnable): DisposableHandle { - val timeline = schedule(timeMillis, TimeUnit.MILLISECONDS, EventHandler { - block.run() - }) - return object : DisposableHandle { - override fun dispose() { - timeline.stop() - } - } - } - - private fun schedule(time: Long, unit: TimeUnit, handler: EventHandler): Timeline = - Timeline(KeyFrame(Duration.millis(unit.toMillis(time).toDouble()), handler)).apply { play() } override fun toString() = "JavaFx" } diff --git a/ui/kotlinx-coroutines-swing/resources/META-INF/services/kotlinx.coroutines.experimental.internal.MainDispatcherFactory b/ui/kotlinx-coroutines-swing/resources/META-INF/services/kotlinx.coroutines.experimental.internal.MainDispatcherFactory new file mode 100644 index 0000000000..59969a78b8 --- /dev/null +++ b/ui/kotlinx-coroutines-swing/resources/META-INF/services/kotlinx.coroutines.experimental.internal.MainDispatcherFactory @@ -0,0 +1 @@ +kotlinx.coroutines.experimental.swing.SwingDispatcherFactory diff --git a/ui/kotlinx-coroutines-swing/src/SwingDispatcher.kt b/ui/kotlinx-coroutines-swing/src/SwingDispatcher.kt index a78e2ed493..40f21c2ee3 100644 --- a/ui/kotlinx-coroutines-swing/src/SwingDispatcher.kt +++ b/ui/kotlinx-coroutines-swing/src/SwingDispatcher.kt @@ -4,7 +4,9 @@ package kotlinx.coroutines.experimental.swing +import javafx.application.* import kotlinx.coroutines.experimental.* +import kotlinx.coroutines.experimental.internal.* import java.awt.event.* import java.util.concurrent.* import javax.swing.* @@ -13,6 +15,7 @@ import kotlin.coroutines.experimental.* /** * Dispatches execution onto Swing event dispatching thread and provides native [delay] support. */ +@Suppress("unused") public val Dispatchers.Swing : SwingDispatcher get() = kotlinx.coroutines.experimental.swing.Swing @@ -21,19 +24,7 @@ public val Dispatchers.Swing : SwingDispatcher * * This class provides type-safety and a point for future extensions. */ -public sealed class SwingDispatcher : CoroutineDispatcher(), Delay - -/** - * Dispatches execution onto Swing event dispatching thread and provides native [delay] support. - * @suppress **Deprecated**: Use [Dispatchers.Swing]. - */ -@Deprecated( - message = "Use Dispatchers.Swing", - replaceWith = ReplaceWith("Dispatchers.Swing", - imports = ["kotlinx.coroutines.experimental.Dispatchers", "kotlinx.coroutines.experimental.swing.Swing"]) -) -// todo: it will become an internal implementation object -object Swing : SwingDispatcher() { +public sealed class SwingDispatcher : MainCoroutineDispatcher(), Delay { override fun dispatch(context: CoroutineContext, block: Runnable) = SwingUtilities.invokeLater(block) override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation) { @@ -59,6 +50,34 @@ object Swing : SwingDispatcher() { isRepeats = false start() } +} + +internal class SwingDispatcherFactory : MainDispatcherFactory { + override fun createDispatcher(): MainCoroutineDispatcher = Swing +} + +private object ImmediateSwingDispatcher : SwingDispatcher() { + override val immediate: MainCoroutineDispatcher + get() = this + + override fun isDispatchNeeded(context: CoroutineContext): Boolean = !Platform.isFxApplicationThread() + + override fun toString() = "Swing [immediate]" +} + +/** + * Dispatches execution onto Swing event dispatching thread and provides native [delay] support. + * @suppress **Deprecated**: Use [Dispatchers.Swing]. + */ +@Deprecated( + message = "Use Dispatchers.Swing", + replaceWith = ReplaceWith("Dispatchers.Swing", + imports = ["kotlinx.coroutines.experimental.Dispatchers", "kotlinx.coroutines.experimental.swing.Swing"]) +) +// todo: it will become an internal implementation object +object Swing : SwingDispatcher() { + override val immediate: MainCoroutineDispatcher + get() = ImmediateSwingDispatcher override fun toString() = "Swing" } From d53db42e12f2debcaf0b3adb43140d3347a86b5f Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Mon, 1 Oct 2018 17:27:53 +0300 Subject: [PATCH 2/3] Update readme --- README.md | 9 ++++++--- ui/README.md | 1 + ui/coroutines-guide-ui.md | 4 ++++ ui/kotlinx-coroutines-javafx/README.md | 4 ++-- ui/kotlinx-coroutines-swing/README.md | 4 ++-- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 17f7b5f7c6..cb4599d8ae 100644 --- a/README.md +++ b/README.md @@ -20,10 +20,13 @@ GlobalScope.launch { * [common](common/README.md) — common coroutines across all backends: * `launch` and `async` coroutine builders; - * `Job` and `Deferred` light-weight future with cancellation support; + * `Job` `Deferred` light-weight future with cancellation support; + *` Dispatchers.Main` for UI dispatcher for Android, Swing and JavaFx; * `delay` and `yield` top-level suspending functions; * `Channel` and `Mutex` communication and synchronization primitives; * `produce` and `actor` coroutine builders; + * `coroutineScope` and `supervisorScope` scope builders; + * `SupervisorJob` for supervision of coroutines hierarchies; * `select` expression support and more. * [core](core/README.md) — Kotlin/JVM implementation of common coroutines with additional features: * `Dispatchers.IO` dispatcher for blocking coroutines. @@ -39,8 +42,8 @@ GlobalScope.launch { ## Documentation * Presentations and videos: - * [Introduction to Coroutines](https://www.youtube.com/watch?v=_hfBv0a09Jc) (Roman Elizarov at KotlinConf 2017, [slides](https://www.slideshare.net/elizarov/introduction-to-coroutines-kotlinconf-2017)) - * [Deep dive into Coroutines](https://www.youtube.com/watch?v=YrrUCSi72E8) (Roman Elizarov at KotlinConf 2017, [slides](https://www.slideshare.net/elizarov/deep-dive-into-coroutines-on-jvm-kotlinconf-2017)) + * [Introduction to Coroutines](https://www.youtube.com/watch?v=_hfBv0a09Jc) (Roman Elizarov at KotlinConf 2017, [slides](https://www.slideshare.net/elizarov/introduction-to-coroutines-kotlinconf-2017)) + * [Deep dive into Coroutines](https://www.youtube.com/watch?v=YrrUCSi72E8) (Roman Elizarov at KotlinConf 2017, [slides](https://www.slideshare.net/elizarov/deep-dive-into-coroutines-on-jvm-kotlinconf-2017)) * Guides and manuals: * [Guide to kotlinx.coroutines by example](docs/coroutines-guide.md) (**read it first**) * [Guide to UI programming with coroutines](ui/coroutines-guide-ui.md) diff --git a/ui/README.md b/ui/README.md index db5b92d562..0417ff92e5 100644 --- a/ui/README.md +++ b/ui/README.md @@ -1,6 +1,7 @@ # Coroutines for UI This directory contains modules for coroutine programming with various single-threaded UI libraries. +After adding dependency to the UI library, corresponding UI dispatcher will be available via `Dispatchers.Main`. Module name below corresponds to the artifact name in Maven/Gradle. ## Modules diff --git a/ui/coroutines-guide-ui.md b/ui/coroutines-guide-ui.md index 4a757621a6..47409b9766 100644 --- a/ui/coroutines-guide-ui.md +++ b/ui/coroutines-guide-ui.md @@ -71,6 +71,10 @@ different UI application libraries: * [kotlinx-coroutines-javafx](kotlinx-coroutines-javafx) -- `Dispatchers.JavaFx` context for JavaFX UI applications. * [kotlinx-coroutines-swing](kotlinx-coroutines-swing) -- `Dispatchers.Swing` context for Swing UI applications. +Also, UI dispatcher is available via `Dispatchers.Main` from `kotlinx-coroutines-core` and corresponding +implementation (Android, JavaFx or Swing) is discovered by [`ServiceLoader`](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html) API. +For example, if you are writing JavaFx application, you can use either `Dispatchers.Main` or `Dispachers.JavaFx` extension, it will be the same object. + This guide covers all UI libraries simultaneously, because each of these modules consists of just one object definition that is a couple of pages long. You can use any of them as an example to write the corresponding context object for your favourite UI library, even if it is not included out of the box here. diff --git a/ui/kotlinx-coroutines-javafx/README.md b/ui/kotlinx-coroutines-javafx/README.md index 32a8aa22ef..de763aba30 100644 --- a/ui/kotlinx-coroutines-javafx/README.md +++ b/ui/kotlinx-coroutines-javafx/README.md @@ -1,10 +1,10 @@ # Module kotlinx-coroutines-javafx -Provides `Dispatchers.JavaFx` context for JavaFX UI applications. +Provides `Dispatchers.JavaFx` context and `Dispatchers.Main` implementation for JavaFX UI applications. Read [Guide to UI programming with coroutines](https://github.com/Kotlin/kotlinx.coroutines/blob/master/ui/coroutines-guide-ui.md) for tutorial on this module. # Package kotlinx.coroutines.experimental.javafx -Provides `Dispatchers.JavaFx` context for JavaFX UI applications. +Provides `Dispatchers.JavaFx` context and `Dispatchers.Main` implementation for JavaFX UI applications. diff --git a/ui/kotlinx-coroutines-swing/README.md b/ui/kotlinx-coroutines-swing/README.md index dbd15d7bc1..140da35fdd 100644 --- a/ui/kotlinx-coroutines-swing/README.md +++ b/ui/kotlinx-coroutines-swing/README.md @@ -1,10 +1,10 @@ # Module kotlinx-coroutines-swing -Provides `Dispatchers.Swing` context for Swing UI applications. +Provides `Dispatchers.Swing` context and `Dispatchers.Main` implementation for Swing UI applications. Read [Guide to UI programming with coroutines](https://github.com/Kotlin/kotlinx.coroutines/blob/master/ui/coroutines-guide-ui.md) for tutorial on this module. # Package kotlinx.coroutines.experimental.swing -Provides `Dispatchers.Swing` context for Swing UI applications. +Provides `Dispatchers.Swing` context and `Dispatchers.Main` implementation for Swing UI applications. From b5d61573454ab332dd842034606988d4324d6218 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Mon, 1 Oct 2018 18:30:01 +0300 Subject: [PATCH 3/3] ~: squash me --- README.md | 2 +- .../src/internal/MainDispatcherFactory.kt | 2 +- ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt | 6 +++++- ui/kotlinx-coroutines-swing/src/SwingDispatcher.kt | 5 ++++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cb4599d8ae..695364c740 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ GlobalScope.launch { * [common](common/README.md) — common coroutines across all backends: * `launch` and `async` coroutine builders; - * `Job` `Deferred` light-weight future with cancellation support; + * `Job` and `Deferred` light-weight future with cancellation support; *` Dispatchers.Main` for UI dispatcher for Android, Swing and JavaFx; * `delay` and `yield` top-level suspending functions; * `Channel` and `Mutex` communication and synchronization primitives; diff --git a/common/kotlinx-coroutines-core-common/src/internal/MainDispatcherFactory.kt b/common/kotlinx-coroutines-core-common/src/internal/MainDispatcherFactory.kt index 253804b75b..60778493f8 100644 --- a/common/kotlinx-coroutines-core-common/src/internal/MainDispatcherFactory.kt +++ b/common/kotlinx-coroutines-core-common/src/internal/MainDispatcherFactory.kt @@ -8,7 +8,7 @@ import kotlinx.coroutines.experimental.* @InternalCoroutinesApi // Emulating DI for Kotlin object's public interface MainDispatcherFactory { - val loadPriority: Int get() = 0 // higher priority wins + val loadPriority: Int // higher priority wins fun createDispatcher(): MainCoroutineDispatcher } diff --git a/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt b/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt index 072cf484ce..dd27bba84a 100644 --- a/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt +++ b/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt @@ -7,7 +7,7 @@ package kotlinx.coroutines.experimental.android import android.os.* -import android.support.annotation.VisibleForTesting +import android.support.annotation.* import android.view.* import kotlinx.coroutines.experimental.* import kotlinx.coroutines.experimental.internal.MainDispatcherFactory @@ -38,8 +38,12 @@ public sealed class HandlerDispatcher : MainCoroutineDispatcher(), Delay { public abstract override val immediate: HandlerDispatcher } +@Keep internal class AndroidDispatcherFactory : MainDispatcherFactory { override fun createDispatcher(): MainCoroutineDispatcher = Main + + override val loadPriority: Int + get() = Int.MAX_VALUE } /** diff --git a/ui/kotlinx-coroutines-swing/src/SwingDispatcher.kt b/ui/kotlinx-coroutines-swing/src/SwingDispatcher.kt index 40f21c2ee3..71dadb808a 100644 --- a/ui/kotlinx-coroutines-swing/src/SwingDispatcher.kt +++ b/ui/kotlinx-coroutines-swing/src/SwingDispatcher.kt @@ -53,6 +53,9 @@ public sealed class SwingDispatcher : MainCoroutineDispatcher(), Delay { } internal class SwingDispatcherFactory : MainDispatcherFactory { + override val loadPriority: Int + get() = 0 + override fun createDispatcher(): MainCoroutineDispatcher = Swing } @@ -60,7 +63,7 @@ private object ImmediateSwingDispatcher : SwingDispatcher() { override val immediate: MainCoroutineDispatcher get() = this - override fun isDispatchNeeded(context: CoroutineContext): Boolean = !Platform.isFxApplicationThread() + override fun isDispatchNeeded(context: CoroutineContext): Boolean = !SwingUtilities.isEventDispatchThread() override fun toString() = "Swing [immediate]" }