Repository: Kotlin/kotlinx.coroutines
File: kotlinx-coroutines-core/jvm/src/Dispatchers.kt (Kotlin)

Code before:
     * you should use a custom dispatcher backed by your own thread pool.
     *
     * ### Implementation note
     *
     * This dispatcher shares threads with the [Default][Dispatchers.Default] dispatcher, so using
     * `withContext(Dispatchers.IO) { ... }` when already running on the [Default][Dispatchers.Default]
     * dispatcher does not lead to an actual switching to another thread &mdash; typically execution
     * continues in the same thread.
     * As a result of thread sharing, more than 64 (default parallelism) threads can be created (but not used)
     * during operations over IO dispatcher.
     */
    @JvmStatic
    public val IO: CoroutineDispatcher = DefaultScheduler.IO

    /**
     * Shuts down built-in dispatchers, such as [Default] and [IO],
     * stopping all the threads associated with them and making them reject all new tasks.
     * Dispatcher used as a fallback for time-related operations (`delay`, `withTimeout`)
     * and to handle rejected tasks from other dispatchers is also shut down.
     *
     * This is a **delicate** API. It is not supposed to be called from a general
     * application-level code and its invocation is irreversible.
     * The invocation of shutdown affects most of the coroutines machinery and
     * leaves the coroutines framework in an inoperable state.
     * An invocation of shutdown method while having pending tasks or active coroutines
     * will leave them in a permanent dormant state, preventing them from completion or execution.
     *
     * The main goal of the shutdown is to stop all background threads associated with the coroutines
     * framework in order to make kotlinx.coroutines classes unloadable by Java Virtual Machine.
     * It is only recommended to be used in containerized environments (OSGi, Gradle plugins system,
     * IDEA plugins) at the end of the container lifecycle.
     */
    @DelicateCoroutinesApi
    public fun shutdown() {
        DefaultExecutor.shutdown()
        // Also shuts down Dispatchers.IO
        shutdownDefaultDispatchers()
    }
}


Review comment (bug):
I agree with your reasoning: maybe we shouldn't give some specific guarantees or thoroughly discuss the API for less than 0.001% of our users.

That said, I think that the line of documentation that I'm commenting on is still misleading. It implies strongly that `shutdown` will always succeed, and the tasks will just be silently dropped. The example above still crashes, though with a better message.

Maybe we should document the lack of guarantees then? Something like
```suggestion
     * The shutdown method should only be invoked when there are no pending tasks or active coroutines.
     * Otherwise, the behavior is unspecified: the call to `shutdown` may throw an exception without completing
     * the shutdown, or it may finish successfully, but the remaining jobs will be in a permanent dormant state,
     * never completing nor executing.
```
I like this version more because it clarifies two important points:
* One can't rely on `shutdown` finishing successfully if there are active jobs;
* One can't rely on `shutdown` throwing an exception to indicate the presence of active jobs (see the example below):
```kotlin
        val j = GlobalScope.launch {
            println("Before the delay")
            Thread.sleep(1000)
            println("This was reached")
        }
        println("Shutdown pending")
        Dispatchers.shutdown()
        println("Shutdown successful")
        runBlocking {
            j.join()
        }
```
This successfully performs the rescheduling :
```
Shutdown pending
Before the delay
This was reached
Shutdown successful
```
Replacing `Thread.sleep` with `delay` makes this crash after printing "Before the delay". So, the exception-throwing behavior is inconsistent.

Code after:
     * you should use a custom dispatcher backed by your own thread pool.
     *
     * ### Implementation note
     *
     * This dispatcher shares threads with the [Default][Dispatchers.Default] dispatcher, so using
     * `withContext(Dispatchers.IO) { ... }` when already running on the [Default][Dispatchers.Default]
     * dispatcher does not lead to an actual switching to another thread &mdash; typically execution
     * continues in the same thread.
     * As a result of thread sharing, more than 64 (default parallelism) threads can be created (but not used)
     * during operations over IO dispatcher.
     */
    @JvmStatic
    public val IO: CoroutineDispatcher = DefaultScheduler.IO

    /**
     * Shuts down built-in dispatchers, such as [Default] and [IO],
     * stopping all the threads associated with them and making them reject all new tasks.
     * Dispatcher used as a fallback for time-related operations (`delay`, `withTimeout`)
     * and to handle rejected tasks from other dispatchers is also shut down.
     *
     * This is a **delicate** API. It is not supposed to be called from a general
     * application-level code and its invocation is irreversible.
     * The invocation of shutdown affects most of the coroutines machinery and
     * leaves the coroutines framework in an inoperable state.
     * The shutdown method should only be invoked when there are no pending tasks or active coroutines.
     * Otherwise, the behavior is unspecified: the call to `shutdown` may throw an exception without completing
     * the shutdown, or it may finish successfully, but the remaining jobs will be in a permanent dormant state,
     * never completing nor executing.
     *
     * The main goal of the shutdown is to stop all background threads associated with the coroutines
     * framework in order to make kotlinx.coroutines classes unloadable by Java Virtual Machine.
     * It is only recommended to be used in containerized environments (OSGi, Gradle plugins system,
     * IDEA plugins) at the end of the container lifecycle.
     */
    @DelicateCoroutinesApi
    public fun shutdown() {
        DefaultExecutor.shutdown()
        // Also shuts down Dispatchers.IO
        DefaultScheduler.shutdown()
    }
}