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 a277169065..37ba315549 100644 --- a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-core.txt +++ b/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-core.txt @@ -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; } diff --git a/kotlinx-coroutines-core/jvm/src/Executors.kt b/kotlinx-coroutines-core/jvm/src/Executors.kt index c5ce53724b..7d7f4ba7be 100644 --- a/kotlinx-coroutines-core/jvm/src/Executors.kt +++ b/kotlinx-coroutines-core/jvm/src/Executors.kt @@ -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 @@ -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 { diff --git a/kotlinx-coroutines-core/jvm/test/ExecutorsTest.kt b/kotlinx-coroutines-core/jvm/test/ExecutorsTest.kt index a959b4ebb4..2cf4361867 100644 --- a/kotlinx-coroutines-core/jvm/test/ExecutorsTest.kt +++ b/kotlinx-coroutines-core/jvm/test/ExecutorsTest.kt @@ -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) { @@ -32,7 +34,7 @@ class ExecutorsTest : TestBase() { } @Test - fun testToExecutor() { + fun testExecutorToDispatcher() { val executor = Executors.newSingleThreadExecutor { r -> Thread(r, "TestExecutor") } runBlocking(executor.asCoroutineDispatcher()) { checkThreadName("TestExecutor") @@ -40,6 +42,41 @@ class ExecutorsTest : TestBase() { 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")