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

Code before:
     * If the channel is closed already, the handler is invoked immediately.
     *
     * The meaning of `cause` that is passed to the handler:
     * - `null` if the channel was closed normally without the corresponding argument.
     * - Instance of [CancellationException] if the channel was cancelled normally without the corresponding argument.
     * - The cause of `close` or `cancel` otherwise.
     *
     * ### 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)
     *       .onFailure { ... buffer overflown, trySend is unsuccessful ... }
     *       .onClosed { ... channel is already closed, but callback hasn't stopped yet ... }
     * }
     *
     * val uiUpdater = launch(Dispatchers.Main, parent = UILifecycle) {
     *    events.consume { ... handle events ... }
     * }
     * // Stop the callback as soon as the channel is closed or cancelled
     * events.invokeOnClose { callbackBasedApi.stop() }
     * ```
     * Note that `invokeOnClose` is invoked **after** the channel is in its final state,
     * meaning that [handler] always observes already closed/cancelled channel and
     * that callback's code should be ready to handle the situation when callback
     * is yet active, but the channel is already closed.
     *
     * **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`

Review comment (suggestion):
```suggestion
     * // Stop the callback after the channel is closed or cancelled
```

Code after:
     * If the channel is closed already, the handler is invoked immediately.
     *
     * The meaning of `cause` that is passed to the handler:
     * - `null` if the channel was closed normally without the corresponding argument.
     * - Instance of [CancellationException] if the channel was cancelled normally without the corresponding argument.
     * - The cause of `close` or `cancel` otherwise.
     *
     * ### 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.