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

Code before:
     *
     * ### Execution context and exception safety
     *
     * The [handler] is executed in the context of closing or cancelling operation
     * as the very last action of channel's lifecycle, meaning that improper
     * (e.g. throwing or hanging) implementations of handler cannot interfere
     * with the channel's final state.
     * Unhandled exceptions from [handler] are propagated to the operation's caller.
     *
     * Example of usage (exception handling is omitted):
     *
     * ```
     * val events = Channel(UNLIMITED)
     * callbackBasedApi.registerCallback { event ->
     *   events.trySend(event)
     * }
     *
     * val uiUpdater = launch(Dispatchers.Main, parent = UILifecycle) {
     *    events.consume { ... handle events ... }
     * }
     * // Stop callback as soon as the channel is closed or cancelled
     * events.invokeOnClose { callbackBasedApi.stop() }
     * ```
     *
     * **Stability note.** This function constitutes a stable API surface with the only exception
     * about [IllegalStateException] being thrown when multiple handlers are registered.
     * There is a possibility that this restriction will be lifted in the future.
     *
     * @throws UnsupportedOperationException if the underlying channel does not support [invokeOnClose].
     * Implementation note: currently, [invokeOnClose] is unsupported only by Rx-like integrations
     *
     * @throws IllegalStateException if another handler was already registered
     */
    public fun invokeOnClose(handler: (cause: Throwable?) -> Unit)

    /**
     * **Deprecated** offer method.
     *
     * This method was deprecated in the favour of [trySend].
     * It has proven itself as the most error-prone method in Channel API:
     *
     * * `Boolean` return type creates the false sense of security, implying that `false`
     *    is returned instead of throwing an exception.
     * * It was used mostly from non-suspending APIs where CancellationException triggered
     *   internal failures in the application (the most common source of bugs).
     * * Due to signature and explicit `if (ch.offer(...))` checks it was easy to
     *   oversee such error during code review.
     * * Its name was not aligned with the rest of the API and tried to mimic Java's queue instead.
     *
     * **NB** Automatic migration provides best-effort for the user experience, but requires removal
     * or adjusting of the code that relied on the exception handling.

Review comment (suggestion):
```suggestion
     * **Stability note.** This function constitutes a stable API surface, with the only exception being
     * that an [IllegalStateException] is thrown when multiple handlers are registered.
```

Code after:
     *
     * ### Execution context and exception safety
     *
     * The [handler] is executed as part of the closing or cancelling operation, and only after the channel reaches its final state.
     * This means that if the handler throws an exception or hangs, the channel will still be successfully closed or cancelled.
     * Unhandled exceptions from [handler] are propagated to the closing or cancelling operation's caller.
     *
     * Example of usage:
     * ```
     * val events = Channel<Event>(UNLIMITED)
     * callbackBasedApi.registerCallback { event ->
     *   events.trySend(event)
     *       .onClosed { /* channel is already closed, but the callback hasn't stopped yet */ }
     * }
     *
     * val uiUpdater = uiScope.launch(Dispatchers.Main) {
     *    events.consume { /* handle events */ }
     * }
     * // Stop the callback after the channel is closed or cancelled
     * events.invokeOnClose { callbackBasedApi.stop() }
     * ```
     *
     * **Stability note.** This function constitutes a stable API surface, with the only exception being
     * that an [IllegalStateException] is thrown when multiple handlers are registered.
     * This restriction could be lifted in the future.
     *
     * @throws UnsupportedOperationException if the underlying channel does not support [invokeOnClose].
     * Implementation note: currently, [invokeOnClose] is unsupported only by Rx-like integrations
     *
     * @throws IllegalStateException if another handler was already registered
     */
    public fun invokeOnClose(handler: (cause: Throwable?) -> Unit)

    /**
     * **Deprecated** offer method.
     *
     * This method was deprecated in the favour of [trySend].
     * It has proven itself as the most error-prone method in Channel API:
     *
     * * `Boolean` return type creates the false sense of security, implying that `false`
     *    is returned instead of throwing an exception.
     * * It was used mostly from non-suspending APIs where CancellationException triggered
     *   internal failures in the application (the most common source of bugs).
     * * Due to signature and explicit `if (ch.offer(...))` checks it was easy to
     *   oversee such error during code review.
     * * Its name was not aligned with the rest of the API and tried to mimic Java's queue instead.
     *
     * **NB** Automatic migration provides best-effort for the user experience, but requires removal
     * or adjusting of the code that relied on the exception handling.
     * The complete replacement has a more verbose form:
     * ```