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

Code before:

    /**
     * Atomically (i.e., in a non-cancellable way) schedules the coroutine for execution according to its context.
     *
     * This is similar to [DEFAULT], but the coroutine is guaranteed to start executing even if it was cancelled.
     * This only affects the initial portion of the code: on subsequent suspensions, cancellation will work as usual.
     *
     * [UNDISPATCHED] also ensures that coroutines will be started in any case.
     * The difference is that, instead of immediately starting them on the same thread,
     * [ATOMIC] performs the full dispatch procedure just as [DEFAULT] does.
     *
     * Because of this, we can use [ATOMIC] in cases where we want to be certain that some code eventually runs
     * and uses a specific dispatcher to do that.
     *
     * Example:
     * ```
     * val N_PERMITS = 3
     * val semaphore = Semaphore(N_PERMITS)
     * try {
     *     repeat(100) {
     *         semaphore.acquire()
     *         if (it != 7) {
     *             println("Scheduling $it...")
     *         } else {
     *             // "randomly" cancel the whole procedure
     *             cancel()
     *         }
     *         launch(Dispatchers.Default, start = CoroutineStart.ATOMIC) {
     *             println("Entered $it")
     *             try {
     *                 println("Performing the procedure $it")
     *                 delay(10.milliseconds)
     *                 println("Done with the procedure $it")
     *             } finally {
     *                 semaphore.release()
     *             }
     *         }
     *     }
     * } finally {
     *     withContext(NonCancellable) {
     *         repeat(N_PERMITS) { semaphore.acquire() }
     *         println("All permits were successfully returned!")
     *     }
     * }
     * ```
     *
     * Here, we used [ATOMIC] to ensure that a semaphore that was acquired outside of the coroutine does get released
     * even if cancellation happens between `acquire()` and `launch`.
     * As a result, the semaphore will eventually regain all three permits.
     *
     * Behavior of [ATOMIC] can be described with the following examples:

Review comment (bug):
This sample is a little bit problematic because it contains non-trivial logic that obscures the actual atomic logic.

I would suggest the example from the `DEFAULT` but with something like `This code will execute even though the coroutine was cancelled before the dispatch` instead of `This code will never execute`.

Also, let's leave a warning comment (without an example) that it might be bug-prone if used e.g. with UI elements that are destroyed as part of the cancellatiobn

Code after:
     * Note that with this, any access to this variable will start the coroutine,
     * even something like `page.invokeOnCompletion { }` or `page.isActive`.
     *
     * If a coroutine is started with [LAZY] and then unconditionally started,
     * it is more idiomatic to create the coroutine in the exact place where it is started:
     *
     * ```
     * // instead of `val job = scope.launch(start = CoroutineStart.LAZY) { }; job.start()`, do
     * scope.launch { }
     * ```
     */
    LAZY,

    /**
     * Atomically (i.e., in a non-cancellable way) schedules the coroutine for execution according to its context.
     *
     * This is similar to [DEFAULT], but the coroutine is guaranteed to start executing even if it was cancelled.
     * This only affects the behavior until the body of the coroutine starts executing;
     * inside the body, cancellation will work as usual.
     *
     * Like [ATOMIC], [UNDISPATCHED], too, ensures that coroutines will be started in any case.
     * The difference is that, instead of immediately starting them on the same thread,
     * [ATOMIC] performs the full dispatch procedure just as [DEFAULT] does.
     *
     * Because of this, we can use [ATOMIC] in cases where we want to be certain that some code eventually runs
     * and uses a specific dispatcher to do that.
     *
     * Example:
     * ```
     * val mutex = Mutex()
     *
     * mutex.lock() // lock the mutex outside the coroutine
     * // ... // initial portion of the work, protected by the mutex
     * val job = launch(start = CoroutineStart.ATOMIC) {
     *     // the work must continue in a coroutine, but still under the mutex
     *     println("Coroutine running!")
     *     try {
     *         // this `try` block will be entered in any case because of ATOMIC
     *         println("Starting task...")
     *         delay(10.milliseconds) // throws due to cancellation
     *         println("Finished task.")
     *     } finally {
     *         mutex.unlock() // correctly release the mutex
     *     }
     * }
     *
     * job.cancelAndJoin() // we immediately cancel the coroutine.
     * mutex.withLock {
     *     println("The lock has been returned correctly!")
     * }
     * ```