Skip to content

Move UI dispatcher to common Dispatchers.Main #641

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@ 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;
*` 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.
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -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;
}

Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 <init> ()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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down
33 changes: 24 additions & 9 deletions common/kotlinx-coroutines-core-common/src/Dispatchers.common.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand All @@ -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
}
public val Unconfined: CoroutineDispatcher
}
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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 // higher priority wins

fun createDispatcher(): MainCoroutineDispatcher
}
94 changes: 82 additions & 12 deletions core/kotlinx-coroutines-core/src/Dispatchers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,94 @@

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.
*/
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 &mdash;
* 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 &mdash;
* typically execution continues in the same thread.
*/
@JvmStatic
public val IO: CoroutineDispatcher = DefaultScheduler.IO
}
Loading