diff --git a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api index 2a97c37f6c..015a257854 100644 --- a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api +++ b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api @@ -820,7 +820,9 @@ public final class kotlinx/coroutines/channels/ChannelsKt { public static synthetic fun takeWhile$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; public static final fun toChannel (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/SendChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun toCollection (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Collection;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun toList (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun toList (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final synthetic fun toList (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun toList$default (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/List;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static final fun toMap (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Map;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final synthetic fun toMap (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final synthetic fun toMutableList (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; diff --git a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.klib.api b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.klib.api index a839ffcfa0..c76a81d1d8 100644 --- a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.klib.api +++ b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.klib.api @@ -1104,6 +1104,7 @@ final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel< final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/single(): #A // kotlinx.coroutines.channels/single|single@kotlinx.coroutines.channels.ReceiveChannel<0:0>(){0§}[0] final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/singleOrNull(): #A? // kotlinx.coroutines.channels/singleOrNull|singleOrNull@kotlinx.coroutines.channels.ReceiveChannel<0:0>(){0§}[0] final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/toList(): kotlin.collections/List<#A> // kotlinx.coroutines.channels/toList|toList@kotlinx.coroutines.channels.ReceiveChannel<0:0>(){0§}[0] +final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/toList(kotlin.collections/MutableList<#A> = ...): kotlin.collections/List<#A> // kotlinx.coroutines.channels/toList|toList@kotlinx.coroutines.channels.ReceiveChannel<0:0>(kotlin.collections.MutableList<0:0>){0§}[0] final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/toMutableList(): kotlin.collections/MutableList<#A> // kotlinx.coroutines.channels/toMutableList|toMutableList@kotlinx.coroutines.channels.ReceiveChannel<0:0>(){0§}[0] final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/toMutableSet(): kotlin.collections/MutableSet<#A> // kotlinx.coroutines.channels/toMutableSet|toMutableSet@kotlinx.coroutines.channels.ReceiveChannel<0:0>(){0§}[0] final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/toSet(): kotlin.collections/Set<#A> // kotlinx.coroutines.channels/toSet|toSet@kotlinx.coroutines.channels.ReceiveChannel<0:0>(){0§}[0] diff --git a/kotlinx-coroutines-core/common/src/channels/Channels.common.kt b/kotlinx-coroutines-core/common/src/channels/Channels.common.kt index 15534b08fe..3bf9219be8 100644 --- a/kotlinx-coroutines-core/common/src/channels/Channels.common.kt +++ b/kotlinx-coroutines-core/common/src/channels/Channels.common.kt @@ -162,7 +162,8 @@ public suspend inline fun ReceiveChannel.consumeEach(action: (E) -> Unit) } /** - * Returns a [List] containing all the elements sent to this channel, preserving their order. + * [Consumes][consume] the elements of this channel into the given [destination] mutable list. + * If none is provided, a new [ArrayList] will be created. * * This function will attempt to receive elements and put them into the list until the channel is * [closed][SendChannel.close]. @@ -172,6 +173,8 @@ public suspend inline fun ReceiveChannel.consumeEach(action: (E) -> Unit) * until exhausting it. * * If the channel is [closed][SendChannel.close] with a cause, [toList] will rethrow that cause. + * However, the [destination] list is left in a consistent state containing all the elements received from the channel + * up to that point. * * The operation is _terminal_. * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. @@ -188,11 +191,8 @@ public suspend inline fun ReceiveChannel.consumeEach(action: (E) -> Unit) * check(channel.toList() == values) * ``` */ -public suspend fun ReceiveChannel.toList(): List = buildList { - consumeEach { - add(it) - } -} +public suspend fun ReceiveChannel.toList(destination: MutableList = ArrayList()): List = + consumeEach(destination::add).let { destination } @PublishedApi internal fun ReceiveChannel<*>.cancelConsumed(cause: Throwable?) { @@ -201,3 +201,5 @@ internal fun ReceiveChannel<*>.cancelConsumed(cause: Throwable?) { }) } +@Deprecated("Preserving binary compatibility, was stable", level = DeprecationLevel.HIDDEN) +public suspend fun ReceiveChannel.toList(): List = toList(ArrayList()) diff --git a/kotlinx-coroutines-core/common/test/channels/ChannelsTest.kt b/kotlinx-coroutines-core/common/test/channels/ChannelsTest.kt index 235609c804..83ae2f4e24 100644 --- a/kotlinx-coroutines-core/common/test/channels/ChannelsTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/ChannelsTest.kt @@ -85,14 +85,13 @@ class ChannelsTest: TestBase() { } @Test - fun testEmptyList() = runTest { + fun testEmptyToList() = runTest { assertTrue(emptyList().asReceiveChannel().toList().isEmpty()) } @Test fun testToList() = runTest { assertEquals(testList, testList.asReceiveChannel().toList()) - } @Test @@ -104,6 +103,39 @@ class ChannelsTest: TestBase() { } } + @Test + fun testEmptyToListWithDestination() = runTest { + val initialList = listOf(-1, -2, -3) + val destination = initialList.toMutableList() + emptyList().asReceiveChannel().toList(destination) + assertEquals(initialList, destination) + } + + @Test + fun testToListWithDestination() = runTest { + val initialList = listOf(-1, -2, -3) + val destination = initialList.toMutableList() + testList.asReceiveChannel().toList(destination) + assertEquals(initialList + testList, destination) + } + + @Test + fun testToListWithDestinationOnFailedChannel() = runTest { + val initialList = listOf(-1, -2, -3) + val destination = initialList.toMutableList() + val channel = Channel(10) + val elementsToSend = (1..5).toList() + elementsToSend.forEach { + val result = channel.trySend(it) + assertTrue(result.isSuccess) + } + channel.close(TestException()) + assertFailsWith { + channel.toList(destination) + } + assertEquals(initialList + elementsToSend, destination) + } + private fun Iterable.asReceiveChannel(context: CoroutineContext = Dispatchers.Unconfined): ReceiveChannel = GlobalScope.produce(context) { for (element in this@asReceiveChannel)