Skip to content

Commit

Permalink
Add ensureActive extension
Browse files Browse the repository at this point in the history
Fixes #963
  • Loading branch information
qwwdfsad committed Mar 6, 2019
1 parent 40a2560 commit ab92346
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 0 deletions.
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 @@ -203,3 +203,19 @@ public 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)
}
}

0 comments on commit ab92346

Please sign in to comment.