From c1cb3231a885cc98d65862aa615abac5bc35d435 Mon Sep 17 00:00:00 2001 From: Nikita Koval Date: Wed, 19 Apr 2023 16:24:36 +0200 Subject: [PATCH 1/9] Bump Lincheck version to 2.17 (#3720) Signed-off-by: Nikita Koval --- gradle.properties | 2 +- kotlinx-coroutines-core/jvm/test/AbstractLincheckTest.kt | 4 +--- .../jvm/test/internal/OnDemandAllocatingPoolLincheckTest.kt | 2 -- .../jvm/test/lincheck/ChannelsLincheckTest.kt | 5 +---- .../jvm/test/lincheck/LockFreeTaskQueueLincheckTest.kt | 5 +---- .../jvm/test/lincheck/MutexLincheckTest.kt | 3 --- .../jvm/test/lincheck/ResizableAtomicArrayLincheckTest.kt | 5 +---- .../jvm/test/lincheck/SemaphoreLincheckTest.kt | 2 -- 8 files changed, 5 insertions(+), 23 deletions(-) diff --git a/gradle.properties b/gradle.properties index 966a90daf6..5ee75c42e4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ junit5_version=5.7.0 atomicfu_version=0.20.2 knit_version=0.4.0 html_version=0.7.2 -lincheck_version=2.16 +lincheck_version=2.17 dokka_version=1.8.10 byte_buddy_version=1.10.9 reactor_version=3.4.1 diff --git a/kotlinx-coroutines-core/jvm/test/AbstractLincheckTest.kt b/kotlinx-coroutines-core/jvm/test/AbstractLincheckTest.kt index bdb615d83c..da73ca6220 100644 --- a/kotlinx-coroutines-core/jvm/test/AbstractLincheckTest.kt +++ b/kotlinx-coroutines-core/jvm/test/AbstractLincheckTest.kt @@ -9,7 +9,7 @@ import org.jetbrains.kotlinx.lincheck.strategy.stress.* import org.jetbrains.kotlinx.lincheck.verifier.* import org.junit.* -abstract class AbstractLincheckTest : VerifierState() { +abstract class AbstractLincheckTest { open fun > O.customize(isStressTest: Boolean): O = this open fun ModelCheckingOptions.customize(isStressTest: Boolean): ModelCheckingOptions = this open fun StressOptions.customize(isStressTest: Boolean): StressOptions = this @@ -41,6 +41,4 @@ abstract class AbstractLincheckTest : VerifierState() { .actorsPerThread(if (isStressTest) 3 else 2) .actorsAfter(if (isStressTest) 3 else 0) .customize(isStressTest) - - override fun extractState(): Any = error("Not implemented") } diff --git a/kotlinx-coroutines-core/jvm/test/internal/OnDemandAllocatingPoolLincheckTest.kt b/kotlinx-coroutines-core/jvm/test/internal/OnDemandAllocatingPoolLincheckTest.kt index de9ab8d5cd..9655890c4f 100644 --- a/kotlinx-coroutines-core/jvm/test/internal/OnDemandAllocatingPoolLincheckTest.kt +++ b/kotlinx-coroutines-core/jvm/test/internal/OnDemandAllocatingPoolLincheckTest.kt @@ -27,8 +27,6 @@ abstract class OnDemandAllocatingPoolLincheckTest(maxCapacity: Int) : AbstractLi @Operation fun close(): String = pool.close().sorted().toString() - - override fun extractState(): Any = pool.stateRepresentation() } abstract class OnDemandAllocatingSequentialPool(private val maxCapacity: Int) { diff --git a/kotlinx-coroutines-core/jvm/test/lincheck/ChannelsLincheckTest.kt b/kotlinx-coroutines-core/jvm/test/lincheck/ChannelsLincheckTest.kt index 87ed74b715..092ef6fc52 100644 --- a/kotlinx-coroutines-core/jvm/test/lincheck/ChannelsLincheckTest.kt +++ b/kotlinx-coroutines-core/jvm/test/lincheck/ChannelsLincheckTest.kt @@ -16,7 +16,6 @@ import org.jetbrains.kotlinx.lincheck.annotations.* import org.jetbrains.kotlinx.lincheck.annotations.Operation import org.jetbrains.kotlinx.lincheck.paramgen.* import org.jetbrains.kotlinx.lincheck.strategy.managed.modelchecking.* -import org.jetbrains.kotlinx.lincheck.verifier.* class RendezvousChannelLincheckTest : ChannelLincheckTestBaseWithOnSend( c = Channel(RENDEZVOUS), @@ -176,7 +175,7 @@ private class NumberedCancellationException(number: Int) : CancellationException } -abstract class SequentialIntChannelBase(private val capacity: Int) : VerifierState() { +abstract class SequentialIntChannelBase(private val capacity: Int) { private val senders = ArrayList, Int>>() private val receivers = ArrayList>() private val buffer = ArrayList() @@ -266,8 +265,6 @@ abstract class SequentialIntChannelBase(private val capacity: Int) : VerifierSta if (closedMessage !== null) return false return buffer.isEmpty() && senders.isEmpty() } - - override fun extractState() = buffer to closedMessage } private fun CancellableContinuation.resume(res: T): Boolean { diff --git a/kotlinx-coroutines-core/jvm/test/lincheck/LockFreeTaskQueueLincheckTest.kt b/kotlinx-coroutines-core/jvm/test/lincheck/LockFreeTaskQueueLincheckTest.kt index 2a9164e1d7..11f5535b5a 100644 --- a/kotlinx-coroutines-core/jvm/test/lincheck/LockFreeTaskQueueLincheckTest.kt +++ b/kotlinx-coroutines-core/jvm/test/lincheck/LockFreeTaskQueueLincheckTest.kt @@ -30,8 +30,6 @@ internal abstract class AbstractLockFreeTaskQueueWithoutRemoveLincheckTest( override fun > O.customize(isStressTest: Boolean): O = verifier(QuiescentConsistencyVerifier::class.java) - override fun extractState() = q.map { it } to q.isClosed() - override fun ModelCheckingOptions.customize(isStressTest: Boolean) = checkObstructionFreedom() } @@ -42,9 +40,8 @@ internal class MCLockFreeTaskQueueWithRemoveLincheckTest : AbstractLockFreeTaskQ fun removeFirstOrNull() = q.removeFirstOrNull() } -@OpGroupConfig(name = "consumer", nonParallel = true) internal class SCLockFreeTaskQueueWithRemoveLincheckTest : AbstractLockFreeTaskQueueWithoutRemoveLincheckTest(singleConsumer = true) { @QuiescentConsistent - @Operation(group = "consumer") + @Operation(nonParallelGroup = "consumer") fun removeFirstOrNull() = q.removeFirstOrNull() } \ No newline at end of file diff --git a/kotlinx-coroutines-core/jvm/test/lincheck/MutexLincheckTest.kt b/kotlinx-coroutines-core/jvm/test/lincheck/MutexLincheckTest.kt index 6fd28e424e..983a64acda 100644 --- a/kotlinx-coroutines-core/jvm/test/lincheck/MutexLincheckTest.kt +++ b/kotlinx-coroutines-core/jvm/test/lincheck/MutexLincheckTest.kt @@ -40,8 +40,5 @@ class MutexLincheckTest : AbstractLincheckTest() { override fun > O.customize(isStressTest: Boolean): O = actorsBefore(0) - // state[i] == true <=> mutex.holdsLock(i) with the only exception for 0 that specifies `null`. - override fun extractState() = (1..2).map { mutex.holdsLock(it) } + mutex.isLocked - private val Int.asOwnerOrNull get() = if (this == 0) null else this } diff --git a/kotlinx-coroutines-core/jvm/test/lincheck/ResizableAtomicArrayLincheckTest.kt b/kotlinx-coroutines-core/jvm/test/lincheck/ResizableAtomicArrayLincheckTest.kt index 1948a78ecc..e937b37e08 100644 --- a/kotlinx-coroutines-core/jvm/test/lincheck/ResizableAtomicArrayLincheckTest.kt +++ b/kotlinx-coroutines-core/jvm/test/lincheck/ResizableAtomicArrayLincheckTest.kt @@ -11,17 +11,14 @@ import org.jetbrains.kotlinx.lincheck.paramgen.* @Param(name = "index", gen = IntGen::class, conf = "0:4") @Param(name = "value", gen = IntGen::class, conf = "1:5") -@OpGroupConfig(name = "sync", nonParallel = true) class ResizableAtomicArrayLincheckTest : AbstractLincheckTest() { private val a = ResizableAtomicArray(2) @Operation fun get(@Param(name = "index") index: Int): Int? = a[index] - @Operation(group = "sync") + @Operation(nonParallelGroup = "writer") fun set(@Param(name = "index") index: Int, @Param(name = "value") value: Int) { a.setSynchronized(index, value) } - - override fun extractState() = (0..4).map { a[it] } } \ No newline at end of file diff --git a/kotlinx-coroutines-core/jvm/test/lincheck/SemaphoreLincheckTest.kt b/kotlinx-coroutines-core/jvm/test/lincheck/SemaphoreLincheckTest.kt index 09dee56c51..d093e8066a 100644 --- a/kotlinx-coroutines-core/jvm/test/lincheck/SemaphoreLincheckTest.kt +++ b/kotlinx-coroutines-core/jvm/test/lincheck/SemaphoreLincheckTest.kt @@ -25,8 +25,6 @@ abstract class SemaphoreLincheckTestBase(permits: Int) : AbstractLincheckTest() override fun > O.customize(isStressTest: Boolean): O = actorsBefore(0) - override fun extractState() = semaphore.availablePermits - override fun ModelCheckingOptions.customize(isStressTest: Boolean) = checkObstructionFreedom() } From ea78820f0358a1604b671f3fece8cdef86d1a351 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 26 Apr 2023 12:53:20 +0200 Subject: [PATCH 2/9] Enable -Xjvm-default=all for benchmarks (#3727) And remove usages of JvmDefault, which is going to be deprecated with error in KT-54746. --- benchmarks/build.gradle.kts | 2 +- .../kotlin/benchmarks/flow/scrabble/ShakespearePlaysScrabble.kt | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index b4629809db..e64f18905f 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -24,7 +24,7 @@ java { tasks.named("compileJmhKotlin") { kotlinOptions { jvmTarget = "1.8" - freeCompilerArgs += "-Xjvm-default=enable" + freeCompilerArgs += "-Xjvm-default=all" } } diff --git a/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/ShakespearePlaysScrabble.kt b/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/ShakespearePlaysScrabble.kt index 006d36c04b..10433fcb45 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/ShakespearePlaysScrabble.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/ShakespearePlaysScrabble.kt @@ -34,14 +34,12 @@ abstract class ShakespearePlaysScrabble { public interface LongWrapper { fun get(): Long - @JvmDefault fun incAndSet(): LongWrapper { return object : LongWrapper { override fun get(): Long = this@LongWrapper.get() + 1L } } - @JvmDefault fun add(other: LongWrapper): LongWrapper { return object : LongWrapper { override fun get(): Long = this@LongWrapper.get() + other.get() From f5370890634f342165742c44c67485961c767c3c Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 26 Apr 2023 16:15:00 +0200 Subject: [PATCH 3/9] Filter out "java.util.concurrent" frames from debugging test machinery (#3723) We do rely on java.util.concurrent primitives for rendezvous in tests, but we cannot rely on their internal stacktraces in tests, thus filtering them out from test data. Otherwise, tests outcome depends on the underlying JDK version Fixes #3700 --- .../test/RunningThreadStackMergeTest.kt | 4 ++-- .../test/StacktraceUtils.kt | 23 +++++++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/kotlinx-coroutines-debug/test/RunningThreadStackMergeTest.kt b/kotlinx-coroutines-debug/test/RunningThreadStackMergeTest.kt index 965f17883f..89dd914879 100644 --- a/kotlinx-coroutines-debug/test/RunningThreadStackMergeTest.kt +++ b/kotlinx-coroutines-debug/test/RunningThreadStackMergeTest.kt @@ -1,7 +1,6 @@ /* * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") package kotlinx.coroutines.debug import kotlinx.coroutines.* @@ -170,7 +169,8 @@ class RunningThreadStackMergeTest : DebugTestBase() { assertTrue(true) } - @Test + @Test // IDEA-specific debugger API test + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") fun testActiveThread() = runBlocking { launchCoroutine() awaitCoroutineStarted() diff --git a/kotlinx-coroutines-debug/test/StacktraceUtils.kt b/kotlinx-coroutines-debug/test/StacktraceUtils.kt index 55bdd7e0b0..869e29751c 100644 --- a/kotlinx-coroutines-debug/test/StacktraceUtils.kt +++ b/kotlinx-coroutines-debug/test/StacktraceUtils.kt @@ -74,6 +74,18 @@ private fun cleanBlockHoundTraces(frames: List): List { return result } +/** + * Removes all frames that contain "java.util.concurrent" in it. + * + * We do leverage Java's locks for proper rendezvous and to fix the coroutine stack's state, + * but this API doesn't have (nor expected to) stable stacktrace, so we are filtering all such + * frames out. + * + * See https://github.com/Kotlin/kotlinx.coroutines/issues/3700 for the example of failure + */ +private fun removeJavaUtilConcurrentTraces(frames: List): List = + frames.filter { !it.contains("java.util.concurrent") } + private data class CoroutineDump( val header: CoroutineDumpHeader, val coroutineStackTrace: List, @@ -185,7 +197,9 @@ public fun verifyDump(vararg expectedTraces: String, ignoredCoroutine: String? = .drop(1) // Parse dumps and filter out ignored coroutines .mapNotNull { trace -> - val dump = CoroutineDump.parse(trace, traceCleaner = ::cleanBlockHoundTraces) + val dump = CoroutineDump.parse(trace, { + removeJavaUtilConcurrentTraces(cleanBlockHoundTraces(it)) + }) if (dump.header.className == ignoredCoroutine) { null } else { @@ -194,9 +208,10 @@ public fun verifyDump(vararg expectedTraces: String, ignoredCoroutine: String? = } assertEquals(expectedTraces.size, dumps.size) - dumps.zip(expectedTraces.map(CoroutineDump::parse)).forEach { (dump, expectedDump) -> - dump.verify(expectedDump) - } + dumps.zip(expectedTraces.map { CoroutineDump.parse(it, ::removeJavaUtilConcurrentTraces) }) + .forEach { (dump, expectedDump) -> + dump.verify(expectedDump) + } } public fun String.trimPackage() = replace("kotlinx.coroutines.debug.", "") From d6f1403e3b502c181b678a954f4160f659dbbd9d Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 26 Apr 2023 17:34:00 +0200 Subject: [PATCH 4/9] Fix MutexCancellationStressTest flakiness (#3724) --- .../jvm/test/MutexCancellationStressTest.kt | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/kotlinx-coroutines-core/jvm/test/MutexCancellationStressTest.kt b/kotlinx-coroutines-core/jvm/test/MutexCancellationStressTest.kt index eb6360dac0..20798b837d 100644 --- a/kotlinx-coroutines-core/jvm/test/MutexCancellationStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/MutexCancellationStressTest.kt @@ -8,7 +8,11 @@ import kotlinx.coroutines.internal.* import kotlinx.coroutines.selects.* import kotlinx.coroutines.sync.* import org.junit.* +import org.junit.Test import java.util.concurrent.* +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicInteger +import kotlin.test.* class MutexCancellationStressTest : TestBase() { @Test @@ -18,13 +22,16 @@ class MutexCancellationStressTest : TestBase() { val mutexOwners = Array(mutexJobNumber) { "$it" } val dispatcher = Executors.newFixedThreadPool(mutexJobNumber + 2).asCoroutineDispatcher() var counter = 0 - val counterLocal = Array(mutexJobNumber) { LocalAtomicInt(0) } - val completed = LocalAtomicInt(0) + val counterLocal = Array(mutexJobNumber) { AtomicInteger(0) } + val completed = AtomicBoolean(false) val mutexJobLauncher: (jobNumber: Int) -> Job = { jobId -> val coroutineName = "MutexJob-$jobId" - launch(dispatcher + CoroutineName(coroutineName)) { - while (completed.value == 0) { + // ATOMIC to always have a chance to proceed + launch(dispatcher + CoroutineName(coroutineName), CoroutineStart.ATOMIC) { + while (!completed.get()) { + // Stress out holdsLock mutex.holdsLock(mutexOwners[(jobId + 1) % mutexJobNumber]) + // Stress out lock-like primitives if (mutex.tryLock(mutexOwners[jobId])) { counterLocal[jobId].incrementAndGet() counter++ @@ -47,18 +54,20 @@ class MutexCancellationStressTest : TestBase() { val mutexJobs = (0 until mutexJobNumber).map { mutexJobLauncher(it) }.toMutableList() val checkProgressJob = launch(dispatcher + CoroutineName("checkProgressJob")) { var lastCounterLocalSnapshot = (0 until mutexJobNumber).map { 0 } - while (completed.value == 0) { - delay(1000) + while (!completed.get()) { + delay(500) + // If we've caught the completion after delay, then there is a chance no progress were made whatsoever, bail out + if (completed.get()) return@launch val c = counterLocal.map { it.value } for (i in 0 until mutexJobNumber) { - assert(c[i] > lastCounterLocalSnapshot[i]) { "No progress in MutexJob-$i" } + assert(c[i] > lastCounterLocalSnapshot[i]) { "No progress in MutexJob-$i, last observed state: ${c[i]}" } } lastCounterLocalSnapshot = c } } val cancellationJob = launch(dispatcher + CoroutineName("cancellationJob")) { var cancellingJobId = 0 - while (completed.value == 0) { + while (!completed.get()) { val jobToCancel = mutexJobs.removeFirst() jobToCancel.cancelAndJoin() mutexJobs += mutexJobLauncher(cancellingJobId) @@ -66,11 +75,11 @@ class MutexCancellationStressTest : TestBase() { } } delay(2000L * stressTestMultiplier) - completed.value = 1 + completed.set(true) cancellationJob.join() mutexJobs.forEach { it.join() } checkProgressJob.join() - check(counter == counterLocal.sumOf { it.value }) + assertEquals(counter, counterLocal.sumOf { it.value }) dispatcher.close() } } From 298419f8accffb496cc8ea11187120a5d65586c0 Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy <52952525+dkhalanskyjb@users.noreply.github.com> Date: Wed, 3 May 2023 11:18:50 +0300 Subject: [PATCH 5/9] Fix the error message if there were uncaught exceptions before test (#3733) With #3449, the unrelated uncaught exceptions can also be caught in this manner. --- kotlinx-coroutines-test/common/src/TestScope.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotlinx-coroutines-test/common/src/TestScope.kt b/kotlinx-coroutines-test/common/src/TestScope.kt index a5a36a8524..e72f0f14f3 100644 --- a/kotlinx-coroutines-test/common/src/TestScope.kt +++ b/kotlinx-coroutines-test/common/src/TestScope.kt @@ -312,7 +312,7 @@ internal fun TestScope.asSpecificImplementation(): TestScopeImpl = when (this) { } internal class UncaughtExceptionsBeforeTest : IllegalStateException( - "There were uncaught exceptions in coroutines launched from TestScope before the test started. Please avoid this," + + "There were uncaught exceptions before the test started. Please avoid this," + " as such exceptions are also reported in a platform-dependent manner so that they are not lost." ) From 25a3553ed0466ca14d5166f9386cc8607d56ac84 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 3 May 2023 10:49:26 +0200 Subject: [PATCH 6/9] =?UTF-8?q?Properly=20recover=20exceptions=20when=20th?= =?UTF-8?q?ey=20are=20constructed=20from=20'Throwable=E2=80=A6=20(#3731)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Properly recover exceptions when they are constructed from 'Throwable(cause)' constructor. And restore the original behaviour. After #1631 this constructor's recovery mechanism was broken because 'Throwable(cause)' changes the message to be equal to 'cause.toString()', which isn't equal to the original message. Also, make reflective constructor choice undependable from source-code order Fixes #3714 --- .idea/codeStyles/Project.xml | 2 +- .../jvm/src/internal/ExceptionsConstructor.kt | 68 ++++++++++--------- .../jvm/src/internal/StackTraceRecovery.kt | 21 ++---- .../channels/testReceiveFromChannel.txt | 3 +- .../resume-mode/testUnconfined.txt | 3 +- .../StackTraceRecoveryCustomExceptionsTest.kt | 20 ++++++ 6 files changed, 66 insertions(+), 51 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 62fd5c7dfd..461a31ed76 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -12,4 +12,4 @@ - \ No newline at end of file + diff --git a/kotlinx-coroutines-core/jvm/src/internal/ExceptionsConstructor.kt b/kotlinx-coroutines-core/jvm/src/internal/ExceptionsConstructor.kt index de15225266..03308f6137 100644 --- a/kotlinx-coroutines-core/jvm/src/internal/ExceptionsConstructor.kt +++ b/kotlinx-coroutines-core/jvm/src/internal/ExceptionsConstructor.kt @@ -32,42 +32,48 @@ internal fun tryCopyException(exception: E): E? { private fun createConstructor(clz: Class): Ctor { val nullResult: Ctor = { null } // Pre-cache class - // Skip reflective copy if an exception has additional fields (that are usually populated in user-defined constructors) + // Skip reflective copy if an exception has additional fields (that are typically populated in user-defined constructors) if (throwableFields != clz.fieldsCountOrDefault(0)) return nullResult /* - * Try to reflectively find constructor(), constructor(message, cause), constructor(cause) or constructor(message). - * Exceptions are shared among coroutines, so we should copy exception before recovering current stacktrace. - */ - val constructors = clz.constructors.sortedByDescending { it.parameterTypes.size } - for (constructor in constructors) { - val result = createSafeConstructor(constructor) - if (result != null) return result - } - return nullResult -} - -private fun createSafeConstructor(constructor: Constructor<*>): Ctor? { - val p = constructor.parameterTypes - return when (p.size) { - 2 -> when { - p[0] == String::class.java && p[1] == Throwable::class.java -> - safeCtor { e -> constructor.newInstance(e.message, e) as Throwable } - else -> null + * Try to reflectively find constructor(message, cause), constructor(message), constructor(cause), or constructor(), + * in that order of priority. + * Exceptions are shared among coroutines, so we should copy exception before recovering current stacktrace. + * + * By default, Java's reflection iterates over ctors in the source-code order and the sorting is stable, so we can + * not rely on the order of iteration. Instead, we assign a unique priority to each ctor type. + */ + return clz.constructors.map { constructor -> + val p = constructor.parameterTypes + when (p.size) { + 2 -> when { + p[0] == String::class.java && p[1] == Throwable::class.java -> + safeCtor { e -> constructor.newInstance(e.message, e) as Throwable } to 3 + else -> null to -1 + } + 1 -> when (p[0]) { + String::class.java -> + safeCtor { e -> (constructor.newInstance(e.message) as Throwable).also { it.initCause(e) } } to 2 + Throwable::class.java -> + safeCtor { e -> constructor.newInstance(e) as Throwable } to 1 + else -> null to -1 + } + 0 -> safeCtor { e -> (constructor.newInstance() as Throwable).also { it.initCause(e) } } to 0 + else -> null to -1 } - 1 -> when (p[0]) { - Throwable::class.java -> - safeCtor { e -> constructor.newInstance(e) as Throwable } - String::class.java -> - safeCtor { e -> (constructor.newInstance(e.message) as Throwable).also { it.initCause(e) } } - else -> null - } - 0 -> safeCtor { e -> (constructor.newInstance() as Throwable).also { it.initCause(e) } } - else -> null - } + }.maxByOrNull(Pair<*, Int>::second)?.first ?: nullResult } -private inline fun safeCtor(crossinline block: (Throwable) -> Throwable): Ctor = - { e -> runCatching { block(e) }.getOrNull() } +private fun safeCtor(block: (Throwable) -> Throwable): Ctor = { e -> + runCatching { + val result = block(e) + /* + * Verify that the new exception has the same message as the original one (bail out if not, see #1631) + * or if the new message complies the contract from `Throwable(cause).message` contract. + */ + if (e.message != result.message && result.message != e.toString()) null + else result + }.getOrNull() +} private fun Class<*>.fieldsCountOrDefault(defaultValue: Int) = kotlin.runCatching { fieldsCount() }.getOrDefault(defaultValue) diff --git a/kotlinx-coroutines-core/jvm/src/internal/StackTraceRecovery.kt b/kotlinx-coroutines-core/jvm/src/internal/StackTraceRecovery.kt index 5193b0dc26..afc9989646 100644 --- a/kotlinx-coroutines-core/jvm/src/internal/StackTraceRecovery.kt +++ b/kotlinx-coroutines-core/jvm/src/internal/StackTraceRecovery.kt @@ -33,16 +33,16 @@ private val stackTraceRecoveryClassName = runCatching { internal actual fun recoverStackTrace(exception: E): E { if (!RECOVER_STACK_TRACES) return exception // No unwrapping on continuation-less path: exception is not reported multiple times via slow paths - val copy = tryCopyAndVerify(exception) ?: return exception + val copy = tryCopyException(exception) ?: return exception return copy.sanitizeStackTrace() } private fun E.sanitizeStackTrace(): E { val stackTrace = stackTrace val size = stackTrace.size - val lastIntrinsic = stackTrace.frameIndex(stackTraceRecoveryClassName) + val lastIntrinsic = stackTrace.indexOfLast { stackTraceRecoveryClassName == it.className } val startIndex = lastIntrinsic + 1 - val endIndex = stackTrace.frameIndex(baseContinuationImplClassName) + val endIndex = stackTrace.firstFrameIndex(baseContinuationImplClassName) val adjustment = if (endIndex == -1) 0 else size - endIndex val trace = Array(size - lastIntrinsic - adjustment) { if (it == 0) { @@ -70,7 +70,7 @@ private fun recoverFromStackFrame(exception: E, continuation: Co val (cause, recoveredStacktrace) = exception.causeAndStacktrace() // Try to create an exception of the same type and get stacktrace from continuation - val newException = tryCopyAndVerify(cause) ?: return exception + val newException = tryCopyException(cause) ?: return exception // Update stacktrace val stacktrace = createStackTrace(continuation) if (stacktrace.isEmpty()) return exception @@ -82,14 +82,6 @@ private fun recoverFromStackFrame(exception: E, continuation: Co return createFinalException(cause, newException, stacktrace) } -private fun tryCopyAndVerify(exception: E): E? { - val newException = tryCopyException(exception) ?: return null - // Verify that the new exception has the same message as the original one (bail out if not, see #1631) - // CopyableThrowable has control over its message and thus can modify it the way it wants - if (exception !is CopyableThrowable<*> && newException.message != exception.message) return null - return newException -} - /* * Here we partially copy original exception stackTrace to make current one much prettier. * E.g. for @@ -109,7 +101,7 @@ private fun tryCopyAndVerify(exception: E): E? { private fun createFinalException(cause: E, result: E, resultStackTrace: ArrayDeque): E { resultStackTrace.addFirst(ARTIFICIAL_FRAME) val causeTrace = cause.stackTrace - val size = causeTrace.frameIndex(baseContinuationImplClassName) + val size = causeTrace.firstFrameIndex(baseContinuationImplClassName) if (size == -1) { result.stackTrace = resultStackTrace.toTypedArray() return result @@ -157,7 +149,6 @@ private fun mergeRecoveredTraces(recoveredStacktrace: Array, } } -@Suppress("NOTHING_TO_INLINE") internal actual suspend inline fun recoverAndThrow(exception: Throwable): Nothing { if (!RECOVER_STACK_TRACES) throw exception suspendCoroutineUninterceptedOrReturn { @@ -198,7 +189,7 @@ private fun createStackTrace(continuation: CoroutineStackFrame): ArrayDeque.frameIndex(methodName: String) = indexOfFirst { methodName == it.className } +private fun Array.firstFrameIndex(methodName: String) = indexOfFirst { methodName == it.className } private fun StackTraceElement.elementWiseEquals(e: StackTraceElement): Boolean { /* diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testReceiveFromChannel.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testReceiveFromChannel.txt index 64085ad329..da3558ba30 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testReceiveFromChannel.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testReceiveFromChannel.txt @@ -1,5 +1,4 @@ kotlinx.coroutines.RecoverableTestException - at kotlinx.coroutines.internal.StackTraceRecoveryKt.recoverStackTrace(StackTraceRecovery.kt) at kotlinx.coroutines.channels.BufferedChannel.receive$suspendImpl(BufferedChannel.kt) at kotlinx.coroutines.channels.BufferedChannel.receive(BufferedChannel.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.channelReceive(StackTraceRecoveryChannelsTest.kt) @@ -7,4 +6,4 @@ kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$channelReceive$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt) Caused by: kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testReceiveFromChannel$1$job$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt) - at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt) \ No newline at end of file + at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testUnconfined.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testUnconfined.txt index a8461556d1..9fc7167833 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testUnconfined.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testUnconfined.txt @@ -1,5 +1,4 @@ kotlinx.coroutines.RecoverableTestException - at kotlinx.coroutines.internal.StackTraceRecoveryKt.recoverStackTrace(StackTraceRecovery.kt) at kotlinx.coroutines.channels.BufferedChannel.receive$suspendImpl(BufferedChannel.kt) at kotlinx.coroutines.channels.BufferedChannel.receive(BufferedChannel.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$withContext$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt) @@ -7,4 +6,4 @@ Caused by: kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.access$testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testUnconfined$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt) - at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt) \ No newline at end of file + at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt) diff --git a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryCustomExceptionsTest.kt b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryCustomExceptionsTest.kt index d4e19040a5..0f987e56e0 100644 --- a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryCustomExceptionsTest.kt +++ b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryCustomExceptionsTest.kt @@ -87,6 +87,26 @@ class StackTraceRecoveryCustomExceptionsTest : TestBase() { assertEquals("Token OK", ex.message) } + @Test + fun testNestedExceptionWithCause() = runTest { + val result = runCatching { + coroutineScope { + throw NestedException(IllegalStateException("ERROR")) + } + } + val ex = result.exceptionOrNull() ?: error("Expected to fail") + assertIs(ex) + assertIs(ex.cause) + val originalCause = ex.cause?.cause + assertIs(originalCause) + assertEquals("ERROR", originalCause.message) + } + + class NestedException : RuntimeException { + constructor(cause: Throwable) : super(cause) + constructor() : super() + } + @Test fun testWrongMessageExceptionInChannel() = runTest { val result = produce(SupervisorJob() + Dispatchers.Unconfined) { From 41b4665958be181c76c490f8142fa96f24d9ec72 Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy <52952525+dkhalanskyjb@users.noreply.github.com> Date: Wed, 3 May 2023 17:28:25 +0300 Subject: [PATCH 7/9] Support disabling reporting of uncaught exceptions in tests (#3736) The solution for #1205 may be undesirable for those who already have their own solution, like setting the default exception handlers. In this case, there's a usability issue without the corresponding benefit: instead of all tests being ran and the exceptions from them being reported, unrelated tests may fail, making looking for problems more difficult. This is probably a very rare issue, so we don't provide public API for that, instead introducing a need-to-know internal variable. --- .../api/kotlinx-coroutines-test.api | 2 ++ kotlinx-coroutines-test/common/src/TestScope.kt | 13 ++++++++++++- .../common/src/internal/ExceptionCollector.kt | 6 ++++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/kotlinx-coroutines-test/api/kotlinx-coroutines-test.api b/kotlinx-coroutines-test/api/kotlinx-coroutines-test.api index 00d9fb659e..a41502b2e1 100644 --- a/kotlinx-coroutines-test/api/kotlinx-coroutines-test.api +++ b/kotlinx-coroutines-test/api/kotlinx-coroutines-test.api @@ -121,9 +121,11 @@ public final class kotlinx/coroutines/test/TestScopeKt { public static final fun advanceTimeBy (Lkotlinx/coroutines/test/TestScope;J)V public static final fun advanceTimeBy-HG0u8IE (Lkotlinx/coroutines/test/TestScope;J)V public static final fun advanceUntilIdle (Lkotlinx/coroutines/test/TestScope;)V + public static final fun getCatchNonTestRelatedExceptions ()Z public static final fun getCurrentTime (Lkotlinx/coroutines/test/TestScope;)J public static final fun getTestTimeSource (Lkotlinx/coroutines/test/TestScope;)Lkotlin/time/TimeSource$WithComparableMarks; public static final fun runCurrent (Lkotlinx/coroutines/test/TestScope;)V + public static final fun setCatchNonTestRelatedExceptions (Z)V } public abstract interface class kotlinx/coroutines/test/UncaughtExceptionCaptor { diff --git a/kotlinx-coroutines-test/common/src/TestScope.kt b/kotlinx-coroutines-test/common/src/TestScope.kt index e72f0f14f3..fa6e3930d8 100644 --- a/kotlinx-coroutines-test/common/src/TestScope.kt +++ b/kotlinx-coroutines-test/common/src/TestScope.kt @@ -233,7 +233,9 @@ internal class TestScopeImpl(context: CoroutineContext) : * after the previous one, and learning about such exceptions as soon is possible is nice. */ @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") run { ensurePlatformExceptionHandlerLoaded(ExceptionCollector) } - ExceptionCollector.addOnExceptionCallback(lock, this::reportException) + if (catchNonTestRelatedExceptions) { + ExceptionCollector.addOnExceptionCallback(lock, this::reportException) + } uncaughtExceptions } if (exceptions.isNotEmpty()) { @@ -321,3 +323,12 @@ internal class UncaughtExceptionsBeforeTest : IllegalStateException( */ @ExperimentalCoroutinesApi internal class UncompletedCoroutinesError(message: String) : AssertionError(message) + +/** + * A flag that controls whether [TestScope] should attempt to catch arbitrary exceptions flying through the system. + * If it is enabled, then any exception that is not caught by the user code will be reported as a test failure. + * By default, it is enabled, but some tests may want to disable it to test the behavior of the system when they have + * their own exception handling procedures. + */ +@PublishedApi +internal var catchNonTestRelatedExceptions: Boolean = true diff --git a/kotlinx-coroutines-test/common/src/internal/ExceptionCollector.kt b/kotlinx-coroutines-test/common/src/internal/ExceptionCollector.kt index 90fa763523..70fcb9487f 100644 --- a/kotlinx-coroutines-test/common/src/internal/ExceptionCollector.kt +++ b/kotlinx-coroutines-test/common/src/internal/ExceptionCollector.kt @@ -43,8 +43,10 @@ internal object ExceptionCollector : AbstractCoroutineContextElement(CoroutineEx * Unregisters the callback associated with [owner]. */ fun removeOnExceptionCallback(owner: Any) = synchronized(lock) { - val existingValue = callbacks.remove(owner) - check(existingValue !== null) + if (enabled) { + val existingValue = callbacks.remove(owner) + check(existingValue !== null) + } } /** From 61655331824924d4de4db4d2c872d99ad143c1ae Mon Sep 17 00:00:00 2001 From: Nikita Koval Date: Fri, 5 May 2023 15:18:24 +0200 Subject: [PATCH 8/9] Fix non-linearizability in `BufferedChannel.expandBuffer()` (#3730) Signed-off-by: Nikita Koval --- .../common/src/channels/BufferedChannel.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kotlinx-coroutines-core/common/src/channels/BufferedChannel.kt b/kotlinx-coroutines-core/common/src/channels/BufferedChannel.kt index edb1c3c9f7..b749ee63f8 100644 --- a/kotlinx-coroutines-core/common/src/channels/BufferedChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/BufferedChannel.kt @@ -1220,9 +1220,9 @@ internal open class BufferedChannel( incCompletedExpandBufferAttempts() return } - // Is `bufferEndSegment` outdated? + // Is `bufferEndSegment` outdated or is the segment with the required id already removed? // Find the required segment, creating new ones if needed. - if (segment.id < id) { + if (segment.id != id) { segment = findSegmentBufferEnd(id, segment, b) // Restart if the required segment is removed, or // the linked list of segments is already closed, From 72ef8fd8318665b800e7d831618f0050df7f9116 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Fri, 5 May 2023 15:20:28 +0200 Subject: [PATCH 9/9] Version 1.7.0 --- CHANGES.md | 73 +++++++++++++++++++++++++++ README.md | 12 ++--- gradle.properties | 2 +- integration-testing/gradle.properties | 2 +- kotlinx-coroutines-debug/README.md | 2 +- kotlinx-coroutines-test/README.md | 2 +- ui/coroutines-guide-ui.md | 2 +- 7 files changed, 84 insertions(+), 11 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index acf2d2c28b..c59e3b306e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,78 @@ # Change log for kotlinx.coroutines +## Version 1.7.0 + +### Core API significant improvements + +* New `Channel` implementation with significant performance improvements across the API (#3621). +* New `select` operator implementation: faster, more lightweight, and more robust (#3020). +* `Mutex` and `Semaphore` now share the same underlying data structure (#3020). +* `Dispatchers.IO` is added to K/N (#3205) + * `newFixedThreadPool` and `Dispatchers.Default` implementations on K/N were wholly rewritten to support graceful growth under load (#3595). +* `kotlinx-coroutines-test` rework: + - Add the `timeout` parameter to `runTest` for the whole-test timeout, 10 seconds by default (#3270). This replaces the configuration of quiescence timeouts, which is now deprecated (#3603). + - The `withTimeout` exception messages indicate if the timeout used the virtual time (#3588). + - `TestCoroutineScheduler`, `runTest`, and `TestScope` API are promoted to stable (#3622). + - `runTest` now also fails if there were uncaught exceptions in coroutines not inherited from the test coroutine (#1205). + +### Breaking changes + +* Old K/N memory model is no longer supported (#3375). +* New generic upper bounds were added to reactive integration API where the language since 1.8.0 dictates (#3393). +* `kotlinx-coroutines-core` and `kotlinx-coroutines-jdk8` artifacts were merged into a single artifact (#3268). +* Artificial stackframes in stacktrace recovery no longer contain the `\b` symbol and are now navigable in IDE and supplied with proper documentation (#2291). +* `CoroutineContext.isActive` returns `true` for contexts without any job in them (#3300). + +### Bug fixes and improvements + +* Kotlin version is updated to 1.8.20 +* Atomicfu version is updated to 0.20.2. +* `JavaFx` version is updated to 17.0.2 in `kotlinx-coroutines-javafx` (#3671).. +* JPMS is supported (#2237). Thanks @lion7! +* `BroadcastChannel` and all the corresponding API are deprecated (#2680). +* Added all supported K/N targets (#3601, #812, #855). +* K/N `Dispatchers.Default` is backed by the number of threads equal to the number of available cores (#3366). +* Fixed an issue where some coroutines' internal exceptions were not properly serializable (#3328). +* Introduced `Job.parent` API (#3201). +* Fixed a bug when `TestScheduler` leaked cancelled jobs (#3398). +* `TestScope.timeSource` now provides comparable time marks (#3617). Thanks @hfhbd! +* Fixed an issue when cancelled `withTimeout` handles were preserved in JS runtime (#3440). +* Ensure `awaitFrame` only awaits a single frame when used from the main looper (#3432). Thanks @pablobaxter! +* Obsolete `Class-Path` attribute was removed from `kotlinx-coroutines-debug.jar` manifest (#3361). +* Fixed a bug when `updateThreadContext` operated on the parent context (#3411). +* Added new `Flow.filterIsInstance` extension (#3240). +* `Dispatchers.Default` thread name prefixes are now configurable with system property (#3231). +* Added `Flow.timeout` operator as `@FlowPreview` (#2624). Thanks @pablobaxter! +* Improved the performance of the `future` builder in case of exceptions (#3475). Thanks @He-Pin! +* `Mono.awaitSingleOrNull` now waits for the `onComplete` signal (#3487). +* `Channel.isClosedForSend` and `Channel.isClosedForReceive` are promoted from experimental to delicate (#3448). +* Fixed a data race in native `EventLoop` (#3547). +* `Dispatchers.IO.limitedParallelism(valueLargerThanIOSize)` no longer creates an additional wrapper (#3442). Thanks @dovchinnikov! +* Various `@FlowPreview` and `@ExperimentalCoroutinesApi` are promoted to experimental and stable respectively (#3542, #3097, #3548). +* Performance improvements in `Dispatchers.Default` and `Dispatchers.IO` (#3416, #3418). +* Fixed a bug when internal `suspendCancellableCoroutineReusable` might have hanged (#3613). +* Introduced internal API to process events in the current system dispatcher (#3439). +* Global `CoroutineExceptionHandler` is no longer invoked in case of unprocessed `future` failure (#3452). +* Performance improvements and reduced thread-local pressure for the `withContext` operator (#3592). +* Improved performance of `DebugProbes` (#3527). +* Fixed a bug when the coroutine debugger might have detected the state of a coroutine incorrectly (#3193). +* `CoroutineDispatcher.asExecutor()` runs tasks without dispatching if the dispatcher is unconfined (#3683). Thanks @odedniv! +* `SharedFlow.toMutableList` and `SharedFlow.toSet` lints are introduced (#3706). +* `Channel.invokeOnClose` is promoted to stable API (#3358). +* Improved lock contention in `Dispatchers.Default` and `Dispatchers.IO` during the startup phase (#3652). +* Fixed a bug that led to threads oversubscription in `Dispatchers.Default` (#3642). +* Fixed a bug that allowed `limitedParallelism` to perform dispatches even after the underlying dispatcher was closed (#3672). +* Fixed a bug that prevented stacktrace recovery when the exception's constructor from `cause` was selected (#3714). +* Improved sanitizing of stracktrace-recovered traces (#3714). +* Introduced an internal flag to disable uncaught exceptions reporting in tests as a temporary migration mechanism (#3736). +* Various documentation improvements and fixes. + +### Changelog relative to version 1.7.0-RC + +* Fixed a bug that prevented stacktrace recovery when the exception's constructor from `cause` was selected (#3714). +* Improved sanitizing of stracktrace-recovered traces (#3714). +* Introduced an internal flag to disable uncaught exceptions reporting in tests as a temporary migration mechanism (#3736). + ## Version 1.7.0-RC * Kotlin version is updated to 1.8.20. diff --git a/README.md b/README.md index 283afb85ba..dad7e02f52 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Kotlin Stable](https://kotl.in/badges/stable.svg)](https://kotlinlang.org/docs/components-stability.html) [![JetBrains official project](https://jb.gg/badges/official.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) [![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](https://www.apache.org/licenses/LICENSE-2.0) -[![Download](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.7.0-RC)](https://central.sonatype.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.7.0-RC) +[![Download](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.7.0)](https://central.sonatype.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.7.0) [![Kotlin](https://img.shields.io/badge/kotlin-1.8.20-blue.svg?logo=kotlin)](http://kotlinlang.org) [![Slack channel](https://img.shields.io/badge/chat-slack-green.svg?logo=slack)](https://kotlinlang.slack.com/messages/coroutines/) @@ -85,7 +85,7 @@ Add dependencies (you can also add other modules that you need): org.jetbrains.kotlinx kotlinx-coroutines-core - 1.7.0-RC + 1.7.0 ``` @@ -103,7 +103,7 @@ Add dependencies (you can also add other modules that you need): ```kotlin dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.0-RC") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.0") } ``` @@ -133,7 +133,7 @@ Add [`kotlinx-coroutines-android`](ui/kotlinx-coroutines-android) module as a dependency when using `kotlinx.coroutines` on Android: ```kotlin -implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.0-RC") +implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.0") ``` This gives you access to the Android [Dispatchers.Main] @@ -168,7 +168,7 @@ In common code that should get compiled for different platforms, you can add a d ```kotlin commonMain { dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.0-RC") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.0") } } ``` @@ -180,7 +180,7 @@ Platform-specific dependencies are recommended to be used only for non-multiplat #### JS Kotlin/JS version of `kotlinx.coroutines` is published as -[`kotlinx-coroutines-core-js`](https://central.sonatype.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core-js/1.7.0-RC) +[`kotlinx-coroutines-core-js`](https://central.sonatype.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core-js/1.7.0) (follow the link to get the dependency declaration snippet) and as [`kotlinx-coroutines-core`](https://www.npmjs.com/package/kotlinx-coroutines-core) NPM package. #### Native diff --git a/gradle.properties b/gradle.properties index 5ee75c42e4..2daed3cf4c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ # # Kotlin -version=1.7.0-RC-SNAPSHOT +version=1.7.0-SNAPSHOT group=org.jetbrains.kotlinx kotlin_version=1.8.20 diff --git a/integration-testing/gradle.properties b/integration-testing/gradle.properties index 4644d2346a..ac155f1dac 100644 --- a/integration-testing/gradle.properties +++ b/integration-testing/gradle.properties @@ -1,5 +1,5 @@ kotlin_version=1.8.20 -coroutines_version=1.7.0-RC-SNAPSHOT +coroutines_version=1.7.0-SNAPSHOT asm_version=9.3 kotlin.code.style=official diff --git a/kotlinx-coroutines-debug/README.md b/kotlinx-coroutines-debug/README.md index ff9ba9fffa..117c663afc 100644 --- a/kotlinx-coroutines-debug/README.md +++ b/kotlinx-coroutines-debug/README.md @@ -61,7 +61,7 @@ stacktraces will be dumped to the console. ### Using as JVM agent Debug module can also be used as a standalone JVM agent to enable debug probes on the application startup. -You can run your application with an additional argument: `-javaagent:kotlinx-coroutines-debug-1.7.0-RC.jar`. +You can run your application with an additional argument: `-javaagent:kotlinx-coroutines-debug-1.7.0.jar`. Additionally, on Linux and Mac OS X you can use `kill -5 $pid` command in order to force your application to print all alive coroutines. When used as Java agent, `"kotlinx.coroutines.debug.enable.creation.stack.trace"` system property can be used to control [DebugProbes.enableCreationStackTraces] along with agent startup. diff --git a/kotlinx-coroutines-test/README.md b/kotlinx-coroutines-test/README.md index f2805086bb..fae47fa777 100644 --- a/kotlinx-coroutines-test/README.md +++ b/kotlinx-coroutines-test/README.md @@ -26,7 +26,7 @@ Provided [TestDispatcher] implementations: Add `kotlinx-coroutines-test` to your project test dependencies: ``` dependencies { - testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.0-RC' + testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.0' } ``` diff --git a/ui/coroutines-guide-ui.md b/ui/coroutines-guide-ui.md index e00d74edee..f45c382a2f 100644 --- a/ui/coroutines-guide-ui.md +++ b/ui/coroutines-guide-ui.md @@ -110,7 +110,7 @@ Add dependencies on `kotlinx-coroutines-android` module to the `dependencies { . `app/build.gradle` file: ```groovy -implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.0-RC" +implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.0" ``` You can clone [kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) project from GitHub onto your