Skip to content

Commit f3868a7

Browse files
committed
Introduce CoroutineDispatcher.asExecutor() extension
Fixes #1450
1 parent fbaedc4 commit f3868a7

File tree

3 files changed

+58
-8
lines changed

3 files changed

+58
-8
lines changed

binary-compatibility-validator/reference-public-api/kotlinx-coroutines-core.txt

+1
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ public abstract class kotlinx/coroutines/ExecutorCoroutineDispatcher : kotlinx/c
293293
}
294294

295295
public final class kotlinx/coroutines/ExecutorsKt {
296+
public static final fun asExecutor (Lkotlinx/coroutines/CoroutineDispatcher;)Ljava/util/concurrent/Executor;
296297
public static final fun from (Ljava/util/concurrent/Executor;)Lkotlinx/coroutines/CoroutineDispatcher;
297298
public static final fun from (Ljava/util/concurrent/ExecutorService;)Lkotlinx/coroutines/ExecutorCoroutineDispatcher;
298299
}

kotlinx-coroutines-core/jvm/src/Executors.kt

+16-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
2+
* Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

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

4140
/**
4241
* Converts an instance of [Executor] to an implementation of [CoroutineDispatcher].
4342
*/
4443
@JvmName("from") // this is for a nice Java API, see issue #255
4544
public fun Executor.asCoroutineDispatcher(): CoroutineDispatcher =
46-
ExecutorCoroutineDispatcherImpl(this)
45+
(this as? DispatcherExecutor)?.dispatcher ?: ExecutorCoroutineDispatcherImpl(this)
46+
47+
/**
48+
* Converts an instance of [CoroutineDispatcher] to an implementation of [Executor].
49+
*
50+
* It returns an original executor when used on the result of [Executor.asCoroutineDispatcher] extensions.
51+
*/
52+
public fun CoroutineDispatcher.asExecutor(): Executor =
53+
(this as? ExecutorCoroutineDispatcher)?.executor ?: DispatcherExecutor(this)
54+
55+
private class DispatcherExecutor(@JvmField val dispatcher: CoroutineDispatcher) : Executor {
56+
override fun execute(block: Runnable) = dispatcher.dispatch(EmptyCoroutineContext, block)
57+
override fun toString(): String = dispatcher.toString()
58+
}
4759

4860
private class ExecutorCoroutineDispatcherImpl(override val executor: Executor) : ExecutorCoroutineDispatcherBase() {
4961
init {

kotlinx-coroutines-core/jvm/test/ExecutorsTest.kt

+41-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
/*
2-
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
2+
* Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

55
package kotlinx.coroutines
66

7-
import org.junit.Test
8-
import java.util.concurrent.Executors
7+
import org.junit.*
8+
import org.junit.Assert.*
9+
import java.util.concurrent.*
10+
import kotlin.coroutines.*
911

1012
class ExecutorsTest : TestBase() {
1113
private fun checkThreadName(prefix: String) {
@@ -32,14 +34,49 @@ class ExecutorsTest : TestBase() {
3234
}
3335

3436
@Test
35-
fun testToExecutor() {
37+
fun testExecutorToDispatcher() {
3638
val executor = Executors.newSingleThreadExecutor { r -> Thread(r, "TestExecutor") }
3739
runBlocking(executor.asCoroutineDispatcher()) {
3840
checkThreadName("TestExecutor")
3941
}
4042
executor.shutdown()
4143
}
4244

45+
@Test
46+
fun testConvertedDispatcherToExecutor() {
47+
val executor: ExecutorService = Executors.newSingleThreadExecutor { r -> Thread(r, "TestExecutor") }
48+
val dispatcher: CoroutineDispatcher = executor.asCoroutineDispatcher()
49+
assertSame(executor, dispatcher.asExecutor())
50+
executor.shutdown()
51+
}
52+
53+
@Test
54+
fun testDefaultDispatcherToExecutor() {
55+
val latch = CountDownLatch(2)
56+
Dispatchers.Default.asExecutor().execute {
57+
checkThreadName("DefaultDispatcher")
58+
latch.countDown()
59+
}
60+
latch.countDown()
61+
}
62+
63+
@Test
64+
fun testCustomDispatcherToExecutor() {
65+
expect(1)
66+
val dispatcher = object : CoroutineDispatcher() {
67+
override fun dispatch(context: CoroutineContext, block: Runnable) {
68+
expect(2)
69+
block.run()
70+
}
71+
}
72+
val executor = dispatcher.asExecutor()
73+
assertSame(dispatcher, executor.asCoroutineDispatcher())
74+
executor.execute {
75+
expect(3)
76+
}
77+
finish(4)
78+
}
79+
4380
@Test
4481
fun testTwoThreads() {
4582
val ctx1 = newSingleThreadContext("Ctx1")

0 commit comments

Comments
 (0)