Repository: Kotlin/kotlinx.coroutines
File: kotlinx-coroutines-core/common/src/CoroutineScope.kt (Kotlin)

Code before:
 */
@Suppress("FunctionName")
public fun CoroutineScope(context: CoroutineContext): CoroutineScope =
    ContextScope(if (context[Job] != null) context else context + Job())

/**
 * Cancels this scope, including its job and all its children with an optional cancellation [cause].
 * A cause can be used to specify an error message or to provide other details on
 * a cancellation reason for debugging purposes.
 * Throws [IllegalStateException] if the scope does not have a job in it.
 */
public fun CoroutineScope.cancel(cause: CancellationException? = null) {
    val job = coroutineContext[Job] ?: error("Scope cannot be cancelled because it does not have a job: $this")
    job.cancel(cause)
}

/**
 * Cancels this scope, including its job and all its children with a specified diagnostic error [message].
 * A [cause] can be specified to provide additional details on a cancellation reason for debugging purposes.
 * Throws [IllegalStateException] if the scope does not have a job in it.
 */
public fun CoroutineScope.cancel(message: String, cause: Throwable? = null): Unit = cancel(CancellationException(message, cause))

/**
 * Ensures that current scope is [active][CoroutineScope.isActive] and throws the [CancellationException] that was the
 * scope's cancellation cause otherwise.
 *
 * A coroutine cancallation [is cooperative](https://kotlinlang.org/docs/cancellation-and-timeouts.html#cancellation-is-cooperative)
 * and normally, the cancellation machinery happens when the coroutine *suspends*, for example,
 * when trying to read from a [channel][kotlinx.coroutines.channels.Channel] that is empty.
 *
 * Sometimes, a coroutine does not need to do any of such operations, but still wants to be a cooperative
 * and respect cancellation.
 *
 * [ensureActive] function is inteded to be used for these scenarios and immediately bubble up the cancellation exception:
 * ```
 * val watchdogDispatcher = Dispatchers.IO.limitParallelism(1)
 * fun backgroundWork() {
 *     println("Doing bookkeeping in the background in a non-suspending manner")
 *     Thread.sleep(100L) // Sleep 100ms
 * }
 * fun postBackgroundCleanup() = println("Doing something else")
 * // Part of some non-trivial CoroutineScope-confined lifecycle
 * launch(watchdogDispatcher) {
 *     while (true) {
 *         // Repeatatively do some background work that is non-suspending
 *         backgroundWork()
 *         ensureActive() // Bail out if the scope was cancelled
 *         postBackgroundCleanup() // Won't be invoked if the scope was cancelled
 *     }
 * }

Review comment (suggestion):
```suggestion
 * Throws the [CancellationException] that was the scope's cancellation cause if the scope is no longer [active][CoroutineScope.isActive].
```
Otherwise, it may leave the impression that "ensuring the scope is active" is some procedure that's not just throwing an exception.

Code after:
 */
@Suppress("FunctionName")
public fun CoroutineScope(context: CoroutineContext): CoroutineScope =
    ContextScope(if (context[Job] != null) context else context + Job())

/**
 * Cancels this scope, including its job and all its children with an optional cancellation [cause].
 * A cause can be used to specify an error message or to provide other details on
 * a cancellation reason for debugging purposes.
 * Throws [IllegalStateException] if the scope does not have a job in it.
 */
public fun CoroutineScope.cancel(cause: CancellationException? = null) {
    val job = coroutineContext[Job] ?: error("Scope cannot be cancelled because it does not have a job: $this")
    job.cancel(cause)
}

/**
 * Cancels this scope, including its job and all its children with a specified diagnostic error [message].
 * A [cause] can be specified to provide additional details on a cancellation reason for debugging purposes.
 * Throws [IllegalStateException] if the scope does not have a job in it.
 */
public fun CoroutineScope.cancel(message: String, cause: Throwable? = null): Unit = cancel(CancellationException(message, cause))

/**
 * Throws the [CancellationException] that was the scope's cancellation cause if the scope is no longer [active][CoroutineScope.isActive].
 *
 * Coroutine cancallation [is cooperative](https://kotlinlang.org/docs/cancellation-and-timeouts.html#cancellation-is-cooperative)
 * and normally, it's checked if a coroutine is cancelled when it *suspends*, for example,
 * when trying to read from a [channel][kotlinx.coroutines.channels.Channel] that is empty.
 *
 * Sometimes, a coroutine does not need to perform suspending operations, but still wants to be cooperative
 * and respect cancellation.
 *
 * [ensureActive] function is inteded to be used for these scenarios and immediately bubble up the cancellation exception:
 * ```
 * val watchdogDispatcher = Dispatchers.IO.limitParallelism(1)
 * fun backgroundWork() {
 *     println("Doing bookkeeping in the background in a non-suspending manner")
 *     Thread.sleep(100L) // Sleep 100ms
 * }
 * fun postBackgroundCleanup() = println("Doing something else")
 * // Part of some non-trivial CoroutineScope-confined lifecycle
 * launch(watchdogDispatcher) {
 *     while (true) {
 *         // Repeatatively do some background work that is non-suspending
 *         backgroundWork()
 *         ensureActive() // Bail out if the scope was cancelled
 *         postBackgroundCleanup() // Won't be invoked if the scope was cancelled
 *     }
 * }
 * ```