Skip to content

Commit

Permalink
Introduce CoroutineDispatcher.asExecutor() extension (#1457)
Browse files Browse the repository at this point in the history
Fixes #1450
  • Loading branch information
elizarov authored and qwwdfsad committed Aug 22, 2019
1 parent 9b7c03c commit bcf4a8c
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ public abstract class kotlinx/coroutines/ExecutorCoroutineDispatcher : kotlinx/c
}

public final class kotlinx/coroutines/ExecutorsKt {
public static final fun asExecutor (Lkotlinx/coroutines/CoroutineDispatcher;)Ljava/util/concurrent/Executor;
public static final fun from (Ljava/util/concurrent/Executor;)Lkotlinx/coroutines/CoroutineDispatcher;
public static final fun from (Ljava/util/concurrent/ExecutorService;)Lkotlinx/coroutines/ExecutorCoroutineDispatcher;
}
Expand Down
20 changes: 16 additions & 4 deletions kotlinx-coroutines-core/jvm/src/Executors.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
* Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/

package kotlinx.coroutines
Expand Down Expand Up @@ -35,15 +35,27 @@ public abstract class ExecutorCoroutineDispatcher: CoroutineDispatcher(), Closea
*/
@JvmName("from") // this is for a nice Java API, see issue #255
public fun ExecutorService.asCoroutineDispatcher(): ExecutorCoroutineDispatcher =
// we know that an implementation of Executor.asCoroutineDispatcher actually returns a closeable one
(this as Executor).asCoroutineDispatcher() as ExecutorCoroutineDispatcher
ExecutorCoroutineDispatcherImpl(this)

/**
* Converts an instance of [Executor] to an implementation of [CoroutineDispatcher].
*/
@JvmName("from") // this is for a nice Java API, see issue #255
public fun Executor.asCoroutineDispatcher(): CoroutineDispatcher =
ExecutorCoroutineDispatcherImpl(this)
(this as? DispatcherExecutor)?.dispatcher ?: ExecutorCoroutineDispatcherImpl(this)

/**
* Converts an instance of [CoroutineDispatcher] to an implementation of [Executor].
*
* It returns the original executor when used on the result of [Executor.asCoroutineDispatcher] extensions.
*/
public fun CoroutineDispatcher.asExecutor(): Executor =
(this as? ExecutorCoroutineDispatcher)?.executor ?: DispatcherExecutor(this)

private class DispatcherExecutor(@JvmField val dispatcher: CoroutineDispatcher) : Executor {
override fun execute(block: Runnable) = dispatcher.dispatch(EmptyCoroutineContext, block)
override fun toString(): String = dispatcher.toString()
}

private class ExecutorCoroutineDispatcherImpl(override val executor: Executor) : ExecutorCoroutineDispatcherBase() {
init {
Expand Down
45 changes: 41 additions & 4 deletions kotlinx-coroutines-core/jvm/test/ExecutorsTest.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
/*
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
* Copyright 2016-2019 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.util.concurrent.Executors
import org.junit.*
import org.junit.Assert.*
import java.util.concurrent.*
import kotlin.coroutines.*

class ExecutorsTest : TestBase() {
private fun checkThreadName(prefix: String) {
Expand All @@ -32,14 +34,49 @@ class ExecutorsTest : TestBase() {
}

@Test
fun testToExecutor() {
fun testExecutorToDispatcher() {
val executor = Executors.newSingleThreadExecutor { r -> Thread(r, "TestExecutor") }
runBlocking(executor.asCoroutineDispatcher()) {
checkThreadName("TestExecutor")
}
executor.shutdown()
}

@Test
fun testConvertedDispatcherToExecutor() {
val executor: ExecutorService = Executors.newSingleThreadExecutor { r -> Thread(r, "TestExecutor") }
val dispatcher: CoroutineDispatcher = executor.asCoroutineDispatcher()
assertSame(executor, dispatcher.asExecutor())
executor.shutdown()
}

@Test
fun testDefaultDispatcherToExecutor() {
val latch = CountDownLatch(1)
Dispatchers.Default.asExecutor().execute {
checkThreadName("DefaultDispatcher")
latch.countDown()
}
latch.await()
}

@Test
fun testCustomDispatcherToExecutor() {
expect(1)
val dispatcher = object : CoroutineDispatcher() {
override fun dispatch(context: CoroutineContext, block: Runnable) {
expect(2)
block.run()
}
}
val executor = dispatcher.asExecutor()
assertSame(dispatcher, executor.asCoroutineDispatcher())
executor.execute {
expect(3)
}
finish(4)
}

@Test
fun testTwoThreads() {
val ctx1 = newSingleThreadContext("Ctx1")
Expand Down

0 comments on commit bcf4a8c

Please sign in to comment.