From 361c78150e054f5ef6dd9ed7f4d9fee52fb42618 Mon Sep 17 00:00:00 2001 From: Roman Elizarov Date: Wed, 6 Feb 2019 13:19:28 +0300 Subject: [PATCH 1/3] CompletableJob support added Fixes #607 --- .../kotlinx-coroutines-core.txt | 22 ++++++++- .../common/src/CompletableDeferred.kt | 3 ++ .../common/src/CompletableJob.kt | 30 ++++++++++++ kotlinx-coroutines-core/common/src/Job.kt | 13 ++++- .../common/src/JobSupport.kt | 5 +- .../common/src/Supervisor.kt | 14 +++--- .../common/test/CompletableJobTest.kt | 49 +++++++++++++++++++ 7 files changed, 126 insertions(+), 10 deletions(-) create mode 100644 kotlinx-coroutines-core/common/src/CompletableJob.kt create mode 100644 kotlinx-coroutines-core/common/test/CompletableJobTest.kt 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 18041f72d0..6e9c3798f1 100644 --- a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-core.txt +++ b/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-core.txt @@ -117,6 +117,20 @@ public final class kotlinx/coroutines/CompletableDeferredKt { public static synthetic fun CompletableDeferred$default (Lkotlinx/coroutines/Job;ILjava/lang/Object;)Lkotlinx/coroutines/CompletableDeferred; } +public abstract interface class kotlinx/coroutines/CompletableJob : kotlinx/coroutines/Job { + public abstract fun complete ()Z + public abstract fun completeExceptionally (Ljava/lang/Throwable;)Z +} + +public final class kotlinx/coroutines/CompletableJob$DefaultImpls { + public static synthetic fun cancel (Lkotlinx/coroutines/CompletableJob;)Z + public static fun fold (Lkotlinx/coroutines/CompletableJob;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public static fun get (Lkotlinx/coroutines/CompletableJob;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; + public static fun minusKey (Lkotlinx/coroutines/CompletableJob;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; + public static fun plus (Lkotlinx/coroutines/CompletableJob;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; + public static fun plus (Lkotlinx/coroutines/CompletableJob;Lkotlinx/coroutines/Job;)Lkotlinx/coroutines/Job; +} + public final class kotlinx/coroutines/CompletionHandlerException : java/lang/RuntimeException { public fun (Ljava/lang/String;Ljava/lang/Throwable;)V } @@ -315,7 +329,9 @@ public final class kotlinx/coroutines/Job$Key : kotlin/coroutines/CoroutineConte public final class kotlinx/coroutines/JobKt { public static final fun DisposableHandle (Lkotlin/jvm/functions/Function0;)Lkotlinx/coroutines/DisposableHandle; - public static final fun Job (Lkotlinx/coroutines/Job;)Lkotlinx/coroutines/Job; + public static final fun Job (Lkotlinx/coroutines/Job;)Lkotlinx/coroutines/CompletableJob; + public static final synthetic fun Job (Lkotlinx/coroutines/Job;)Lkotlinx/coroutines/Job; + public static synthetic fun Job$default (Lkotlinx/coroutines/Job;ILjava/lang/Object;)Lkotlinx/coroutines/CompletableJob; public static synthetic fun Job$default (Lkotlinx/coroutines/Job;ILjava/lang/Object;)Lkotlinx/coroutines/Job; public static final fun cancel (Lkotlin/coroutines/CoroutineContext;)V public static final synthetic fun cancel (Lkotlin/coroutines/CoroutineContext;)Z @@ -422,7 +438,9 @@ public final class kotlinx/coroutines/RunnableKt { } public final class kotlinx/coroutines/SupervisorKt { - public static final fun SupervisorJob (Lkotlinx/coroutines/Job;)Lkotlinx/coroutines/Job; + public static final fun SupervisorJob (Lkotlinx/coroutines/Job;)Lkotlinx/coroutines/CompletableJob; + public static final synthetic fun SupervisorJob (Lkotlinx/coroutines/Job;)Lkotlinx/coroutines/Job; + public static synthetic fun SupervisorJob$default (Lkotlinx/coroutines/Job;ILjava/lang/Object;)Lkotlinx/coroutines/CompletableJob; public static synthetic fun SupervisorJob$default (Lkotlinx/coroutines/Job;ILjava/lang/Object;)Lkotlinx/coroutines/Job; public static final fun supervisorScope (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } diff --git a/kotlinx-coroutines-core/common/src/CompletableDeferred.kt b/kotlinx-coroutines-core/common/src/CompletableDeferred.kt index b7f821ba6d..9cc90e4434 100644 --- a/kotlinx-coroutines-core/common/src/CompletableDeferred.kt +++ b/kotlinx-coroutines-core/common/src/CompletableDeferred.kt @@ -25,6 +25,9 @@ public interface CompletableDeferred : Deferred { * completed as a result of this invocation and `false` otherwise (if it was already completed). * * Repeated invocations of this function have no effect and always produce `false`. + * + * Note, that if this deferred has children, then it transitions into _completing_ state and becomes _complete_ + * once all its children are _complete_. See [Job] for details. */ public fun complete(value: T): Boolean diff --git a/kotlinx-coroutines-core/common/src/CompletableJob.kt b/kotlinx-coroutines-core/common/src/CompletableJob.kt new file mode 100644 index 0000000000..d752dcda75 --- /dev/null +++ b/kotlinx-coroutines-core/common/src/CompletableJob.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines + +/** + * A job that can be completed using [complete()] function. + * It is returned by [Job()][Job] and [SupervisorJob()][SupervisorJob] constructor functions. + */ +public interface CompletableJob : Job { + /** + * Completes this job. The result is `true` if this job was completed as a result of this invocation and + * `false` otherwise (if it was already completed). + * + * Repeated invocations of this function have no effect and always produce `false`. + * + * Note, that if this job has children, then it transitions into _completing_ state and becomes _complete_ + * once all its children are _complete_. See [Job] for details. + */ + public fun complete(): Boolean + + /** + * Completes this job exceptionally with a given [exception]. The result is `true` if this job was + * completed as a result of this invocation and `false` otherwise (if it was already completed). + * + * Repeated invocations of this function have no effect and always produce `false`. + */ + public fun completeExceptionally(exception: Throwable): Boolean +} \ No newline at end of file diff --git a/kotlinx-coroutines-core/common/src/Job.kt b/kotlinx-coroutines-core/common/src/Job.kt index 31fe6e1d55..2dd65bdf36 100644 --- a/kotlinx-coroutines-core/common/src/Job.kt +++ b/kotlinx-coroutines-core/common/src/Job.kt @@ -348,10 +348,21 @@ public interface Job : CoroutineContext.Element { * is cancelled when its parent fails or is cancelled. All this job's children are cancelled in this case, too. * The invocation of [cancel][Job.cancel] with exception (other than [CancellationException]) on this job also cancels parent. * + * Conceptually, the resulting job works in the same way as the job created by the `launch { body }` invocation + * (see [launch]), but without any code in the body. It is active until cancelled or completed. Invocation of + * [CompletableJob.complete] or [CompletableJob.completeExceptionally] corresponds to the successful or + * failed completion of the body of the coroutine. + * * @param parent an optional parent job. */ @Suppress("FunctionName") -public fun Job(parent: Job? = null): Job = JobImpl(parent) +public fun Job(parent: Job? = null): CompletableJob = JobImpl(parent) + +/** @suppress Binary compatibility only */ +@Suppress("FunctionName") +@Deprecated(level = DeprecationLevel.HIDDEN, message = "Binary compatibility") +@JvmName("Job") +public fun Job0(parent: Job? = null): Job = Job(parent) /** * A handle to an allocated object that can be disposed to make it eligible for garbage collection. diff --git a/kotlinx-coroutines-core/common/src/JobSupport.kt b/kotlinx-coroutines-core/common/src/JobSupport.kt index 8504f84c6b..3a3dd3b2ca 100644 --- a/kotlinx-coroutines-core/common/src/JobSupport.kt +++ b/kotlinx-coroutines-core/common/src/JobSupport.kt @@ -1182,11 +1182,14 @@ private class Empty(override val isActive: Boolean) : Incomplete { override fun toString(): String = "Empty{${if (isActive) "Active" else "New" }}" } -internal class JobImpl(parent: Job? = null) : JobSupport(true) { +internal open class JobImpl(parent: Job?) : JobSupport(true), CompletableJob { init { initParentJobInternal(parent) } override val cancelsParent: Boolean get() = true override val onCancelComplete get() = true override val handlesException: Boolean get() = false + override fun complete() = makeCompleting(Unit) + override fun completeExceptionally(exception: Throwable): Boolean = + makeCompleting(CompletedExceptionally(exception)) } // -------- invokeOnCompletion nodes diff --git a/kotlinx-coroutines-core/common/src/Supervisor.kt b/kotlinx-coroutines-core/common/src/Supervisor.kt index f150737952..cbc1bc818e 100644 --- a/kotlinx-coroutines-core/common/src/Supervisor.kt +++ b/kotlinx-coroutines-core/common/src/Supervisor.kt @@ -28,7 +28,13 @@ import kotlin.jvm.* * @param parent an optional parent job. */ @Suppress("FunctionName") -public fun SupervisorJob(parent: Job? = null) : Job = SupervisorJobImpl(parent) +public fun SupervisorJob(parent: Job? = null) : CompletableJob = SupervisorJobImpl(parent) + +/** @suppress Binary compatibility only */ +@Suppress("FunctionName") +@Deprecated(level = DeprecationLevel.HIDDEN, message = "Binary compatibility") +@JvmName("SupervisorJob") +public fun SupervisorJob0(parent: Job? = null) : Job = SupervisorJob(parent) /** * Creates new [CoroutineScope] with [SupervisorJob] and calls the specified suspend block with this scope. @@ -46,11 +52,7 @@ public suspend fun supervisorScope(block: suspend CoroutineScope.() -> R): coroutine.startUndispatchedOrReturn(coroutine, block) } -private class SupervisorJobImpl(parent: Job?) : JobSupport(true) { - init { initParentJobInternal(parent) } - override val cancelsParent: Boolean get() = true - override val onCancelComplete get() = true - override val handlesException: Boolean get() = false +private class SupervisorJobImpl(parent: Job?) : JobImpl(parent) { override fun childCancelled(cause: Throwable): Boolean = false } diff --git a/kotlinx-coroutines-core/common/test/CompletableJobTest.kt b/kotlinx-coroutines-core/common/test/CompletableJobTest.kt new file mode 100644 index 0000000000..16686a70d3 --- /dev/null +++ b/kotlinx-coroutines-core/common/test/CompletableJobTest.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines + +import kotlin.test.* + +class CompletableJobTest { + @Test + fun testComplete() { + val job = Job() + assertTrue(job.isActive) + assertFalse(job.isCompleted) + assertTrue(job.complete()) + assertTrue(job.isCompleted) + assertFalse(job.isActive) + assertFalse(job.isCancelled) + assertFalse(job.complete()) + } + + @Test + fun testCompleteWithException() { + val job = Job() + assertTrue(job.isActive) + assertFalse(job.isCompleted) + assertTrue(job.completeExceptionally(TestException())) + assertTrue(job.isCompleted) + assertFalse(job.isActive) + assertTrue(job.isCancelled) + assertFalse(job.completeExceptionally(TestException())) + assertFalse(job.complete()) + } + + @Test + fun testCompleteWithChildren() { + val parent = Job() + val child = Job(parent) + assertTrue(parent.complete()) + assertFalse(parent.complete()) + assertTrue(parent.isActive) + assertFalse(parent.isCompleted) + assertTrue(child.complete()) + assertTrue(child.isCompleted) + assertTrue(parent.isCompleted) + assertFalse(child.isActive) + assertFalse(parent.isActive) + } +} \ No newline at end of file From c5e4c25d810f6316379280f20e131a3044fbac91 Mon Sep 17 00:00:00 2001 From: Roman Elizarov Date: Wed, 6 Feb 2019 18:46:00 +0300 Subject: [PATCH 2/3] Updated copyright in modified files --- kotlinx-coroutines-core/common/src/CompletableDeferred.kt | 2 +- kotlinx-coroutines-core/common/src/CompletableJob.kt | 2 +- kotlinx-coroutines-core/common/src/Job.kt | 2 +- kotlinx-coroutines-core/common/src/JobSupport.kt | 2 +- kotlinx-coroutines-core/common/src/Supervisor.kt | 2 +- kotlinx-coroutines-core/common/test/CompletableJobTest.kt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/kotlinx-coroutines-core/common/src/CompletableDeferred.kt b/kotlinx-coroutines-core/common/src/CompletableDeferred.kt index 9cc90e4434..8973bd0787 100644 --- a/kotlinx-coroutines-core/common/src/CompletableDeferred.kt +++ b/kotlinx-coroutines-core/common/src/CompletableDeferred.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. */ @file:Suppress("DEPRECATION_ERROR") diff --git a/kotlinx-coroutines-core/common/src/CompletableJob.kt b/kotlinx-coroutines-core/common/src/CompletableJob.kt index d752dcda75..9b3382ba43 100644 --- a/kotlinx-coroutines-core/common/src/CompletableJob.kt +++ b/kotlinx-coroutines-core/common/src/CompletableJob.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 diff --git a/kotlinx-coroutines-core/common/src/Job.kt b/kotlinx-coroutines-core/common/src/Job.kt index 2dd65bdf36..a428de8595 100644 --- a/kotlinx-coroutines-core/common/src/Job.kt +++ b/kotlinx-coroutines-core/common/src/Job.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. */ @file:JvmMultifileClass diff --git a/kotlinx-coroutines-core/common/src/JobSupport.kt b/kotlinx-coroutines-core/common/src/JobSupport.kt index 3a3dd3b2ca..e79bd4b6f7 100644 --- a/kotlinx-coroutines-core/common/src/JobSupport.kt +++ b/kotlinx-coroutines-core/common/src/JobSupport.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. */ @file:Suppress("DEPRECATION_ERROR") diff --git a/kotlinx-coroutines-core/common/src/Supervisor.kt b/kotlinx-coroutines-core/common/src/Supervisor.kt index cbc1bc818e..869859f7e7 100644 --- a/kotlinx-coroutines-core/common/src/Supervisor.kt +++ b/kotlinx-coroutines-core/common/src/Supervisor.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. */ @file:Suppress("DEPRECATION_ERROR") diff --git a/kotlinx-coroutines-core/common/test/CompletableJobTest.kt b/kotlinx-coroutines-core/common/test/CompletableJobTest.kt index 16686a70d3..335e5d5672 100644 --- a/kotlinx-coroutines-core/common/test/CompletableJobTest.kt +++ b/kotlinx-coroutines-core/common/test/CompletableJobTest.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 From 85aa46d6c412f155f7d6f51caa939a5659fc2713 Mon Sep 17 00:00:00 2001 From: Roman Elizarov Date: Wed, 6 Feb 2019 18:53:05 +0300 Subject: [PATCH 3/3] Clarification on CompletableJob/Deferred.completeExceptionally logic --- .../common/src/CompletableDeferred.kt | 9 +++++++-- kotlinx-coroutines-core/common/src/CompletableJob.kt | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/kotlinx-coroutines-core/common/src/CompletableDeferred.kt b/kotlinx-coroutines-core/common/src/CompletableDeferred.kt index 8973bd0787..b280b319be 100644 --- a/kotlinx-coroutines-core/common/src/CompletableDeferred.kt +++ b/kotlinx-coroutines-core/common/src/CompletableDeferred.kt @@ -26,8 +26,9 @@ public interface CompletableDeferred : Deferred { * * Repeated invocations of this function have no effect and always produce `false`. * - * Note, that if this deferred has children, then it transitions into _completing_ state and becomes _complete_ - * once all its children are _complete_. See [Job] for details. + * This function transitions this deferred into _completed_ state if it was not completed or cancelled yet. + * However, if this deferred has children, then it transitions into _completing_ state and becomes _complete_ + * once all its children are [complete][isCompleted]. See [Job] for details. */ public fun complete(value: T): Boolean @@ -36,6 +37,10 @@ public interface CompletableDeferred : Deferred { * completed as a result of this invocation and `false` otherwise (if it was already completed). * * Repeated invocations of this function have no effect and always produce `false`. + * + * This function transitions this deferred into _cancelled_ state if it was not completed or cancelled yet. + * However, that if this deferred has children, then it transitions into _cancelling_ state and becomes _cancelled_ + * once all its children are [complete][isCompleted]. See [Job] for details. */ public fun completeExceptionally(exception: Throwable): Boolean } diff --git a/kotlinx-coroutines-core/common/src/CompletableJob.kt b/kotlinx-coroutines-core/common/src/CompletableJob.kt index 9b3382ba43..7f959910ce 100644 --- a/kotlinx-coroutines-core/common/src/CompletableJob.kt +++ b/kotlinx-coroutines-core/common/src/CompletableJob.kt @@ -15,8 +15,9 @@ public interface CompletableJob : Job { * * Repeated invocations of this function have no effect and always produce `false`. * - * Note, that if this job has children, then it transitions into _completing_ state and becomes _complete_ - * once all its children are _complete_. See [Job] for details. + * This function transitions this job into _completed- state if it was not completed or cancelled yet. + * However, that if this job has children, then it transitions into _completing_ state and becomes _complete_ + * once all its children are [complete][isCompleted]. See [Job] for details. */ public fun complete(): Boolean @@ -25,6 +26,10 @@ public interface CompletableJob : Job { * completed as a result of this invocation and `false` otherwise (if it was already completed). * * Repeated invocations of this function have no effect and always produce `false`. + * + * This function transitions this job into _cancelled_ state if it was not completed or cancelled yet. + * However, that if this job has children, then it transitions into _cancelling_ state and becomes _cancelled_ + * once all its children are [complete][isCompleted]. See [Job] for details. */ public fun completeExceptionally(exception: Throwable): Boolean } \ No newline at end of file