Skip to content

Commit 9643c70

Browse files
committed
Change the contract of CoroutineContext.isActive to return 'true' for contexts with no job in it.
Otherwise, the API is becoming an error prone for being called from jobless entrypoints (i.e. 'suspend fun main' or Ktor handlers), as 'if (ctx.isActive)' is a well-established pattern for busy-wait or synchronous job. It is now aligned with CoroutineScope.isActive behaviour. Fixes #3300
1 parent 1ed19c8 commit 9643c70

File tree

2 files changed

+14
-3
lines changed

2 files changed

+14
-3
lines changed

kotlinx-coroutines-core/common/src/Job.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ public fun Job.cancelChildren(cause: Throwable? = null) {
539539

540540
/**
541541
* Returns `true` when the [Job] of the coroutine in this context is still active
542-
* (has not completed and was not cancelled yet).
542+
* (has not completed and was not cancelled yet) or the context does not have a [Job] in it.
543543
*
544544
* Check this property in long-running computation loops to support cancellation
545545
* when [CoroutineScope.isActive] is not available:
@@ -550,11 +550,11 @@ public fun Job.cancelChildren(cause: Throwable? = null) {
550550
* }
551551
* ```
552552
*
553-
* The `coroutineContext.isActive` expression is a shortcut for `coroutineContext[Job]?.isActive == true`.
553+
* The `coroutineContext.isActive` expression is a shortcut for `get(Job)?.isActive ?: true`.
554554
* See [Job.isActive].
555555
*/
556556
public val CoroutineContext.isActive: Boolean
557-
get() = this[Job]?.isActive == true
557+
get() = get(Job)?.isActive ?: true
558558

559559
/**
560560
* Cancels [Job] of this context with an optional cancellation cause.

kotlinx-coroutines-core/common/test/CoroutineScopeTest.kt

+11
Original file line numberDiff line numberDiff line change
@@ -277,4 +277,15 @@ class CoroutineScopeTest : TestBase() {
277277

278278
private fun scopePlusContext(c1: CoroutineContext, c2: CoroutineContext) =
279279
(ContextScope(c1) + c2).coroutineContext
280+
281+
@Test
282+
fun testIsActiveWithoutJob() {
283+
var invoked = false
284+
suspend fun testIsActive() {
285+
assertTrue(coroutineContext.isActive)
286+
invoked = true
287+
}
288+
::testIsActive.startCoroutine(Continuation(EmptyCoroutineContext){})
289+
assertTrue(invoked)
290+
}
280291
}

0 commit comments

Comments
 (0)