Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 1 | package kotlinx.coroutines.experimental |
| 2 | |
| 3 | import kotlin.coroutines.AbstractCoroutineContextElement |
| 4 | import kotlin.coroutines.Continuation |
| 5 | import kotlin.coroutines.ContinuationInterceptor |
Roman Elizarov | 67891d8 | 2017-01-23 16:47:20 +0300 | [diff] [blame^] | 6 | import kotlin.coroutines.CoroutineContext |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 7 | |
| 8 | /** |
Roman Elizarov | ed7b864 | 2017-01-19 11:22:28 +0300 | [diff] [blame] | 9 | * Base class that shall be extended by all coroutine dispatcher implementations. |
| 10 | * |
| 11 | * The following standard implementations are provided by `kotlinx.coroutines`: |
| 12 | * * [Here] -- starts coroutine execution _right here_ in the current call-frame until the first suspension. On first |
| 13 | * suspension the coroutine builder function returns. The coroutine will resume in whatever thread that is used by the |
| 14 | * corresponding suspending function, without mandating any specific threading policy. |
| 15 | * This in an appropriate choice for IO-intensive coroutines that do not consume CPU resources. |
| 16 | * * [CommonPool] -- immediately returns from the coroutine builder and schedules coroutine execution to |
| 17 | * a common pool of shared background threads. |
| 18 | * This is an appropriate choice for compute-intensive coroutines that consume a lot of CPU resources. |
| 19 | * * Private thread pools can be created with [newSingleThreadContext] and [newFixedThreadPoolContext]. |
| 20 | * * [currentCoroutineContext] -- inherits the context of the parent coroutine, |
| 21 | * but throws [IllegalStateException] if used outside of coroutine. Use [currentCoroutineContextOrDefault] |
| 22 | * if a default is needed when outside of coroutine. |
| 23 | * This is an appropriate choice for libraries that need to inherit parent coroutine context. |
| 24 | * * There are context implementations for UI libraries like `Swing` and `JavaFx` in separate modules. |
| 25 | * |
| 26 | * This class ensures that [currentCoroutineContext] is correctly transferred to a new thread and that |
| 27 | * debugging facilities in [newCoroutineContext] function work properly. |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 28 | */ |
| 29 | public abstract class CoroutineDispatcher : |
| 30 | AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor { |
| 31 | /** |
| 32 | * Return `true` if execution shall be dispatched onto another thread. |
| 33 | */ |
Roman Elizarov | 67891d8 | 2017-01-23 16:47:20 +0300 | [diff] [blame^] | 34 | public abstract fun isDispatchNeeded(context: CoroutineContext): Boolean |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 35 | |
| 36 | /** |
Roman Elizarov | 67891d8 | 2017-01-23 16:47:20 +0300 | [diff] [blame^] | 37 | * Dispatches execution of a runnable [block] onto another thread in the given [context]. |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 38 | */ |
Roman Elizarov | 67891d8 | 2017-01-23 16:47:20 +0300 | [diff] [blame^] | 39 | public abstract fun dispatch(context: CoroutineContext, block: Runnable) |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 40 | |
| 41 | override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> = |
| 42 | DispatchedContinuation<T>(this, continuation) |
| 43 | } |
| 44 | |
| 45 | private class DispatchedContinuation<T>( |
| 46 | val dispatcher: CoroutineDispatcher, |
| 47 | val continuation: Continuation<T> |
| 48 | ): Continuation<T> by continuation { |
| 49 | override fun resume(value: T) { |
Roman Elizarov | 67891d8 | 2017-01-23 16:47:20 +0300 | [diff] [blame^] | 50 | val context = continuation.context |
| 51 | if (dispatcher.isDispatchNeeded(context)) |
| 52 | dispatcher.dispatch(context, Runnable { |
| 53 | withDefaultCoroutineContext(context) { |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 54 | continuation.resume(value) |
| 55 | } |
| 56 | }) |
| 57 | else |
Roman Elizarov | 67891d8 | 2017-01-23 16:47:20 +0300 | [diff] [blame^] | 58 | withDefaultCoroutineContext(context) { |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 59 | continuation.resume(value) |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | override fun resumeWithException(exception: Throwable) { |
Roman Elizarov | 67891d8 | 2017-01-23 16:47:20 +0300 | [diff] [blame^] | 64 | val context = continuation.context |
| 65 | if (dispatcher.isDispatchNeeded(context)) |
| 66 | dispatcher.dispatch(context, Runnable { |
| 67 | withDefaultCoroutineContext(context) { |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 68 | continuation.resumeWithException(exception) |
| 69 | } |
| 70 | }) |
| 71 | else |
Roman Elizarov | 67891d8 | 2017-01-23 16:47:20 +0300 | [diff] [blame^] | 72 | withDefaultCoroutineContext(context) { |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 73 | continuation.resumeWithException(exception) |
| 74 | } |
| 75 | } |
| 76 | } |