Skip to content

Add ensureActive extension #1015

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ public final class kotlinx/coroutines/CoroutineScopeKt {
public static final fun MainScope ()Lkotlinx/coroutines/CoroutineScope;
public static final fun cancel (Lkotlinx/coroutines/CoroutineScope;)V
public static final fun coroutineScope (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun ensureActive (Lkotlinx/coroutines/CoroutineScope;)V
public static final fun isActive (Lkotlinx/coroutines/CoroutineScope;)Z
public static final fun plus (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;)Lkotlinx/coroutines/CoroutineScope;
}
Expand Down Expand Up @@ -350,6 +351,8 @@ public final class kotlinx/coroutines/JobKt {
public static synthetic fun cancelChildren$default (Lkotlinx/coroutines/Job;Ljava/lang/Throwable;ILjava/lang/Object;)V
public static final fun cancelFutureOnCancellation (Lkotlinx/coroutines/CancellableContinuation;Ljava/util/concurrent/Future;)V
public static final fun cancelFutureOnCompletion (Lkotlinx/coroutines/Job;Ljava/util/concurrent/Future;)Lkotlinx/coroutines/DisposableHandle;
public static final fun ensureActive (Lkotlin/coroutines/CoroutineContext;)V
public static final fun ensureActive (Lkotlinx/coroutines/Job;)V
public static final fun isActive (Lkotlin/coroutines/CoroutineContext;)Z
}

Expand Down
16 changes: 16 additions & 0 deletions kotlinx-coroutines-core/common/src/CoroutineScope.kt
Original file line number Diff line number Diff line change
Expand Up @@ -209,3 +209,19 @@ public inline fun CoroutineScope.cancel() {
val job = coroutineContext[Job] ?: error("Scope cannot be cancelled because it does not have a job: $this")
job.cancel()
}

/**
* Ensures that current scope is [active][CoroutineScope.isActive].
* Throws [IllegalStateException] if the context does not have a job in it.
*
* If the job is no longer active, throws [CancellationException].
* If the job was cancelled, thrown exception contains the original cancellation cause.
*
* This method is a drop-in replacement for the following code, but with more precise exception:
* ```
* if (!isActive) {
* throw CancellationException()
* }
* ```
*/
public fun CoroutineScope.ensureActive(): Unit = coroutineContext.ensureActive()
35 changes: 35 additions & 0 deletions kotlinx-coroutines-core/common/src/Job.kt
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,41 @@ public fun CoroutineContext.cancel(): Unit {
this[Job]?.cancel()
}

/**
* Ensures that current job is [active][Job.isActive].
* If the job is no longer active, throws [CancellationException].
* If the job was cancelled, thrown exception contains the original cancellation cause.
*
* This method is a drop-in replacement for the following code, but with more precise exception:
* ```
* if (!job.isActive) {
* throw CancellationException()
* }
* ```
*/
public fun Job.ensureActive(): Unit {
if (!isActive) throw getCancellationException()
}

/**
* Ensures that job in the current context is [active][Job.isActive].
* Throws [IllegalStateException] if the context does not have a job in it.
*
* If the job is no longer active, throws [CancellationException].
* If the job was cancelled, thrown exception contains the original cancellation cause.
*
* This method is a drop-in replacement for the following code, but with more precise exception:
* ```
* if (!isActive) {
* throw CancellationException()
* }
* ```
*/
public fun CoroutineContext.ensureActive(): Unit {
val job = get(Job) ?: error("Context cannot be checked for liveness because it does not have a job: $this")
job.ensureActive()
}

/**
* @suppress
*/
Expand Down
77 changes: 77 additions & 0 deletions kotlinx-coroutines-core/common/test/EnsureActiveTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/

package kotlinx.coroutines

import kotlin.test.*

class EnsureActiveTest : TestBase() {

private val job = Job()
private val scope = CoroutineScope(job + CoroutineExceptionHandler { _, _ -> })

@Test
fun testIsActive() = runTest {
expect(1)
scope.launch(Dispatchers.Unconfined) {
ensureActive()
coroutineContext.ensureActive()
coroutineContext[Job]!!.ensureActive()
expect(2)
delay(Long.MAX_VALUE)
}

expect(3)
job.ensureActive()
scope.ensureActive()
scope.coroutineContext.ensureActive()
job.cancelAndJoin()
finish(4)
}

@Test
fun testIsCompleted() = runTest {
expect(1)
scope.launch(Dispatchers.Unconfined) {
ensureActive()
coroutineContext.ensureActive()
coroutineContext[Job]!!.ensureActive()
expect(2)
}

expect(3)
job.complete()
job.join()
assertFailsWith<JobCancellationException> { job.ensureActive() }
assertFailsWith<JobCancellationException> { scope.ensureActive() }
assertFailsWith<JobCancellationException> { scope.coroutineContext.ensureActive() }
finish(4)
}


@Test
fun testIsCancelled() = runTest {
expect(1)
scope.launch(Dispatchers.Unconfined) {
ensureActive()
coroutineContext.ensureActive()
coroutineContext[Job]!!.ensureActive()
expect(2)
throw TestException()
}

expect(3)
checkException { job.ensureActive() }
checkException { scope.ensureActive() }
checkException { scope.coroutineContext.ensureActive() }
finish(4)
}

private inline fun checkException(block: () -> Unit) {
val result = runCatching(block)
val exception = result.exceptionOrNull() ?: fail()
assertTrue(exception is JobCancellationException)
assertTrue(exception.cause is TestException)
}
}