All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.channels.Channel.kt Maven / Gradle / Ivy

The newest version!
@file:Suppress("FunctionName")

package kotlinx.coroutines.channels

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel.Factory.BUFFERED
import kotlinx.coroutines.channels.Channel.Factory.CHANNEL_DEFAULT_CAPACITY
import kotlinx.coroutines.channels.Channel.Factory.CONFLATED
import kotlinx.coroutines.channels.Channel.Factory.RENDEZVOUS
import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED
import kotlinx.coroutines.internal.*
import kotlinx.coroutines.selects.*
import kotlin.contracts.*
import kotlin.internal.*
import kotlin.jvm.*

/**
 * Sender's interface to a [Channel].
 *
 * Combined, [SendChannel] and [ReceiveChannel] define the complete [Channel] interface.
 *
 * It is not expected that this interface will be implemented directly.
 * Instead, the existing [Channel] implementations can be used or delegated to.
 */
public interface SendChannel {
    /**
     * Returns `true` if this channel was closed by an invocation of [close] or its receiving side was [cancelled][ReceiveChannel.cancel].
     * This means that calling [send] will result in an exception.
     *
     * Note that if this property returns `false`, it does not guarantee that a subsequent call to [send] will succeed,
     * as the channel can be concurrently closed right after the check.
     * For such scenarios, [trySend] is the more robust solution: it attempts to send the element and returns
     * a result that says whether the channel was closed, and if not, whether sending a value was successful.
     *
     * ```
     * // DANGER! THIS CHECK IS NOT RELIABLE!
     * if (!channel.isClosedForSend) {
     *     channel.send(element) // can still fail!
     * } else {
     *     println("Can not send: the channel is closed")
     * }
     * // DO THIS INSTEAD:
     * channel.trySend(element).onClosed {
     *     println("Can not send: the channel is closed")
     * }
     * ```
     *
     * The primary intended usage of this property is skipping some portions of code that should not be executed if the
     * channel is already known to be closed.
     * For example:
     *
     * ```
     * if (channel.isClosedForSend) {
     *    // fast path
     *    return
     * } else {
     *    // slow path: actually computing the value
     *    val nextElement = run {
     *        // some heavy computation
     *    }
     *    channel.send(nextElement) // can fail anyway,
     *    // but at least we tried to avoid the computation
     * }
     * ```
     *
     * However, in many cases, even that can be achieved more idiomatically by cancelling the coroutine producing the
     * elements to send.
     * See [produce] for a way to launch a coroutine that produces elements and cancels itself when the channel is
     * closed.
     *
     * [isClosedForSend] can also be used for assertions and diagnostics to verify the expected state of the channel.
     *
     * @see SendChannel.trySend
     * @see SendChannel.close
     * @see ReceiveChannel.cancel
     */
    @DelicateCoroutinesApi
    public val isClosedForSend: Boolean

    /**
     * Sends the specified [element] to this channel.
     *
     * This function suspends if it does not manage to pass the element to the channel's buffer
     * (or directly the receiving side if there's no buffer),
     * and it can be cancelled with or without having successfully passed the element.
     * See the "Suspending and cancellation" section below for details.
     * If the channel is [closed][close], an exception is thrown (see below).
     *
     * ```
     * val channel = Channel()
     * launch {
     *     check(channel.receive() == 5)
     * }
     * channel.send(5) // suspends until 5 is received
     * ```
     *
     * ## Suspending and cancellation
     *
     * If the [BufferOverflow] strategy of this channel is [BufferOverflow.SUSPEND],
     * this function may suspend.
     * The exact scenarios differ depending on the channel's capacity:
     * - If the channel is [rendezvous][RENDEZVOUS],
     *   the sender will be suspended until the receiver calls [ReceiveChannel.receive].
     * - If the channel is [unlimited][UNLIMITED] or [conflated][CONFLATED],
     *   the sender will never be suspended even with the [BufferOverflow.SUSPEND] strategy.
     * - If the channel is buffered (either [BUFFERED] or uses a non-default buffer capacity),
     *   the sender will be suspended until the buffer has free space.
     *
     * This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this
     * suspending function is waiting, this function immediately resumes with [CancellationException].
     * There is a **prompt cancellation guarantee**: even if [send] managed to send the element, but was cancelled
     * while suspended, [CancellationException] will be thrown. See [suspendCancellableCoroutine] for low-level details.
     *
     * Because of the prompt cancellation guarantee, an exception does not always mean a failure to deliver the element.
     * See the "Undelivered elements" section in the [Channel] documentation
     * for details on handling undelivered elements.
     *
     * Note that this function does not check for cancellation when it is not suspended.
     * Use [ensureActive] or [CoroutineScope.isActive] to periodically check for cancellation in tight loops if needed:
     *
     * ```
     * // because of UNLIMITED, sending to this channel never suspends
     * val channel = Channel(Channel.UNLIMITED)
     * val job = launch {
     *     while (isActive) {
     *         channel.send(42)
     *     }
     *     // the loop exits when the job is cancelled
     * }
     * ```
     *
     * This isn't needed if other cancellable functions are called inside the loop, like [delay].
     *
     * ## Sending to a closed channel
     *
     * If a channel was [closed][close] before [send] was called and no cause was specified,
     * an [ClosedSendChannelException] will be thrown from [send].
     * If a channel was [closed][close] with a cause before [send] was called,
     * then [send] will rethrow the same (in the `===` sense) exception that was passed to [close].
     *
     * In both cases, it is guaranteed that the element was not delivered to the consumer,
     * and the `onUndeliveredElement` callback will be called.
     * See the "Undelivered elements" section in the [Channel] documentation
     * for details on handling undelivered elements.
     *
     * [Closing][close] a channel _after_ this function suspends does not cause this suspended [send] invocation
     * to abort: although subsequent invocations of [send] fail, the existing ones will continue to completion,
     * unless the sending coroutine is cancelled.
     *
     * ## Related
     *
     * This function can be used in [select] invocations with the [onSend] clause.
     * Use [trySend] to try sending to this channel without waiting and throwing.
     */
    public suspend fun send(element: E)

    /**
     * Clause for the [select] expression of the [send] suspending function that selects when the element that is
     * specified as the parameter is sent to the channel.
     * When the clause is selected, the reference to this channel is passed into the corresponding block.
     *
     * The [select] invocation fails with an exception if the channel [is closed for `send`][isClosedForSend] before
     * the [select] suspends (see the "Sending to a closed channel" section of [send]).
     *
     * Example:
     * ```
     * val sendChannels = List(4) { index ->
     *     Channel(onUndeliveredElement = {
     *         println("Undelivered element $it for $index")
     *     }).also { channel ->
     *         // launch a consumer for this channel
     *         launch {
     *             withTimeout(1.seconds) {
     *                 println("Consumer $index receives: ${channel.receive()}")
     *             }
     *         }
     *     }
     * }
     * val element = 42
     * select {
     *     for (channel in sendChannels) {
     *         channel.onSend(element) {
     *             println("Sent to channel $it")
     *         }
     *     }
     * }
     * ```
     * Here, we start a [select] expression that waits for exactly one of the four [onSend] invocations
     * to successfully send the element to the receiver,
     * and the other three will instead invoke the `onUndeliveredElement` callback.
     * See the "Undelivered elements" section in the [Channel] documentation
     * for details on handling undelivered elements.
     *
     * Like [send], [onSend] obeys the rules of prompt cancellation:
     * [select] may finish with a [CancellationException] even if the element was successfully sent.
     */
    public val onSend: SelectClause2>

    /**
     * Attempts to add the specified [element] to this channel without waiting.
     *
     * [trySend] never suspends and never throws exceptions.
     * Instead, it returns a [ChannelResult] that encapsulates the result of the operation.
     * This makes it different from [send], which can suspend and throw exceptions.
     *
     * If this channel is currently full and cannot receive new elements at the time or is [closed][close],
     * this function returns a result that indicates [a failure][ChannelResult.isFailure].
     * In this case, it is guaranteed that the element was not delivered to the consumer and the
     * `onUndeliveredElement` callback, if one is provided during the [Channel]'s construction, does *not* get called.
     *
     * [trySend] can be used as a non-`suspend` alternative to [send] in cases where it's known beforehand
     * that the channel's buffer can not overflow.
     * ```
     * class Coordinates(val x: Int, val y: Int)
     * // A channel for a single subscriber that stores the latest mouse position update.
     * // If more than one subscriber is expected, consider using a `StateFlow` instead.
     * val mousePositionUpdates = Channel(Channel.CONFLATED)
     * // Notifies the subscriber about the new mouse position.
     * // If the subscriber is slow, the intermediate updates are dropped.
     * fun moveMouse(coordinates: Coordinates) {
     *     val result = mousePositionUpdates.trySend(coordinates)
     *     if (result.isClosed) {
     *         error("Mouse position is no longer being processed")
     *     }
     * }
     * ```
     */
    public fun trySend(element: E): ChannelResult

    /**
     * Closes this channel so that subsequent attempts to [send] to it fail.
     *
     * Returns `true` if the channel was not closed previously and the call to this function closed it.
     * If the channel was already closed, this function does nothing and returns `false`.
     *
     * The existing elements in the channel remain there, and likewise,
     * the calls to [send] an [onSend] that have suspended before [close] was called will not be affected.
     * Only the subsequent calls to [send], [trySend], or [onSend] will fail.
     * [isClosedForSend] will start returning `true` immediately after this function is called.
     *
     * Once all the existing elements are received, the channel will be considered closed for `receive` as well.
     * This means that [receive][ReceiveChannel.receive] will also start throwing exceptions.
     * At that point, [isClosedForReceive][ReceiveChannel.isClosedForReceive] will start returning `true`.
     *
     * If the [cause] is non-null, it will be thrown from all the subsequent attempts to [send] to this channel,
     * as well as from all the attempts to [receive][ReceiveChannel.receive] from the channel after no elements remain.
     *
     * If the [cause] is null, the channel is considered to have completed normally.
     * All subsequent calls to [send] will throw a [ClosedSendChannelException],
     * whereas calling [receive][ReceiveChannel.receive] will throw a [ClosedReceiveChannelException]
     * after there are no more elements.
     *
     * ```
     * val channel = Channel()
     * channel.send(1)
     * channel.close()
     * try {
     *     channel.send(2)
     *     error("The channel is closed, so this line is never reached")
     * } catch (e: ClosedSendChannelException) {
     *     // expected
     * }
     * ```
     */
    public fun close(cause: Throwable? = null): Boolean

    /**
     * Registers a [handler] that is synchronously invoked once the channel is [closed][close]
     * or the receiving side of this channel is [cancelled][ReceiveChannel.cancel].
     * Only one handler can be attached to a channel during its lifetime.
     * The `handler` is invoked when [isClosedForSend] starts to return `true`.
     * 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][close] normally with `cause = null`.
     * - Instance of [CancellationException] if the channel was [cancelled][ReceiveChannel.cancel] 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(Channel.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:
     * ```
     * channel.trySend(element)
     *     .onClosed { throw it ?: ClosedSendChannelException("Channel was closed normally") }
     *     .isSuccess
     * ```
     *
     * See https://github.com/Kotlin/kotlinx.coroutines/issues/974 for more context.
     *
     * @suppress **Deprecated**.
     */
    @Deprecated(
        level = DeprecationLevel.ERROR,
        message = "Deprecated in the favour of 'trySend' method",
        replaceWith = ReplaceWith("trySend(element).isSuccess")
    ) // Warning since 1.5.0, error since 1.6.0, not hidden until 1.8+ because API is quite widespread
    public fun offer(element: E): Boolean {
        val result = trySend(element)
        if (result.isSuccess) return true
        throw recoverStackTrace(result.exceptionOrNull() ?: return false)
    }
}

/**
 * Receiver's interface to a [Channel].
 *
 * Combined, [SendChannel] and [ReceiveChannel] define the complete [Channel] interface.
 */
public interface ReceiveChannel {
    /**
     * Returns `true` if the sending side of this channel was [closed][SendChannel.close]
     * and all previously sent items were already received (which also happens for [cancelled][cancel] channels).
     *
     * Note that if this property returns `false`,
     * it does not guarantee that a subsequent call to [receive] will succeed,
     * as the channel can be concurrently cancelled or closed right after the check.
     * For such scenarios, [receiveCatching] is the more robust solution:
     * if the channel is closed, instead of throwing an exception, [receiveCatching] returns a result that allows
     * querying it.
     *
     * ```
     * // DANGER! THIS CHECK IS NOT RELIABLE!
     * if (!channel.isClosedForReceive) {
     *     channel.receive() // can still fail!
     * } else {
     *     println("Can not receive: the channel is closed")
     *     null
     * }
     * // DO THIS INSTEAD:
     * channel.receiveCatching().onClosed {
     *     println("Can not receive: the channel is closed")
     * }.getOrNull()
     * ```
     *
     * The primary intended usage of this property is for assertions and diagnostics to verify the expected state of
     * the channel.
     * Using it in production code is discouraged.
     *
     * @see ReceiveChannel.receiveCatching
     * @see ReceiveChannel.cancel
     * @see SendChannel.close
     */
    @DelicateCoroutinesApi
    public val isClosedForReceive: Boolean

    /**
     * Returns `true` if the channel contains no elements and isn't [closed for `receive`][isClosedForReceive].
     *
     * If [isEmpty] returns `true`, it means that calling [receive] at exactly the same moment would suspend.
     * However, calling [receive] immediately after checking [isEmpty] may or may not suspend, as new elements
     * could have been added or removed or the channel could have been closed for `receive` between the two invocations.
     * Consider using [tryReceive] in cases when suspensions are undesirable:
     *
     * ```
     * // DANGER! THIS CHECK IS NOT RELIABLE!
     * while (!channel.isEmpty) {
     *     // can still suspend if other `receive` happens in parallel!
     *     val element = channel.receive()
     *     println(element)
     * }
     * // DO THIS INSTEAD:
     * while (true) {
     *     val element = channel.tryReceive().getOrNull() ?: break
     *     println(element)
     * }
     * ```
     */
    @ExperimentalCoroutinesApi
    public val isEmpty: Boolean

    /**
     * Retrieves an element, removing it from the channel.
     *
     * This function suspends if the channel is empty, waiting until an element is available.
     * If the channel is [closed for `receive`][isClosedForReceive], an exception is thrown (see below).
     * ```
     * val channel = Channel()
     * launch {
     *     val element = channel.receive() // suspends until 5 is available
     *     check(element == 5)
     * }
     * channel.send(5)
     * ```
     *
     * ## Suspending and cancellation
     *
     * This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this
     * suspending function is waiting, this function immediately resumes with [CancellationException].
     * There is a **prompt cancellation guarantee**: even if [receive] managed to retrieve the element from the channel,
     * but was cancelled while suspended, [CancellationException] will be thrown, and, if
     * the channel has an `onUndeliveredElement` callback installed, the retrieved element will be passed to it.
     * See the "Undelivered elements" section in the [Channel] documentation
     * for details on handling undelivered elements.
     * See [suspendCancellableCoroutine] for the low-level details of prompt cancellation.
     *
     * Note that this function does not check for cancellation when it manages to immediately receive an element without
     * suspending.
     * Use [ensureActive] or [CoroutineScope.isActive] to periodically check for cancellation in tight loops if needed:
     *
     * ```
     * val channel = Channel()
     * launch { // a very fast producer
     *     while (true) {
     *         channel.send(42)
     *     }
     * }
     * val consumer = launch { // a slow consumer
     *     while (isActive) {
     *         val element = channel.receive()
     *         // some slow computation involving `element`
     *     }
     * }
     * delay(100.milliseconds)
     * consumer.cancelAndJoin()
     * ```
     *
     * ## Receiving from a closed channel
     *
     * - Attempting to [receive] from a [closed][SendChannel.close] channel while there are still some elements
     *   will successfully retrieve an element from the channel.
     * - When a channel is [closed][SendChannel.close] and there are no elements remaining,
     *   the channel becomes [closed for `receive`][isClosedForReceive].
     *   After that,
     *   [receive] will rethrow the same (in the `===` sense) exception that was passed to [SendChannel.close],
     *   or [ClosedReceiveChannelException] if none was given.
     *
     * ## Related
     *
     * This function can be used in [select] invocations with the [onReceive] clause.
     * Use [tryReceive] to try receiving from this channel without waiting and throwing.
     * Use [receiveCatching] to receive from this channel without throwing.
     */
    public suspend fun receive(): E

    /**
     * Clause for the [select] expression of the [receive] suspending function that selects with the element
     * received from the channel.
     *
     * The [select] invocation fails with an exception if the channel [is closed for `receive`][isClosedForReceive]
     * at any point, even if other [select] clauses could still work.
     *
     * Example:
     * ```
     * class ScreenSize(val width: Int, val height: Int)
     * class MouseClick(val x: Int, val y: Int)
     * val screenResizes = Channel(Channel.CONFLATED)
     * val mouseClicks = Channel(Channel.CONFLATED)
     *
     * launch(Dispatchers.Main) {
     *     while (true) {
     *         select {
     *             screenResizes.onReceive { newSize ->
     *                 // update the UI to the new screen size
     *             }
     *             mouseClicks.onReceive { click ->
     *                 // react to a mouse click
     *             }
     *         }
     *     }
     * }
     * ```
     *
     * Like [receive], [onReceive] obeys the rules of prompt cancellation:
     * [select] may finish with a [CancellationException] even if an element was successfully retrieved,
     * in which case the `onUndeliveredElement` callback will be called.
     */
    public val onReceive: SelectClause1

    /**
     * Retrieves an element, removing it from the channel.
     *
     * A difference from [receive] is that this function encapsulates a failure in its return value instead of throwing
     * an exception.
     * However, it will still throw [CancellationException] if the coroutine calling [receiveCatching] is cancelled.
     *
     * It is guaranteed that the only way this function can return a [failed][ChannelResult.isFailure] result is when
     * the channel is [closed for `receive`][isClosedForReceive], so [ChannelResult.isClosed] is also true.
     *
     * This function suspends if the channel is empty, waiting until an element is available or the channel becomes
     * closed.
     * ```
     * val channel = Channel()
     * launch {
     *     while (true) {
     *         val result = channel.receiveCatching() // suspends
     *         when (val element = result.getOrNull()) {
     *             null -> break // the channel is closed
     *             else -> check(element == 5)
     *         }
     *     }
     * }
     * channel.send(5)
     * ```
     *
     * ## Suspending and cancellation
     *
     * This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this
     * suspending function is waiting, this function immediately resumes with [CancellationException].
     * There is a **prompt cancellation guarantee**: even if [receiveCatching] managed to retrieve the element from the
     * channel, but was cancelled while suspended, [CancellationException] will be thrown, and, if
     * the channel has an `onUndeliveredElement` callback installed, the retrieved element will be passed to it.
     * See the "Undelivered elements" section in the [Channel] documentation
     * for details on handling undelivered elements.
     * See [suspendCancellableCoroutine] for the low-level details of prompt cancellation.
     *
     * Note that this function does not check for cancellation when it manages to immediately receive an element without
     * suspending.
     * Use [ensureActive] or [CoroutineScope.isActive] to periodically check for cancellation in tight loops if needed:
     *
     * ```
     * val channel = Channel()
     * launch { // a very fast producer
     *     while (true) {
     *         channel.send(42)
     *     }
     * }
     * val consumer = launch { // a slow consumer
     *     while (isActive) {
     *         val element = channel.receiveCatching().getOrNull() ?: break
     *         // some slow computation involving `element`
     *     }
     * }
     * delay(100.milliseconds)
     * consumer.cancelAndJoin()
     * ```
     *
     * ## Receiving from a closed channel
     *
     * - Attempting to [receiveCatching] from a [closed][SendChannel.close] channel while there are still some elements
     *   will successfully retrieve an element from the channel.
     * - When a channel is [closed][SendChannel.close] and there are no elements remaining,
     *   the channel becomes [closed for `receive`][isClosedForReceive].
     *   After that, [receiveCatching] will return a result with [ChannelResult.isClosed] set.
     *   [ChannelResult.exceptionOrNull] will be the exact (in the `===` sense) exception
     *   that was passed to [SendChannel.close],
     *   or `null` if none was given.
     *
     * ## Related
     *
     * This function can be used in [select] invocations with the [onReceiveCatching] clause.
     * Use [tryReceive] to try receiving from this channel without waiting and throwing.
     * Use [receive] to receive from this channel and throw exceptions on error.
     */
    public suspend fun receiveCatching(): ChannelResult

    /**
     * Clause for the [select] expression of the [receiveCatching] suspending function that selects
     * with a [ChannelResult] when an element is retrieved or the channel gets closed.
     *
     * Like [receiveCatching], [onReceiveCatching] obeys the rules of prompt cancellation:
     * [select] may finish with a [CancellationException] even if an element was successfully retrieved,
     * in which case the `onUndeliveredElement` callback will be called.
     */
    // TODO: think of an example of when this could be useful
    public val onReceiveCatching: SelectClause1>

    /**
     * Attempts to retrieve an element without waiting, removing it from the channel.
     *
     * - When the channel is non-empty, a [successful][ChannelResult.isSuccess] result is returned,
     *   and [ChannelResult.getOrNull] returns the retrieved element.
     * - When the channel is empty, a [failed][ChannelResult.isFailure] result is returned.
     * - When the channel is already [closed for `receive`][isClosedForReceive],
     *   returns the ["channel is closed"][ChannelResult.isClosed] result.
     *   If the channel was [closed][SendChannel.close] with a cause (for example, [cancelled][cancel]),
     *   [ChannelResult.exceptionOrNull] contains the cause.
     *
     * This function is useful when implementing on-demand allocation of resources to be stored in the channel:
     *
     * ```
     * val resourcePool = Channel(maxResources)
     *
     * suspend fun withResource(block: (Resource) -> Unit) {
     *     val result = resourcePool.tryReceive()
     *     val resource = result.getOrNull()
     *         ?: tryCreateNewResource() // try to create a new resource
     *         ?: resourcePool.receive() // could not create: actually wait for the resource
     *     try {
     *         block(resource)
     *     } finally {
     *         resourcePool.trySend(resource)
     *     }
     * }
     * ```
     */
    public fun tryReceive(): ChannelResult

    /**
     * Returns a new iterator to receive elements from this channel using a `for` loop.
     * Iteration completes normally when the channel [is closed for `receive`][isClosedForReceive] without a cause and
     * throws the exception passed to [close][SendChannel.close] if there was one.
     *
     * Instances of [ChannelIterator] are not thread-safe and shall not be used from concurrent coroutines.
     *
     * Example:
     *
     * ```
     * val channel = produce {
     *     repeat(1000) {
     *         send(it)
     *     }
     * }
     * for (v in channel) {
     *     println(v)
     * }
     * ```
     *
     * Note that if an early return happens from the `for` loop, the channel does not get cancelled.
     * To forbid sending new elements after the iteration is completed, use [consumeEach] or
     * call [cancel] manually.
     */
    public operator fun iterator(): ChannelIterator

    /**
     * [Closes][SendChannel.close] the channel for new elements and removes all existing ones.
     *
     * A [cause] can be used to specify an error message or to provide other details on
     * the cancellation reason for debugging purposes.
     * If the cause is not specified, then an instance of [CancellationException] with a
     * default message is created to [close][SendChannel.close] the channel.
     *
     * If the channel was already [closed][SendChannel.close],
     * [cancel] only has the effect of removing all elements from the channel.
     *
     * Immediately after the invocation of this function,
     * [isClosedForReceive] and, on the [SendChannel] side, [isClosedForSend][SendChannel.isClosedForSend]
     * start returning `true`.
     * Any attempt to send to or receive from this channel will lead to a [CancellationException].
     * This also applies to the existing senders and receivers that are suspended at the time of the call:
     * they will be resumed with a [CancellationException] immediately after [cancel] is called.
     *
     * If the channel has an `onUndeliveredElement` callback installed, this function will invoke it for each of the
     * elements still in the channel, since these elements will be inaccessible otherwise.
     * If the callback is not installed, these elements will simply be removed from the channel for garbage collection.
     *
     * ```
     * val channel = Channel()
     * channel.send(1)
     * channel.send(2)
     * channel.cancel()
     * channel.trySend(3) // returns ChannelResult.isClosed
     * for (element in channel) { println(element) } // prints nothing
     * ```
     *
     * [consume] and [consumeEach] are convenient shorthands for cancelling the channel after the single consumer
     * has finished processing.
     */
    public fun cancel(cause: CancellationException? = null)

    /**
     * @suppress This method implements old version of JVM ABI. Use [cancel].
     */
    @Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x")
    public fun cancel(): Unit = cancel(null)

    /**
     * @suppress This method has bad semantics when cause is not a [CancellationException]. Use [cancel].
     */
    @Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x")
    public fun cancel(cause: Throwable? = null): Boolean

    /**
     * **Deprecated** poll method.
     *
     * This method was deprecated in the favour of [tryReceive].
     * It has proven itself as error-prone method in Channel API:
     *
     * - Nullable return type creates the false sense of security, implying that `null`
     *   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).
     * - Its name was not aligned with the rest of the API and tried to mimic Java's queue instead.
     *
     * See https://github.com/Kotlin/kotlinx.coroutines/issues/974 for more context.
     *
     * ### Replacement note
     *
     * The replacement `tryReceive().getOrNull()` is a default that ignores all close exceptions and
     * proceeds with `null`, while `poll` throws an exception if the channel was closed with an exception.
     * Replacement with the very same 'poll' semantics is `tryReceive().onClosed { if (it != null) throw it }.getOrNull()`
     *
     * @suppress **Deprecated**.
     */
    @Deprecated(
        level = DeprecationLevel.ERROR,
        message = "Deprecated in the favour of 'tryReceive'. " +
            "Please note that the provided replacement does not rethrow channel's close cause as 'poll' did, " +
            "for the precise replacement please refer to the 'poll' documentation",
        replaceWith = ReplaceWith("tryReceive().getOrNull()")
    ) // Warning since 1.5.0, error since 1.6.0, not hidden until 1.8+ because API is quite widespread
    public fun poll(): E? {
        val result = tryReceive()
        if (result.isSuccess) return result.getOrThrow()
        throw recoverStackTrace(result.exceptionOrNull() ?: return null)
    }

    /**
     * This function was deprecated since 1.3.0 and is no longer recommended to use
     * or to implement in subclasses.
     *
     * It had the following pitfalls:
     * - Didn't allow to distinguish 'null' as "closed channel" from "null as a value"
     * - Was throwing if the channel has failed even though its signature may suggest it returns 'null'
     * - It didn't really belong to core channel API and can be exposed as an extension instead.
     *
     * ### Replacement note
     *
     * The replacement `receiveCatching().getOrNull()` is a safe default that ignores all close exceptions and
     * proceeds with `null`, while `receiveOrNull` throws an exception if the channel was closed with an exception.
     * Replacement with the very same `receiveOrNull` semantics is `receiveCatching().onClosed { if (it != null) throw it }.getOrNull()`.
     *
     * @suppress **Deprecated**
     */
    @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
    @LowPriorityInOverloadResolution
    @Deprecated(
        message = "Deprecated in favor of 'receiveCatching'. " +
            "Please note that the provided replacement does not rethrow channel's close cause as 'receiveOrNull' did, " +
            "for the detailed replacement please refer to the 'receiveOrNull' documentation",
        level = DeprecationLevel.ERROR,
        replaceWith = ReplaceWith("receiveCatching().getOrNull()")
    ) // Warning since 1.3.0, error in 1.5.0, cannot be hidden due to deprecated extensions
    public suspend fun receiveOrNull(): E? = receiveCatching().getOrNull()

    /**
     * This function was deprecated since 1.3.0 and is no longer recommended to use
     * or to implement in subclasses.
     * See [receiveOrNull] documentation.
     *
     * @suppress **Deprecated**: in favor of onReceiveCatching extension.
     */
    @Suppress("DEPRECATION_ERROR")
    @Deprecated(
        message = "Deprecated in favor of onReceiveCatching extension",
        level = DeprecationLevel.ERROR,
        replaceWith = ReplaceWith("onReceiveCatching")
    ) // Warning since 1.3.0, error in 1.5.0, will be hidden or removed in 1.7.0
    public val onReceiveOrNull: SelectClause1 get() = (this as BufferedChannel).onReceiveOrNull
}

/**
 * A discriminated union representing a channel operation result.
 * It encapsulates the knowledge of whether the operation succeeded, failed with an option to retry,
 * or failed because the channel was closed.
 *
 * If the operation was [successful][isSuccess], [T] is the result of the operation:
 * for example, for [ReceiveChannel.receiveCatching] and [ReceiveChannel.tryReceive],
 * it is the element received from the channel, and for [Channel.trySend], it is [Unit],
 * as the channel does not receive anything in return for sending a channel.
 * This value can be retrieved with [getOrNull] or [getOrThrow].
 *
 * If the operation [failed][isFailure], it does not necessarily mean that the channel itself is closed.
 * For example, [ReceiveChannel.receiveCatching] and [ReceiveChannel.tryReceive] can fail because the channel is empty,
 * and [Channel.trySend] can fail because the channel is full.
 *
 * If the operation [failed][isFailure] because the channel was closed for that operation, [isClosed] returns `true`.
 * The opposite is also true: if [isClosed] returns `true`, then the channel is closed for that operation
 * ([ReceiveChannel.isClosedForReceive] or [SendChannel.isClosedForSend]).
 * In this case, retrying the operation is meaningless: once closed, the channel will remain closed.
 * The [exceptionOrNull] function returns the reason the channel was closed, if any was given.
 *
 * Manually obtaining a [ChannelResult] instance is not supported.
 * See the documentation for [ChannelResult]-returning functions for usage examples.
 */
@JvmInline
public value class ChannelResult
@PublishedApi internal constructor(@PublishedApi internal val holder: Any?) {
    /**
     * Whether the operation succeeded.
     *
     * If this returns `true`, the operation was successful.
     * In this case, [getOrNull] and [getOrThrow] can be used to retrieve the value.
     *
     * If this returns `false`, the operation failed.
     * [isClosed] can be used to determine whether the operation failed because the channel was closed
     * (and therefore retrying the operation is meaningless).
     *
     * ```
     * val result = channel.tryReceive()
     * if (result.isSuccess) {
     *    println("Successfully received the value ${result.getOrThrow()}")
     * } else {
     *    println("Failed to receive the value.")
     *    if (result.isClosed) {
     *        println("The channel is closed.")
     *        if (result.exceptionOrNull() != null) {
     *            println("The reason: ${result.exceptionOrNull()}")
     *        }
     *    }
     * }
     * ```
     *
     * [isFailure] is a shorthand for `!isSuccess`.
     * [getOrNull] can simplify [isSuccess] followed by [getOrThrow] into just one check if [T] is known
     * to be non-nullable.
     */
    public val isSuccess: Boolean get() = holder !is Failed

    /**
     * Whether the operation failed.
     *
     * A shorthand for `!isSuccess`. See [isSuccess] for more details.
     */
    public val isFailure: Boolean get() = holder is Failed

    /**
     * Whether the operation failed because the channel was closed.
     *
     * If this returns `true`, the channel was closed for the operation that returned this result.
     * In this case, retrying the operation is meaningless: once closed, the channel will remain closed.
     * [isSuccess] will return `false`.
     * [exceptionOrNull] can be used to determine the reason the channel was [closed][SendChannel.close]
     * if one was given.
     *
     * If this returns `false`, subsequent attempts to perform the same operation may succeed.
     *
     * ```
     * val result = channel.trySend(42)
     * if (result.isClosed) {
     *     println("The channel is closed.")
     *     if (result.exceptionOrNull() != null) {
     *         println("The reason: ${result.exceptionOrNull()}")
     *     }
     * }
     */
    public val isClosed: Boolean get() = holder is Closed

    /**
     * Returns the encapsulated [T] if the operation succeeded, or `null` if it failed.
     *
     * For non-nullable [T], the following code can be used to handle the result:
     * ```
     * val result = channel.tryReceive()
     * val value = result.getOrNull()
     * if (value == null) {
     *     if (result.isClosed) {
     *         println("The channel is closed.")
     *         if (result.exceptionOrNull() != null) {
     *             println("The reason: ${result.exceptionOrNull()}")
     *         }
     *     }
     *     return
     * }
     * println("Successfully received the value $value")
     * ```
     *
     * If [T] is nullable, [getOrThrow] together with [isSuccess] is a more reliable way to handle the result.
     */
    @Suppress("UNCHECKED_CAST")
    public fun getOrNull(): T? = if (holder !is Failed) holder as T else null

    /**
     *  Returns the encapsulated [T] if the operation succeeded, or throws the encapsulated exception if it failed.
     *
     *  Example:
     *  ```
     *  val result = channel.tryReceive()
     *  if (result.isSuccess) {
     *      println("Successfully received the value ${result.getOrThrow()}")
     *  }
     *  ```
     *
     *  @throws IllegalStateException if the operation failed, but the channel was not closed with a cause.
     */
    public fun getOrThrow(): T {
        @Suppress("UNCHECKED_CAST")
        if (holder !is Failed) return holder as T
        if (holder is Closed) {
            check(holder.cause != null) { "Trying to call 'getOrThrow' on a channel closed without a cause" }
            throw holder.cause
        }
        error("Trying to call 'getOrThrow' on a failed result of a non-closed channel")
    }

    /**
     * Returns the exception with which the channel was closed, or `null` if the channel was not closed or was closed
     * without a cause.
     *
     * [exceptionOrNull] can only return a non-`null` value if [isClosed] is `true`,
     * but even if [isClosed] is `true`,
     * [exceptionOrNull] can still return `null` if the channel was closed without a cause.
     *
     * ```
     * val result = channel.tryReceive()
     * if (result.isClosed) {
     *     // Now we know not to retry the operation later.
     *     // Check if the channel was closed with a cause and rethrow the exception:
     *     result.exceptionOrNull()?.let { throw it }
     *     // Otherwise, the channel was closed without a cause.
     * }
     * ```
     */
    public fun exceptionOrNull(): Throwable? = (holder as? Closed)?.cause

    internal open class Failed {
        override fun toString(): String = "Failed"
    }

    internal class Closed(@JvmField val cause: Throwable?): Failed() {
        override fun equals(other: Any?): Boolean = other is Closed && cause == other.cause
        override fun hashCode(): Int = cause.hashCode()
        override fun toString(): String = "Closed($cause)"
    }

    /**
     * @suppress **This is internal API and it is subject to change.**
     */
    @InternalCoroutinesApi
    public companion object {
        private val failed = Failed()

        @InternalCoroutinesApi
        public fun  success(value: E): ChannelResult =
            ChannelResult(value)

        @InternalCoroutinesApi
        public fun  failure(): ChannelResult =
            ChannelResult(failed)

        @InternalCoroutinesApi
        public fun  closed(cause: Throwable?): ChannelResult =
            ChannelResult(Closed(cause))
    }

    public override fun toString(): String =
        when (holder) {
            is Closed -> holder.toString()
            else -> "Value($holder)"
        }
}

/**
 * Returns the encapsulated value if the operation [succeeded][ChannelResult.isSuccess], or the
 * result of [onFailure] function for [ChannelResult.exceptionOrNull] otherwise.
 *
 * A shorthand for `if (isSuccess) getOrNull() else onFailure(exceptionOrNull())`.
 *
 * @see ChannelResult.getOrNull
 * @see ChannelResult.exceptionOrNull
 */
@OptIn(ExperimentalContracts::class)
public inline fun  ChannelResult.getOrElse(onFailure: (exception: Throwable?) -> T): T {
    contract {
        callsInPlace(onFailure, InvocationKind.AT_MOST_ONCE)
    }
    @Suppress("UNCHECKED_CAST")
    return if (holder is ChannelResult.Failed) onFailure(exceptionOrNull()) else holder as T
}

/**
 * Performs the given [action] on the encapsulated value if the operation [succeeded][ChannelResult.isSuccess].
 * Returns the original `ChannelResult` unchanged.
 *
 * A shorthand for `this.also { if (isSuccess) action(getOrThrow()) }`.
 */
@OptIn(ExperimentalContracts::class)
public inline fun  ChannelResult.onSuccess(action: (value: T) -> Unit): ChannelResult {
    contract {
        callsInPlace(action, InvocationKind.AT_MOST_ONCE)
    }
    @Suppress("UNCHECKED_CAST")
    if (holder !is ChannelResult.Failed) action(holder as T)
    return this
}

/**
 * Performs the given [action] if the operation [failed][ChannelResult.isFailure].
 * The result of [ChannelResult.exceptionOrNull] is passed to the [action] parameter.
 *
 * Returns the original `ChannelResult` unchanged.
 *
 * A shorthand for `this.also { if (isFailure) action(exceptionOrNull()) }`.
 */
@OptIn(ExperimentalContracts::class)
public inline fun  ChannelResult.onFailure(action: (exception: Throwable?) -> Unit): ChannelResult {
    contract {
        callsInPlace(action, InvocationKind.AT_MOST_ONCE)
    }
    if (holder is ChannelResult.Failed) action(exceptionOrNull())
    return this
}

/**
 * Performs the given [action] if the operation failed because the channel was [closed][ChannelResult.isClosed] for
 * that operation.
 * The result of [ChannelResult.exceptionOrNull] is passed to the [action] parameter.
 *
 * It is guaranteed that if action is invoked, then the channel is either [closed for send][Channel.isClosedForSend]
 * or is [closed for receive][Channel.isClosedForReceive] depending on the failed operation.
 *
 * Returns the original `ChannelResult` unchanged.
 *
 * A shorthand for `this.also { if (isClosed) action(exceptionOrNull()) }`.
 */
@OptIn(ExperimentalContracts::class)
public inline fun  ChannelResult.onClosed(action: (exception: Throwable?) -> Unit): ChannelResult {
    contract {
        callsInPlace(action, InvocationKind.AT_MOST_ONCE)
    }
    if (holder is ChannelResult.Closed) action(exceptionOrNull())
    return this
}

/**
 * Iterator for a [ReceiveChannel].
 * Instances of this interface are *not thread-safe* and shall not be used from concurrent coroutines.
 */
public interface ChannelIterator {
    /**
     * Prepare an element for retrieval by the invocation of [next].
     *
     * - If the element that was retrieved by an earlier [hasNext] call was not yet consumed by [next], returns `true`.
     * - If the channel has an element available, returns `true` and removes it from the channel.
     *   This element will be returned by the subsequent invocation of [next].
     * - If the channel is [closed for receiving][ReceiveChannel.isClosedForReceive] without a cause, returns `false`.
     * - If the channel is closed with a cause, throws the original [close][SendChannel.close] cause exception.
     * - If the channel is not closed but does not contain an element,
     *   suspends until either an element is sent to the channel or the channel gets closed.
     *
     * This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this
     * suspending function is waiting, this function immediately resumes with [CancellationException].
     * There is a **prompt cancellation guarantee**: even if [hasNext] retrieves the element from the channel during
     * its operation, but was cancelled while suspended, [CancellationException] will be thrown.
     * See [suspendCancellableCoroutine] for low-level details.
     *
     * Because of the prompt cancellation guarantee, some values retrieved from the channel can become lost.
     * See the "Undelivered elements" section in the [Channel] documentation
     * for details on handling undelivered elements.
     *
     * Note that this function does not check for cancellation when it is not suspended, that is,
     * if the next element is immediately available.
     * Use [ensureActive] or [CoroutineScope.isActive] to periodically check for cancellation in tight loops if needed.
     */
    public suspend operator fun hasNext(): Boolean

    @Deprecated(message = "Since 1.3.0, binary compatibility with versions <= 1.2.x", level = DeprecationLevel.HIDDEN)
    @Suppress("INAPPLICABLE_JVM_NAME")
    @JvmName("next")
    public suspend fun next0(): E {
        /*
         * Before 1.3.0 the "next()" could have been used without invoking "hasNext" first and there were code samples
         * demonstrating this behavior, so we preserve this logic for full binary backwards compatibility with previously
         * compiled code.
         */
        if (!hasNext()) throw ClosedReceiveChannelException(DEFAULT_CLOSE_MESSAGE)
        return next()
    }

    /**
     * Retrieves the element removed from the channel by the preceding call to [hasNext], or
     * throws an [IllegalStateException] if [hasNext] was not invoked.
     *
     * This method can only be used together with [hasNext]:
     * ```
     * while (iterator.hasNext()) {
     *     val element = iterator.next()
     *     // ... handle the element ...
     * }
     * ```
     *
     * A more idiomatic way to iterate over a channel is to use a `for` loop:
     * ```
     * for (element in channel) {
     *    // ... handle the element ...
     * }
     * ```
     *
     * This method never throws if [hasNext] returned `true`.
     * If [hasNext] threw the cause with which the channel was closed, this method will rethrow the same exception.
     * If [hasNext] returned `false` because the channel was closed without a cause, this method throws
     * a [ClosedReceiveChannelException].
     */
    public operator fun next(): E
}

/**
 * Channel is a non-blocking primitive for communication between a sender (via [SendChannel]) and a receiver (via [ReceiveChannel]).
 * Conceptually, a channel is similar to `java.util.concurrent.BlockingQueue`,
 * but it has suspending operations instead of blocking ones and can be [closed][SendChannel.close].
 *
 * ### Channel capacity
 *
 * Most ways to create a [Channel] (in particular, the `Channel()` factory function) allow specifying a capacity,
 * which determines how elements are buffered in the channel.
 * There are several predefined constants for the capacity that have special behavior:
 *
 * - [Channel.RENDEZVOUS] (or 0) creates a _rendezvous_ channel, which does not have a buffer at all.
 *   Instead, the sender and the receiver must rendezvous (meet):
 *   [SendChannel.send] suspends until another coroutine invokes [ReceiveChannel.receive], and vice versa.
 * - [Channel.CONFLATED] creates a buffer for a single element and automatically changes the
 *   [buffer overflow strategy][BufferOverflow] to [BufferOverflow.DROP_OLDEST].
 * - [Channel.UNLIMITED] creates a channel with an unlimited buffer, which never suspends the sender.
 * - [Channel.BUFFERED] creates a channel with a buffer whose size depends on
 *   the [buffer overflow strategy][BufferOverflow].
 *
 * See each constant's documentation for more details.
 *
 * If the capacity is positive but less than [Channel.UNLIMITED], the channel has a buffer with the specified capacity.
 * It is safe to construct a channel with a large buffer, as memory is only allocated gradually as elements are added.
 *
 * Constructing a channel with a negative capacity not equal to a predefined constant is not allowed
 * and throws an [IllegalArgumentException].
 *
 * ### Buffer overflow
 *
 * Some ways to create a [Channel] also expose a [BufferOverflow] parameter (by convention, `onBufferOverflow`),
 * which does not affect the receiver but determines the behavior of the sender when the buffer is full.
 * The options include [suspending][BufferOverflow.SUSPEND] until there is space in the buffer,
 * [dropping the oldest element][BufferOverflow.DROP_OLDEST] to make room for the new one, or
 * [dropping the element to be sent][BufferOverflow.DROP_LATEST]. See the [BufferOverflow] documentation.
 *
 * By convention, the default value for [BufferOverflow] whenever it can not be configured is [BufferOverflow.SUSPEND].
 *
 * See the [Channel.RENDEZVOUS], [Channel.CONFLATED], and [Channel.UNLIMITED] documentation for a description of how
 * they interact with the [BufferOverflow] parameter.
 *
 * ### Prompt cancellation guarantee
 *
 * All suspending functions with channels provide **prompt cancellation guarantee**.
 * If the job was cancelled while send or receive function was suspended, it will not resume successfully, even if it
 * already changed the channel's state, but throws a [CancellationException].
 * With a single-threaded [dispatcher][CoroutineDispatcher] like [Dispatchers.Main], this gives a
 * guarantee that the coroutine promptly reacts to the cancellation of its [Job] and does not resume its execution.
 *
 * > **Prompt cancellation guarantee** for channel operations was added in `kotlinx.coroutines` version `1.4.0`
 * > and has replaced the channel-specific atomic cancellation that was not consistent with other suspending functions.
 * > The low-level mechanics of prompt cancellation are explained in the [suspendCancellableCoroutine] documentation.
 *
 * ### Undelivered elements
 *
 * As a result of the prompt cancellation guarantee, when a closeable resource
 * (like an open file or a handle to another native resource) is transferred via a channel,
 * it can be successfully extracted from the channel,
 * but still be lost if the receiving operation is cancelled in parallel.
 *
 * The `Channel()` factory function has the optional parameter `onUndeliveredElement`.
 * When that parameter is set, the corresponding function is called once for each element
 * that was sent to the channel with the call to the [send][SendChannel.send] function but failed to be delivered,
 * which can happen in the following cases:
 *
 * - When an element is dropped due to the limited buffer capacity.
 *   This can happen when the overflow strategy is [BufferOverflow.DROP_LATEST] or [BufferOverflow.DROP_OLDEST].
 * - When the sending operations like [send][SendChannel.send] or [onSend][SendChannel.onSend]
 *   throw an exception because it was cancelled
 *   before it had a chance to actually send the element
 *   or because the channel was [closed][SendChannel.close] or [cancelled][ReceiveChannel.cancel].
 * - When the receiving operations like [receive][ReceiveChannel.receive],
 *   [onReceive][ReceiveChannel.onReceive], or [hasNext][ChannelIterator.hasNext]
 *   throw an exception after retrieving the element from the channel
 *   because of being cancelled before the code following them had a chance to resume.
 * - When the channel was [cancelled][ReceiveChannel.cancel], in which case `onUndeliveredElement` is called on every
 *   remaining element in the channel's buffer.
 *
 * Note that `onUndeliveredElement` is called synchronously in an arbitrary context.
 * It should be fast, non-blocking, and should not throw exceptions.
 * Any exception thrown by `onUndeliveredElement` is wrapped into an internal runtime exception
 * which is either rethrown from the caller method or handed off to the exception handler in the current context
 * (see [CoroutineExceptionHandler]) when one is available.
 *
 * A typical usage for `onUndeliveredElement` is to close a resource that is being transferred via the channel. The
 * following code pattern guarantees that opened resources are closed even if the producer, the consumer,
 * and/or the channel are cancelled. Resources are never lost.
 *
 * ```
 * // Create a channel with an onUndeliveredElement block that closes a resource
 * val channel = Channel(onUndeliveredElement = { resource -> resource.close() })
 *
 * // Producer code
 * val resourceToSend = openResource()
 * channel.send(resourceToSend)
 *
 * // Consumer code
 * val resourceReceived = channel.receive()
 * try {
 *     // work with received resource
 * } finally {
 *     resourceReceived.close()
 * }
 * ```
 *
 * > Note that if any work happens between `openResource()` and `channel.send(...)`,
 * > it is your responsibility to ensure that resource gets closed in case this additional code fails.
 */
public interface Channel : SendChannel, ReceiveChannel {
    /**
     * Constants for the channel factory function `Channel()`.
     */
    public companion object Factory {
        /**
         * An unlimited buffer capacity.
         *
         * `Channel(UNLIMITED)` creates a channel with an unlimited buffer, which never suspends the sender.
         * The total amount of elements that can be sent to the channel is limited only by the available memory.
         *
         * If [BufferOverflow] is specified for the channel, it is completely ignored,
         * as the channel never suspends the sender.
         *
         * ```
         * val channel = Channel(Channel.UNLIMITED)
         * repeat(1000) {
         *    channel.trySend(it)
         * }
         * repeat(1000) {
         *    check(channel.tryReceive().getOrNull() == it)
         * }
         * ```
         */
        public const val UNLIMITED: Int = Int.MAX_VALUE

        /**
         * The zero buffer capacity.
         *
         * For the default [BufferOverflow] value of [BufferOverflow.SUSPEND],
         * `Channel(RENDEZVOUS)` creates a channel without a buffer.
         * An element is transferred from the sender to the receiver only when [send] and [receive] invocations meet
         * in time (that is, they _rendezvous_),
         * so [send] suspends until another coroutine invokes [receive],
         * and [receive] suspends until another coroutine invokes [send].
         *
         * ```
         * val channel = Channel(Channel.RENDEZVOUS)
         * check(channel.trySend(5).isFailure) // sending fails: no receiver is waiting
         * launch(start = CoroutineStart.UNDISPATCHED) {
         *     val element = channel.receive() // suspends
         *     check(element == 3)
         * }
         * check(channel.trySend(3).isSuccess) // sending succeeds: receiver is waiting
         * ```
         *
         * If a different [BufferOverflow] is specified,
         * `Channel(RENDEZVOUS)` creates a channel with a buffer of size 1:
         *
         * ```
         * val channel = Channel(0, onBufferOverflow = BufferOverflow.DROP_OLDEST)
         * // None of the calls suspend, since the buffer overflow strategy is not SUSPEND
         * channel.send(1)
         * channel.send(2)
         * channel.send(3)
         * check(channel.receive() == 3)
         * ```
         */
        public const val RENDEZVOUS: Int = 0

        /**
         * A single-element buffer with conflating behavior.
         *
         * Specifying [CONFLATED] as the capacity in the `Channel(...)` factory function is equivalent to
         * creating a channel with a buffer of size 1 and a [BufferOverflow] strategy of [BufferOverflow.DROP_OLDEST]:
         * `Channel(1, onBufferOverflow = BufferOverflow.DROP_OLDEST)`.
         * Such a channel buffers at most one element and conflates all subsequent `send` and `trySend` invocations
         * so that the receiver always gets the last element sent, **losing** the previously sent elements:
         * see the "Undelivered elements" section in the [Channel] documentation.
         * [Sending][send] to this channel never suspends, and [trySend] always succeeds.
         *
         * ```
         * val channel = Channel(Channel.CONFLATED)
         * channel.send(1)
         * channel.send(2)
         * channel.send(3)
         * check(channel.receive() == 3)
         * ```
         *
         * Specifying a [BufferOverflow] other than [BufferOverflow.SUSPEND] is not allowed with [CONFLATED], and
         * an [IllegalArgumentException] is thrown if such a combination is used.
         * For creating a conflated channel that instead keeps the existing element in the channel and throws out
         * the new one, use `Channel(1, onBufferOverflow = BufferOverflow.DROP_LATEST)`.
         */
        public const val CONFLATED: Int = -1

        /**
         * A channel capacity marker that is substituted by the default buffer capacity.
         *
         * When passed as a parameter to the `Channel(...)` factory function, the default buffer capacity is used.
         * For [BufferOverflow.SUSPEND] (the default buffer overflow strategy), the default capacity is 64,
         * but on the JVM it can be overridden by setting the [DEFAULT_BUFFER_PROPERTY_NAME] system property.
         * The overridden value is used for all channels created with a default buffer capacity,
         * including those created in third-party libraries.
         *
         * ```
         * val channel = Channel(Channel.BUFFERED)
         * repeat(100) {
         *     channel.trySend(it)
         * }
         * channel.close()
         * // The check can fail if the default buffer capacity is changed
         * check(channel.toList() == (0..<64).toList())
         * ```
         *
         * If a different [BufferOverflow] is specified, `Channel(BUFFERED)` creates a channel with a buffer of size 1:
         *
         * ```
         * val channel = Channel(Channel.BUFFERED, onBufferOverflow = BufferOverflow.DROP_OLDEST)
         * channel.send(1)
         * channel.send(2)
         * channel.send(3)
         * channel.close()
         * check(channel.toList() == listOf(3))
         * ```
         */
        public const val BUFFERED: Int = -2

        // only for internal use, cannot be used with Channel(...)
        internal const val OPTIONAL_CHANNEL = -3

        /**
         * Name of the JVM system property for the default channel capacity (64 by default).
         *
         * See [BUFFERED] for details on how this property is used.
         *
         * Setting this property affects the default channel capacity for channel constructors,
         * channel-backed coroutines and flow operators that imply channel usage,
         * including ones defined in 3rd-party libraries.
         *
         * Usage of this property is highly discouraged and is intended to be used as a last-ditch effort
         * as an immediate measure for hot fixes and duct-taping.
         */
        @DelicateCoroutinesApi
        public const val DEFAULT_BUFFER_PROPERTY_NAME: String = "kotlinx.coroutines.channels.defaultBuffer"

        internal val CHANNEL_DEFAULT_CAPACITY = systemProp(DEFAULT_BUFFER_PROPERTY_NAME,
            64, 1, UNLIMITED - 1
        )
    }
}

/**
 * Creates a channel. See the [Channel] interface documentation for details.
 *
 * This function is the most flexible way to create a channel.
 * It allows specifying the channel's capacity, buffer overflow strategy, and an optional function to call
 * to handle undelivered elements.
 *
 * ```
 * val allocatedResources = HashSet()
 * // An autocloseable resource that must be closed when it is no longer needed
 * class Resource(val id: Int): AutoCloseable {
 *     init {
 *         allocatedResources.add(id)
 *     }
 *     override fun close() {
 *         allocatedResources.remove(id)
 *     }
 * }
 * // A channel with a 15-element buffer that drops the oldest element on buffer overflow
 * // and closes the elements that were not delivered to the consumer
 * val channel = Channel(
 *     capacity = 15,
 *     onBufferOverflow = BufferOverflow.DROP_OLDEST,
 *     onUndeliveredElement = { element -> element.close() }
 * )
 * // A sender's view of the channel
 * val sendChannel: SendChannel = channel
 * repeat(100) {
 *     sendChannel.send(Resource(it))
 * }
 * sendChannel.close()
 * // A receiver's view of the channel
 * val receiveChannel: ReceiveChannel = channel
 * val receivedResources = receiveChannel.toList()
 * // Check that the last 15 sent resources were received
 * check(receivedResources.map { it.id } == (85 until 100).toList())
 * // Close the resources that were successfully received
 * receivedResources.forEach { it.close() }
 * // The dropped resources were closed by the channel itself
 * check(allocatedResources.isEmpty())
 * ```
 *
 * For a full explanation of every parameter and their interaction, see the [Channel] interface documentation.
 *
 * @param capacity either a positive channel capacity or one of the constants defined in [Channel.Factory].
 *   See the "Channel capacity" section in the [Channel] documentation.
 * @param onBufferOverflow configures an action on buffer overflow.
 *   See the "Buffer overflow" section in the [Channel] documentation.
 * @param onUndeliveredElement a function that is called when element was sent but was not delivered to the consumer.
 *   See the "Undelivered elements" section in the [Channel] documentation.
 * @throws IllegalArgumentException when [capacity] < -2
 */
public fun  Channel(
    capacity: Int = RENDEZVOUS,
    onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND,
    onUndeliveredElement: ((E) -> Unit)? = null
): Channel =
    when (capacity) {
        RENDEZVOUS -> {
            if (onBufferOverflow == BufferOverflow.SUSPEND)
                BufferedChannel(RENDEZVOUS, onUndeliveredElement) // an efficient implementation of rendezvous channel
            else
                ConflatedBufferedChannel(1, onBufferOverflow, onUndeliveredElement) // support buffer overflow with buffered channel
        }
        CONFLATED -> {
            require(onBufferOverflow == BufferOverflow.SUSPEND) {
                "CONFLATED capacity cannot be used with non-default onBufferOverflow"
            }
            ConflatedBufferedChannel(1, BufferOverflow.DROP_OLDEST, onUndeliveredElement)
        }
        UNLIMITED -> BufferedChannel(UNLIMITED, onUndeliveredElement) // ignores onBufferOverflow: it has buffer, but it never overflows
        BUFFERED -> { // uses default capacity with SUSPEND
            if (onBufferOverflow == BufferOverflow.SUSPEND) BufferedChannel(CHANNEL_DEFAULT_CAPACITY, onUndeliveredElement)
            else ConflatedBufferedChannel(1, onBufferOverflow, onUndeliveredElement)
        }
        else -> {
            if (onBufferOverflow === BufferOverflow.SUSPEND) BufferedChannel(capacity, onUndeliveredElement)
            else ConflatedBufferedChannel(capacity, onBufferOverflow, onUndeliveredElement)
        }
    }

@Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.4.0, binary compatibility with earlier versions")
public fun  Channel(capacity: Int = RENDEZVOUS): Channel = Channel(capacity)

/**
 * Indicates an attempt to [send][SendChannel.send] to a [closed-for-sending][SendChannel.isClosedForSend] channel
 * that was [closed][SendChannel.close] without a cause.
 *
 * If a cause was provided, that cause is thrown from [send][SendChannel.send] instead of this exception.
 * In particular, if the channel was closed because it was [cancelled][ReceiveChannel.cancel],
 * this exception will never be thrown: either the `cause` of the cancellation is thrown,
 * or a new [CancellationException] gets constructed to be thrown from [SendChannel.send].
 *
 * This exception is a subclass of [IllegalStateException], because the sender should not attempt to send to a closed
 * channel after it itself has [closed][SendChannel.close] it, and indicates an error on the part of the programmer.
 * Usually, this exception can be avoided altogether by restructuring the code.
 */
public class ClosedSendChannelException(message: String?) : IllegalStateException(message)

/**
 * Indicates an attempt to [receive][ReceiveChannel.receive] from a
 * [closed-for-receiving][ReceiveChannel.isClosedForReceive] channel
 * that was [closed][SendChannel.close] without a cause.
 *
 * If a clause was provided, that clause is thrown from [receive][ReceiveChannel.receive] instead of this exception.
 * In particular, if the channel was closed because it was [cancelled][ReceiveChannel.cancel],
 * this exception will never be thrown: either the `cause` of the cancellation is thrown,
 * or a new [CancellationException] gets constructed to be thrown from [ReceiveChannel.receive].
 *
 * This exception is a subclass of [NoSuchElementException] to be consistent with plain collections.
 */
public class ClosedReceiveChannelException(message: String?) : NoSuchElementException(message)




© 2015 - 2025 Weber Informatics LLC | Privacy Policy