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

Code before:
     *             } finally {
     *                 --subscribers
     *             }
     *         }
     *     }
     *     for (subscriberIndex in 1..10) {
     *         awaitTickNumber(10 + subscriberIndex * 3)
     *     }
     *     // Send the current tick number every 10 milliseconds
     *     // while there are subscribers
     *     var i = 0
     *     // Because of UNDISPATCHED,
     *     // we know that the subscribers are already initialized,
     *     // so this number is non-zero initially.
     *     while (subscribers > 0) {
     *         channel.trySend(++i)
     *         delay(10.milliseconds)
     *     }
     * }
     * ```
     *
     * Here, we implement a publisher-subscriber interaction, where [UNDISPATCHED] ensures that the
     * subscribers do get registered before the publisher first checks if it can stop emitting values due to
     * the lack of subscribers.
     *
     * **Pitfall**: unlike [Dispatchers.Unconfined] and [MainCoroutineDispatcher.immediate], nested undispatched
     * coroutines do not form an event loop that otherwise prevents potential stack overflow in case of unlimited
     * nesting.
     *
     * ```
     * // Constant usage of stack space
     * fun CoroutineScope.factorialWithUnconfined(n: Int): Deferred<Int> =
     *     async(Dispatchers.Unconfined) {
     *         if (n > 0) {
     *             n * factorialWithUnconfined(n - 1).await()
     *         } else {
     *             1 // replace with `error()` to see the stacktrace
     *         }
     *     }
     *
     * // Linearly increasing usage of stack space
     * fun CoroutineScope.factorialWithUndispatched(n: Int): Deferred<Int> =
     *     async(start = CoroutineStart.UNDISPATCHED) {
     *         if (n > 0) {
     *             n * factorialWithUndispatched(n - 1).await()
     *         } else {
     *             1 // replace with `error()` to see the stacktrace
     *         }
     *     }
     * ```
     *

Review comment (suggestion):
I believe this can be moved to the bottom of the discussion, like the pitfalls in the other modes as well.

Code after:
     *     launch(start = CoroutineStart.UNDISPATCHED) {
     *         tasks++
     *         try {
     *             println("Waiting for a reply...")
     *             delay(50.milliseconds)
     *             println("Got a reply!")
     *         } finally {
     *             tasks--
     *         }
     *     }
     * }
     * // Because of UNDISPATCHED,
     * // we know that the tasks already ran to their first suspension point,
     * // so this number is non-zero initially.
     * while (tasks > 0) {
     *     println("currently active: $tasks")
     *     delay(10.milliseconds)
     * }
     * ```
     *
     * Here, we implement a publisher-subscriber interaction, where [UNDISPATCHED] ensures that the
     * subscribers do get registered before the publisher first checks if it can stop emitting values due to
     * the lack of subscribers.
     *
     * ```
     * // Constant usage of stack space
     * fun CoroutineScope.factorialWithUnconfined(n: Int): Deferred<Int> =
     *     async(Dispatchers.Unconfined) {
     *         if (n > 0) {
     *             n * factorialWithUnconfined(n - 1).await()
     *         } else {
     *             1 // replace with `error()` to see the stacktrace
     *         }
     *     }
     *
     * // Linearly increasing usage of stack space
     * fun CoroutineScope.factorialWithUndispatched(n: Int): Deferred<Int> =
     *     async(start = CoroutineStart.UNDISPATCHED) {
     *         if (n > 0) {
     *             n * factorialWithUndispatched(n - 1).await()
     *         } else {
     *             1 // replace with `error()` to see the stacktrace
     *         }
     *     }
     * ```
     *
     * Calling `factorialWithUnconfined` from this example will result in a constant-size stack,
     * whereas `factorialWithUndispatched` will lead to `n` recursively nested calls,
     * resulting in a stack overflow for large values of `n`.
     *
     * The behavior of [UNDISPATCHED] can be described with the following examples: