From a97d9dbe35f423457114450c9833241ed2a4ccdf Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 6 Apr 2022 01:58:51 -0700 Subject: [PATCH 01/18] Dispatchers.IO doc clarification (#3236) Addresses #3234 --- kotlinx-coroutines-core/jvm/src/Dispatchers.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kotlinx-coroutines-core/jvm/src/Dispatchers.kt b/kotlinx-coroutines-core/jvm/src/Dispatchers.kt index 4b1b03337d..251a567c54 100644 --- a/kotlinx-coroutines-core/jvm/src/Dispatchers.kt +++ b/kotlinx-coroutines-core/jvm/src/Dispatchers.kt @@ -125,8 +125,9 @@ public actual object Dispatchers { * * This dispatcher and its views share threads with the [Default][Dispatchers.Default] dispatcher, so using * `withContext(Dispatchers.IO) { ... }` when already running on the [Default][Dispatchers.Default] - * dispatcher does not lead to an actual switching to another thread — typically execution - * continues in the same thread. + * dispatcher typically does not lead to an actual switching to another thread. In such scenarios, + * the underlying implementation attempts to keep the execution on the same thread on a best-effort basis. + * * As a result of thread sharing, more than 64 (default parallelism) threads can be created (but not used) * during operations over IO dispatcher. */ From 15aba8d96eaaeee5472ea6ed144a664f25e8f89e Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 6 Apr 2022 03:29:38 -0700 Subject: [PATCH 02/18] Update Kotlin to 1.6.20 (#3235) --- README.md | 6 +-- .../benchmarks/ParametrizedDispatcherBase.kt | 1 - gradle.properties | 2 +- .../jvm/test/DispatcherKeyTest.kt | 2 +- .../common/test/TestDispatchersTest.kt | 1 - .../js/test/FailingTests.kt | 37 ------------------- 6 files changed, 5 insertions(+), 44 deletions(-) delete mode 100644 kotlinx-coroutines-test/js/test/FailingTests.kt diff --git a/README.md b/README.md index 10910f9e22..9495af170c 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ And make sure that you use the latest Kotlin version: ```xml - 1.6.0 + 1.6.20 ``` @@ -110,10 +110,10 @@ And make sure that you use the latest Kotlin version: ```kotlin plugins { // For build.gradle.kts (Kotlin DSL) - kotlin("jvm") version "1.6.0" + kotlin("jvm") version "1.6.20" // For build.gradle (Groovy DSL) - id "org.jetbrains.kotlin.jvm" version "1.6.0" + id "org.jetbrains.kotlin.jvm" version "1.6.20" } ``` diff --git a/benchmarks/src/jmh/kotlin/benchmarks/ParametrizedDispatcherBase.kt b/benchmarks/src/jmh/kotlin/benchmarks/ParametrizedDispatcherBase.kt index 80e15a1b4f..ce64c6a49b 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/ParametrizedDispatcherBase.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/ParametrizedDispatcherBase.kt @@ -25,7 +25,6 @@ abstract class ParametrizedDispatcherBase : CoroutineScope { private var closeable: Closeable? = null @Setup - @UseExperimental(InternalCoroutinesApi::class) open fun setup() { coroutineContext = when { dispatcher == "fjp" -> ForkJoinPool.commonPool().asCoroutineDispatcher() diff --git a/gradle.properties b/gradle.properties index e91e8dadaa..8faae9eb68 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ # Kotlin version=1.6.1-SNAPSHOT group=org.jetbrains.kotlinx -kotlin_version=1.6.0 +kotlin_version=1.6.20 # Dependencies junit_version=4.12 diff --git a/kotlinx-coroutines-core/jvm/test/DispatcherKeyTest.kt b/kotlinx-coroutines-core/jvm/test/DispatcherKeyTest.kt index e2d8ffaa47..303e8cc8fe 100644 --- a/kotlinx-coroutines-core/jvm/test/DispatcherKeyTest.kt +++ b/kotlinx-coroutines-core/jvm/test/DispatcherKeyTest.kt @@ -8,7 +8,7 @@ import org.junit.Test import kotlin.coroutines.* import kotlin.test.* -@UseExperimental(ExperimentalStdlibApi::class) +@OptIn(ExperimentalStdlibApi::class) class DispatcherKeyTest : TestBase() { companion object CustomInterceptor : AbstractCoroutineContextElement(ContinuationInterceptor), diff --git a/kotlinx-coroutines-test/common/test/TestDispatchersTest.kt b/kotlinx-coroutines-test/common/test/TestDispatchersTest.kt index 66a6c24e8f..bcf016b3d3 100644 --- a/kotlinx-coroutines-test/common/test/TestDispatchersTest.kt +++ b/kotlinx-coroutines-test/common/test/TestDispatchersTest.kt @@ -23,7 +23,6 @@ class TestDispatchersTest: OrderedExecutionTestBase() { /** Tests that asynchronous execution of tests does not happen concurrently with [AfterTest]. */ @Test - @NoJs fun testMainMocking() = runTest { val mainAtStart = TestMainDispatcher.currentTestDispatcher assertNotNull(mainAtStart) diff --git a/kotlinx-coroutines-test/js/test/FailingTests.kt b/kotlinx-coroutines-test/js/test/FailingTests.kt deleted file mode 100644 index 4746a737fa..0000000000 --- a/kotlinx-coroutines-test/js/test/FailingTests.kt +++ /dev/null @@ -1,37 +0,0 @@ -package kotlinx.coroutines.test - -import kotlinx.coroutines.* -import kotlinx.coroutines.test.internal.* -import kotlin.test.* - -/** These are tests that we want to fail. They are here so that, when the issue is fixed, their failure indicates that - * everything is better now. */ -class FailingTests { - - private var tearDownEntered = false - - @BeforeTest - fun setUp() { - Dispatchers.setMain(StandardTestDispatcher()) - } - - @AfterTest - fun tearDown() { - Dispatchers.resetMain() - tearDownEntered = true - } - - /** [TestDispatchersTest.testMainMocking]. */ - @Test - fun testAfterTestIsConcurrent() = runTest { - try { - val mainAtStart = TestMainDispatcher.currentTestDispatcher ?: return@runTest - withContext(Dispatchers.Default) { - // context switch - } - assertNotSame(mainAtStart, TestMainDispatcher.currentTestDispatcher!!) - } finally { - assertTrue(tearDownEntered) - } - } -} From 163a55edce3924e262887df3b93925b6d05a2c3d Mon Sep 17 00:00:00 2001 From: shmuelr Date: Wed, 13 Apr 2022 07:14:44 -0400 Subject: [PATCH 03/18] Add missing package statement to test file (#3249) --- .../concurrent/test/LimitedParallelismConcurrentTest.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kotlinx-coroutines-core/concurrent/test/LimitedParallelismConcurrentTest.kt b/kotlinx-coroutines-core/concurrent/test/LimitedParallelismConcurrentTest.kt index 964f678e74..8d38f05b4b 100644 --- a/kotlinx-coroutines-core/concurrent/test/LimitedParallelismConcurrentTest.kt +++ b/kotlinx-coroutines-core/concurrent/test/LimitedParallelismConcurrentTest.kt @@ -2,6 +2,8 @@ * Copyright 2016-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ +package kotlinx.coroutines + import kotlinx.atomicfu.* import kotlinx.coroutines.* import kotlinx.coroutines.exceptions.* From c1cd02c0a9ec62162408c5d3bdd1de7780298a93 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Mon, 18 Apr 2022 07:04:38 -0700 Subject: [PATCH 04/18] Properly preserve thread local values for coroutines that are not intercepted with DispatchedContinuation (#3252) * Properly preserve thread local values for coroutines that are not intercepted with DispatchedContinuation Fixes #2930 --- .../jvm/src/CoroutineContext.kt | 31 ++++++ .../jvm/test/ThreadLocalStressTest.kt | 95 ++++++++++++++++++- 2 files changed, 125 insertions(+), 1 deletion(-) diff --git a/kotlinx-coroutines-core/jvm/src/CoroutineContext.kt b/kotlinx-coroutines-core/jvm/src/CoroutineContext.kt index e08b805295..7209bee803 100644 --- a/kotlinx-coroutines-core/jvm/src/CoroutineContext.kt +++ b/kotlinx-coroutines-core/jvm/src/CoroutineContext.kt @@ -181,6 +181,37 @@ internal actual class UndispatchedCoroutineactual constructor ( */ private var threadStateToRecover = ThreadLocal>() + init { + /* + * This is a hack for a very specific case in #2930 unless #3253 is implemented. + * 'ThreadLocalStressTest' covers this change properly. + * + * The scenario this change covers is the following: + * 1) The coroutine is being started as plain non kotlinx.coroutines related suspend function, + * e.g. `suspend fun main` or, more importantly, Ktor `SuspendFunGun`, that is invoking + * `withContext(tlElement)` which creates `UndispatchedCoroutine`. + * 2) It (original continuation) is then not wrapped into `DispatchedContinuation` via `intercept()` + * and goes neither through `DC.run` nor through `resumeUndispatchedWith` that both + * do thread context element tracking. + * 3) So thread locals never got chance to get properly set up via `saveThreadContext`, + * but when `withContext` finishes, it attempts to recover thread locals in its `afterResume`. + * + * Here we detect precisely this situation and properly setup context to recover later. + * + */ + if (uCont.context[ContinuationInterceptor] !is CoroutineDispatcher) { + /* + * We cannot just "read" the elements as there is no such API, + * so we update-restore it immediately and use the intermediate value + * as the initial state, leveraging the fact that thread context element + * is idempotent and such situations are increasingly rare. + */ + val values = updateThreadContext(context, null) + restoreThreadContext(context, values) + saveThreadContext(context, values) + } + } + fun saveThreadContext(context: CoroutineContext, oldValue: Any?) { threadStateToRecover.set(context to oldValue) } diff --git a/kotlinx-coroutines-core/jvm/test/ThreadLocalStressTest.kt b/kotlinx-coroutines-core/jvm/test/ThreadLocalStressTest.kt index f9941d0215..20621d12bf 100644 --- a/kotlinx-coroutines-core/jvm/test/ThreadLocalStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/ThreadLocalStressTest.kt @@ -4,6 +4,10 @@ package kotlinx.coroutines +import kotlinx.coroutines.sync.* +import java.util.concurrent.* +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* import kotlin.test.* @@ -63,10 +67,99 @@ class ThreadLocalStressTest : TestBase() { withContext(threadLocal.asContextElement("foo")) { yield() cancel() - suspendCancellableCoroutineReusable { } + suspendCancellableCoroutineReusable { } } } finally { assertEquals(expectedValue, threadLocal.get()) } } + + /* + * Another set of tests for undispatcheable continuations that do not require stress test multiplier. + * Also note that `uncaughtExceptionHandler` is used as the only available mechanism to propagate error from + * `resumeWith` + */ + + @Test + fun testNonDispatcheableLeak() { + repeat(100) { + doTestWithPreparation( + ::doTest, + { threadLocal.set(null) }) { threadLocal.get() == null } + assertNull(threadLocal.get()) + } + } + + @Test + fun testNonDispatcheableLeakWithInitial() { + repeat(100) { + doTestWithPreparation(::doTest, { threadLocal.set("initial") }) { threadLocal.get() == "initial" } + assertEquals("initial", threadLocal.get()) + } + } + + @Test + fun testNonDispatcheableLeakWithContextSwitch() { + repeat(100) { + doTestWithPreparation( + ::doTestWithContextSwitch, + { threadLocal.set(null) }) { threadLocal.get() == null } + assertNull(threadLocal.get()) + } + } + + @Test + fun testNonDispatcheableLeakWithInitialWithContextSwitch() { + repeat(100) { + doTestWithPreparation( + ::doTestWithContextSwitch, + { threadLocal.set("initial") }) { true /* can randomly wake up on the non-main thread */ } + // Here we are always on the main thread + assertEquals("initial", threadLocal.get()) + } + } + + private fun doTestWithPreparation(testBody: suspend () -> Unit, setup: () -> Unit, isValid: () -> Boolean) { + setup() + val latch = CountDownLatch(1) + testBody.startCoroutineUninterceptedOrReturn(Continuation(EmptyCoroutineContext) { + if (!isValid()) { + Thread.currentThread().uncaughtExceptionHandler.uncaughtException( + Thread.currentThread(), + IllegalStateException("Unexpected error: thread local was not cleaned") + ) + } + latch.countDown() + }) + latch.await() + } + + private suspend fun doTest() { + withContext(threadLocal.asContextElement("foo")) { + try { + coroutineScope { + val semaphore = Semaphore(1, 1) + cancel() + semaphore.acquire() + } + } catch (e: CancellationException) { + // Ignore cancellation + } + } + } + + private suspend fun doTestWithContextSwitch() { + withContext(threadLocal.asContextElement("foo")) { + try { + coroutineScope { + val semaphore = Semaphore(1, 1) + GlobalScope.launch { }.join() + cancel() + semaphore.acquire() + } + } catch (e: CancellationException) { + // Ignore cancellation + } + } + } } From 31b9269a8cc7b295af985b8a554cab3fb735415e Mon Sep 17 00:00:00 2001 From: mvicsokolova <82594708+mvicsokolova@users.noreply.github.com> Date: Mon, 18 Apr 2022 17:32:50 +0300 Subject: [PATCH 05/18] Update atomicfu 0.17.2 (#3255) * Update kotlinx.atomicfu to 0.17.2 * Turn on atomicfu IR transformation for Kotlin/JS --- gradle.properties | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 8faae9eb68..9040425dbe 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,7 +10,7 @@ kotlin_version=1.6.20 # Dependencies junit_version=4.12 junit5_version=5.7.0 -atomicfu_version=0.17.0 +atomicfu_version=0.17.2 knit_version=0.3.0 html_version=0.7.2 lincheck_version=2.14 @@ -58,3 +58,4 @@ org.gradle.jvmargs=-Xmx3g kotlin.mpp.enableCompatibilityMetadataVariant=true kotlin.mpp.stability.nowarn=true +kotlinx.atomicfu.enableIrTransformation=true From 35cd6d4074b7eeb6c06c60eba0a346198d152cc1 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Tue, 26 Apr 2022 05:54:14 -0700 Subject: [PATCH 06/18] Update Dokka and Knit (#3264) Fixes #3177 Fixes #2991 --- gradle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 9040425dbe..08d156f744 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,10 +11,10 @@ kotlin_version=1.6.20 junit_version=4.12 junit5_version=5.7.0 atomicfu_version=0.17.2 -knit_version=0.3.0 +knit_version=0.4.0 html_version=0.7.2 lincheck_version=2.14 -dokka_version=1.6.10 +dokka_version=1.6.21 byte_buddy_version=1.10.9 reactor_version=3.4.1 reactive_streams_version=1.0.3 From 0fe8f92b3e638c4a8aa7b926468bb8040d991af3 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Mon, 16 May 2022 18:56:15 +0600 Subject: [PATCH 07/18] Fix data-race in Mutex owner when mutex is locked/released inconsistently (#3286) Fixes #3250 --- kotlinx-coroutines-core/common/src/sync/Mutex.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotlinx-coroutines-core/common/src/sync/Mutex.kt b/kotlinx-coroutines-core/common/src/sync/Mutex.kt index d2a2fcd41b..681d5db6b0 100644 --- a/kotlinx-coroutines-core/common/src/sync/Mutex.kt +++ b/kotlinx-coroutines-core/common/src/sync/Mutex.kt @@ -361,7 +361,7 @@ internal class MutexImpl(locked: Boolean) : Mutex, SelectClause2 { } private class LockedQueue( - @JvmField var owner: Any + @Volatile @JvmField var owner: Any ) : LockFreeLinkedListHead() { override fun toString(): String = "LockedQueue[$owner]" } From 5896b731e45164dae8ddd7c1072e16b6f5bb896e Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Mon, 16 May 2022 19:20:26 +0600 Subject: [PATCH 08/18] Improve videos section in readme (#3285) * Add a link to talk about the history of structured concurrency in kotlinx.coroutines * Sightly reorder videos section and remove way too obsolete introduction that is covered by coroutines in practice Fixes #3273 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9d7970316a..e08235ffb0 100644 --- a/README.md +++ b/README.md @@ -61,9 +61,9 @@ suspend fun main() = coroutineScope { ## Documentation * Presentations and videos: - * [Introduction to Coroutines](https://www.youtube.com/watch?v=_hfBv0a09Jc) (Roman Elizarov at KotlinConf 2017, [slides](https://www.slideshare.net/elizarov/introduction-to-coroutines-kotlinconf-2017)) - * [Deep dive into Coroutines](https://www.youtube.com/watch?v=YrrUCSi72E8) (Roman Elizarov at KotlinConf 2017, [slides](https://www.slideshare.net/elizarov/deep-dive-into-coroutines-on-jvm-kotlinconf-2017)) * [Kotlin Coroutines in Practice](https://www.youtube.com/watch?v=a3agLJQ6vt8) (Roman Elizarov at KotlinConf 2018, [slides](https://www.slideshare.net/elizarov/kotlin-coroutines-in-practice-kotlinconf-2018)) + * [Deep Dive into Coroutines](https://www.youtube.com/watch?v=YrrUCSi72E8) (Roman Elizarov at KotlinConf 2017, [slides](https://www.slideshare.net/elizarov/deep-dive-into-coroutines-on-jvm-kotlinconf-2017)) + * [History of Structured Concurrency in Coroutines](https://www.youtube.com/watch?v=Mj5P47F6nJg) (Roman Elizarov at Hydra 2019, [slides](https://speakerdeck.com/elizarov/structured-concurrency)) * Guides and manuals: * [Guide to kotlinx.coroutines by example](https://kotlinlang.org/docs/coroutines-guide.html) (**read it first**) * [Guide to UI programming with coroutines](ui/coroutines-guide-ui.md) From 155868f6d1a025f075e9f85126015b76f8f2677b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojtek=20Kalici=C5=84ski?= Date: Tue, 17 May 2022 18:00:08 +0200 Subject: [PATCH 09/18] Update to Gradle 7.4.2, Kotlin 1.6.21, other plugin updates (#3281) --- .gitignore | 1 + benchmarks/build.gradle.kts | 9 +++-- build.gradle | 13 +++---- buildSrc/src/main/kotlin/Publishing.kt | 6 ++++ .../animalsniffer-conventions.gradle.kts | 20 ++++++----- gradle.properties | 4 +-- gradle/compile-js-multiplatform.gradle | 2 +- gradle/dokka.gradle.kts | 4 --- gradle/node-js.gradle | 6 ++-- gradle/publish.gradle | 5 +-- gradle/test-mocha-js.gradle | 35 ++++++++++--------- gradle/wrapper/gradle-wrapper.properties | 2 +- integration-testing/build.gradle | 6 ++-- .../kotlinx-coroutines-guava/build.gradle.kts | 2 +- kotlinx-coroutines-core/build.gradle | 2 ++ kotlinx-coroutines-debug/build.gradle | 4 +-- .../build.gradle.kts | 4 +-- .../build.gradle.kts | 4 +-- settings.gradle | 1 - ui/kotlinx-coroutines-swing/build.gradle.kts | 2 +- 20 files changed, 72 insertions(+), 60 deletions(-) diff --git a/.gitignore b/.gitignore index 52843ca5b7..36de0e50da 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ build out target local.properties +/kotlin-js-store diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index b7dcb57968..f64c4aaa21 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -8,7 +8,6 @@ import me.champeau.gradle.* import org.jetbrains.kotlin.gradle.tasks.* plugins { - id("net.ltgt.apt") id("com.github.johnrengelman.shadow") id("me.champeau.gradle.jmh") apply false } @@ -31,8 +30,6 @@ tasks.named("compileJmhKotlin") { } } - - // It is better to use the following to run benchmarks, otherwise you may get unexpected errors: // ./gradlew --no-daemon cleanJmhJar jmh -Pjmh="MyBenchmark" extensions.configure("jmh") { @@ -54,6 +51,12 @@ val jmhJarTask = tasks.named("jmhJar") { } tasks { + // For some reason the DuplicatesStrategy from jmh is not enough + // and errors with duplicates appear unless I force it to WARN only: + withType { + duplicatesStrategy = DuplicatesStrategy.WARN + } + build { dependsOn(jmhJarTask) } diff --git a/build.gradle b/build.gradle index bbf24f21d8..649434bba8 100644 --- a/build.gradle +++ b/build.gradle @@ -54,13 +54,13 @@ buildscript { classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version" classpath "org.jetbrains.kotlinx:atomicfu-gradle-plugin:$atomicfu_version" classpath "org.jetbrains.kotlinx:kotlinx-knit:$knit_version" - classpath "com.moowork.gradle:gradle-node-plugin:$gradle_node_version" + classpath "com.github.node-gradle:gradle-node-plugin:$gradle_node_version" classpath "org.jetbrains.kotlinx:binary-compatibility-validator:$binary_compatibility_validator_version" - classpath "ru.vyarus:gradle-animalsniffer-plugin:1.5.3" // Android API check + classpath "ru.vyarus:gradle-animalsniffer-plugin:1.5.4" // Android API check classpath "org.jetbrains.kotlinx:kover:$kover_version" // JMH plugins - classpath "com.github.jengelman.gradle.plugins:shadow:5.1.0" + classpath "gradle.plugin.com.github.johnrengelman:shadow:7.1.2" } CacheRedirector.configureBuildScript(buildscript, rootProject) @@ -130,6 +130,9 @@ allprojects { } } +// needs to be before evaluationDependsOn due to weird Gradle ordering +apply plugin: "animalsniffer-conventions" + // Add dependency to core source sets. Core is configured in kx-core/build.gradle configure(subprojects.findAll { !sourceless.contains(it.name) && it.name != coreModule }) { evaluationDependsOn(":$coreModule") @@ -232,7 +235,7 @@ def core_docs_url = "https://kotlin.github.io/kotlinx.coroutines/$coreModule/" def core_docs_file = "$projectDir/kotlinx-coroutines-core/build/dokka/htmlPartial/package-list" apply plugin: "org.jetbrains.dokka" -configure(subprojects.findAll { !unpublished.contains(it.name) }) { +configure(subprojects.findAll { !unpublished.contains(it.name) && it.name != coreModule }) { if (it.name != 'kotlinx-coroutines-bom') { apply from: rootProject.file('gradle/dokka.gradle.kts') } @@ -303,8 +306,6 @@ def publishTasks = getTasksByName("publish", true) + getTasksByName("publishNpm" task deploy(dependsOn: publishTasks) -apply plugin: "animalsniffer-conventions" - clean.dependsOn gradle.includedBuilds.collect { it.task(':clean') } // --------------- Knit configuration --------------- diff --git a/buildSrc/src/main/kotlin/Publishing.kt b/buildSrc/src/main/kotlin/Publishing.kt index 8c6dd5de3d..cb612c5077 100644 --- a/buildSrc/src/main/kotlin/Publishing.kt +++ b/buildSrc/src/main/kotlin/Publishing.kt @@ -7,6 +7,7 @@ import org.gradle.api.Project import org.gradle.api.artifacts.dsl.* import org.gradle.api.publish.maven.* +import org.gradle.kotlin.dsl.* import org.gradle.plugins.signing.* import java.net.* @@ -56,6 +57,11 @@ fun configureMavenPublication(rh: RepositoryHandler, project: Project) { password = project.getSensitiveProperty("libs.sonatype.password") } } + + // Something that's easy to "clean" for development, not mavenLocal + rh.maven("${project.rootProject.buildDir}/repo") { + name = "buildRepo" + } } fun signPublicationIfKeyPresent(project: Project, publication: MavenPublication) { diff --git a/buildSrc/src/main/kotlin/animalsniffer-conventions.gradle.kts b/buildSrc/src/main/kotlin/animalsniffer-conventions.gradle.kts index 32b4931e57..f00a0b315f 100644 --- a/buildSrc/src/main/kotlin/animalsniffer-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/animalsniffer-conventions.gradle.kts @@ -4,17 +4,19 @@ import ru.vyarus.gradle.plugin.animalsniffer.* -subprojects { +configure(subprojects) { // Skip JDK 8 projects or unpublished ones - if (!shouldSniff()) return@subprojects + if (!shouldSniff()) return@configure apply(plugin = "ru.vyarus.animalsniffer") - configure { - sourceSets = listOf((project.extensions.getByName("sourceSets") as SourceSetContainer).getByName("main")) - } - val signature: Configuration by configurations - dependencies { - signature("net.sf.androidscents.signature:android-api-level-14:4.0_r4@signature") - signature("org.codehaus.mojo.signature:java17:1.0@signature") + project.plugins.withType(JavaPlugin::class.java) { + configure { + sourceSets = listOf((project.extensions.getByName("sourceSets") as SourceSetContainer).getByName("main")) + } + val signature: Configuration by configurations + dependencies { + signature("net.sf.androidscents.signature:android-api-level-14:4.0_r4@signature") + signature("org.codehaus.mojo.signature:java17:1.0@signature") + } } } diff --git a/gradle.properties b/gradle.properties index 08d156f744..320f41d0fd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ # Kotlin version=1.6.1-SNAPSHOT group=org.jetbrains.kotlinx -kotlin_version=1.6.20 +kotlin_version=1.6.21 # Dependencies junit_version=4.12 @@ -35,7 +35,7 @@ baksmali_version=2.2.7 # JS kotlin.js.compiler=both -gradle_node_version=1.2.0 +gradle_node_version=3.1.1 node_version=10.0.0 npm_version=5.7.1 mocha_version=6.2.2 diff --git a/gradle/compile-js-multiplatform.gradle b/gradle/compile-js-multiplatform.gradle index d6df7e403a..c6fc757c7d 100644 --- a/gradle/compile-js-multiplatform.gradle +++ b/gradle/compile-js-multiplatform.gradle @@ -60,7 +60,7 @@ compileTestJsLegacy.configure { task populateNodeModules(type: Copy, dependsOn: compileTestJsLegacy) { // we must copy output that is transformed by atomicfu from(kotlin.js().compilations.main.output.allOutputs) - into "$node.nodeModulesDir/node_modules" + into node.nodeProjectDir.dir("node_modules") def configuration = configurations.hasProperty("jsLegacyTestRuntimeClasspath") ? configurations.jsLegacyTestRuntimeClasspath diff --git a/gradle/dokka.gradle.kts b/gradle/dokka.gradle.kts index 8be4fbe231..2470ded3ea 100644 --- a/gradle/dokka.gradle.kts +++ b/gradle/dokka.gradle.kts @@ -39,10 +39,6 @@ tasks.withType(DokkaTaskPartial::class).configureEach { if (!project.isMultiplatform) { dependsOn(project.configurations["compileClasspath"]) - doFirst { - // resolve classpath only during execution - classpath.from(project.configurations["compileClasspath"].files)// + project.sourceSets.main.output.files) - } } } } diff --git a/gradle/node-js.gradle b/gradle/node-js.gradle index 42f101c5f4..5eddc5fa37 100644 --- a/gradle/node-js.gradle +++ b/gradle/node-js.gradle @@ -2,13 +2,13 @@ * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -apply plugin: 'com.moowork.node' +apply plugin: 'com.github.node-gradle.node' node { version = "$node_version" npmVersion = "$npm_version" download = true - nodeModulesDir = file(buildDir) + nodeProjectDir = file(buildDir) } // Configures testing for JS modules @@ -25,7 +25,7 @@ task prepareNodePackage(type: Copy) { from("npm") { exclude 'package.json' } - into "$node.nodeModulesDir" + into node.nodeProjectDir } npmInstall.dependsOn prepareNodePackage diff --git a/gradle/publish.gradle b/gradle/publish.gradle index fa2bbb8544..00034bfa87 100644 --- a/gradle/publish.gradle +++ b/gradle/publish.gradle @@ -6,7 +6,6 @@ import org.gradle.util.VersionNumber // Configures publishing of Maven artifacts to Maven Central -apply plugin: 'maven' apply plugin: 'maven-publish' apply plugin: 'signing' @@ -16,7 +15,9 @@ def isMultiplatform = project.name == "kotlinx-coroutines-core" || project.name def isBom = project.name == "kotlinx-coroutines-bom" if (!isBom) { - apply plugin: "com.github.johnrengelman.shadow" + if (project.name == "kotlinx-coroutines-debug") { + apply plugin: "com.github.johnrengelman.shadow" + } // empty xxx-javadoc.jar task javadocJar(type: Jar) { diff --git a/gradle/test-mocha-js.gradle b/gradle/test-mocha-js.gradle index d011eeaa20..27d2e5b394 100644 --- a/gradle/test-mocha-js.gradle +++ b/gradle/test-mocha-js.gradle @@ -9,7 +9,7 @@ task installDependenciesMochaNode(type: NpmTask, dependsOn: [npmInstall]) { "mocha@$mocha_version", "source-map-support@$source_map_support_version", '--no-save'] - if (project.hasProperty("teamcity")) args += ["mocha-teamcity-reporter@$mocha_teamcity_reporter_version"] + if (project.hasProperty("teamcity")) args.addAll(["mocha-teamcity-reporter@$mocha_teamcity_reporter_version"]) } def compileJsLegacy = tasks.hasProperty("compileKotlinJsLegacy") @@ -22,9 +22,9 @@ def compileTestJsLegacy = tasks.hasProperty("compileTestKotlinJsLegacy") // todo: use atomicfu-transformed test files here (not critical) task testMochaNode(type: NodeTask, dependsOn: [compileTestJsLegacy, installDependenciesMochaNode]) { - script = file("$node.nodeModulesDir/node_modules/mocha/bin/mocha") - args = [compileTestJsLegacy.outputFile, '--require', 'source-map-support/register'] - if (project.hasProperty("teamcity")) args += ['--reporter', 'mocha-teamcity-reporter'] + script = file("${node.nodeProjectDir.getAsFile().get()}/node_modules/mocha/bin/mocha") + args = [compileTestJsLegacy.outputFile.path, '--require', 'source-map-support/register'] + if (project.hasProperty("teamcity")) args.addAll(['--reporter', 'mocha-teamcity-reporter']) } def jsLegacyTestTask = project.tasks.findByName('jsLegacyTest') ? jsLegacyTest : jsTest @@ -40,8 +40,8 @@ task installDependenciesMochaChrome(type: NpmTask, dependsOn: [npmInstall]) { "kotlin@$kotlin_version", "kotlin-test@$kotlin_version", '--no-save'] - if (project.hasProperty("teamcity")) args += [ - "mocha-teamcity-reporter@$mocha_teamcity_reporter_version"] + if (project.hasProperty("teamcity")) args.addAll([ + "mocha-teamcity-reporter@$mocha_teamcity_reporter_version"]) } def mochaChromeTestPage = file("$buildDir/test-page.html") @@ -51,19 +51,20 @@ task prepareMochaChrome(dependsOn: [compileTestJsLegacy, installDependenciesMoch } prepareMochaChrome.doLast { + def nodeProjDir = node.nodeProjectDir.getAsFile().get() mochaChromeTestPage.text = """ Mocha Tests - +
- + - - + + @@ -73,9 +74,9 @@ prepareMochaChrome.doLast { } task testMochaChrome(type: NodeTask, dependsOn: prepareMochaChrome) { - script = file("$node.nodeModulesDir/node_modules/mocha-headless-chrome/bin/start") - args = [compileTestJsLegacy.outputFile, '--file', mochaChromeTestPage] - if (project.hasProperty("teamcity")) args += ['--reporter', 'mocha-teamcity-reporter'] + script = file("${node.nodeProjectDir.getAsFile().get()}/node_modules/mocha-headless-chrome/bin/start") + args = [compileTestJsLegacy.outputFile.path, '--file', mochaChromeTestPage] + if (project.hasProperty("teamcity")) args.addAll(['--reporter', 'mocha-teamcity-reporter']) } // todo: Commented out because mocha-headless-chrome does not work on TeamCity @@ -90,13 +91,13 @@ task installDependenciesMochaJsdom(type: NpmTask, dependsOn: [npmInstall]) { "jsdom-global@$jsdom_global_version", "source-map-support@$source_map_support_version", '--no-save'] - if (project.hasProperty("teamcity")) args += ["mocha-teamcity-reporter@$mocha_teamcity_reporter_version"] + if (project.hasProperty("teamcity")) args.addAll(["mocha-teamcity-reporter@$mocha_teamcity_reporter_version"]) } task testMochaJsdom(type: NodeTask, dependsOn: [compileTestJsLegacy, installDependenciesMochaJsdom]) { - script = file("$node.nodeModulesDir/node_modules/mocha/bin/mocha") - args = [compileTestJsLegacy.outputFile, '--require', 'source-map-support/register', '--require', 'jsdom-global/register'] - if (project.hasProperty("teamcity")) args += ['--reporter', 'mocha-teamcity-reporter'] + script = file("${node.nodeProjectDir.getAsFile().get()}/node_modules/mocha/bin/mocha") + args = [compileTestJsLegacy.outputFile.path, '--require', 'source-map-support/register', '--require', 'jsdom-global/register'] + if (project.hasProperty("teamcity")) args.addAll(['--reporter', 'mocha-teamcity-reporter']) } jsLegacyTestTask.dependsOn testMochaJsdom diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d7ae858e5a..f57489cf8a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -4,6 +4,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/integration-testing/build.gradle b/integration-testing/build.gradle index aea4d45203..4e025c58dc 100644 --- a/integration-testing/build.gradle +++ b/integration-testing/build.gradle @@ -98,9 +98,9 @@ dependencies { testImplementation 'junit:junit:4.12' npmTestImplementation 'org.apache.commons:commons-compress:1.18' npmTestImplementation 'com.google.code.gson:gson:2.8.5' - debugAgentTestCompile project(':kotlinx-coroutines-core') - debugAgentTestCompile project(':kotlinx-coroutines-debug') - coreAgentTestCompile project(':kotlinx-coroutines-core') + debugAgentTestImplementation project(':kotlinx-coroutines-core') + debugAgentTestImplementation project(':kotlinx-coroutines-debug') + coreAgentTestImplementation project(':kotlinx-coroutines-core') } compileTestKotlin { diff --git a/integration/kotlinx-coroutines-guava/build.gradle.kts b/integration/kotlinx-coroutines-guava/build.gradle.kts index ebddd3c9ce..2a84ca937e 100644 --- a/integration/kotlinx-coroutines-guava/build.gradle.kts +++ b/integration/kotlinx-coroutines-guava/build.gradle.kts @@ -5,7 +5,7 @@ val guavaVersion = "31.0.1-jre" dependencies { - compile("com.google.guava:guava:$guavaVersion") + api("com.google.guava:guava:$guavaVersion") } java { diff --git a/kotlinx-coroutines-core/build.gradle b/kotlinx-coroutines-core/build.gradle index ac6840d98c..78d2af14b2 100644 --- a/kotlinx-coroutines-core/build.gradle +++ b/kotlinx-coroutines-core/build.gradle @@ -14,6 +14,8 @@ if (rootProject.ext.native_targets_enabled) { apply from: rootProject.file("gradle/compile-js-multiplatform.gradle") apply from: rootProject.file('gradle/publish-npm-js.gradle') +apply from: rootProject.file('gradle/dokka.gradle.kts') +apply from: rootProject.file('gradle/publish.gradle') /* ========================================================================== Configure source sets structure for kotlinx-coroutines-core: diff --git a/kotlinx-coroutines-debug/build.gradle b/kotlinx-coroutines-debug/build.gradle index c01e70463f..a420004801 100644 --- a/kotlinx-coroutines-debug/build.gradle +++ b/kotlinx-coroutines-debug/build.gradle @@ -21,8 +21,8 @@ configurations { dependencies { compileOnly "junit:junit:$junit_version" compileOnly "org.junit.jupiter:junit-jupiter-api:$junit5_version" - testCompile "org.junit.jupiter:junit-jupiter-engine:$junit5_version" - testCompile "org.junit.platform:junit-platform-testkit:1.7.0" + testImplementation "org.junit.jupiter:junit-jupiter-engine:$junit5_version" + testImplementation "org.junit.platform:junit-platform-testkit:1.7.0" shadowDeps "net.bytebuddy:byte-buddy:$byte_buddy_version" shadowDeps "net.bytebuddy:byte-buddy-agent:$byte_buddy_version" compileOnly "io.projectreactor.tools:blockhound:$blockhound_version" diff --git a/reactive/kotlinx-coroutines-reactive/build.gradle.kts b/reactive/kotlinx-coroutines-reactive/build.gradle.kts index b624069c60..c2e4b5c9f0 100644 --- a/reactive/kotlinx-coroutines-reactive/build.gradle.kts +++ b/reactive/kotlinx-coroutines-reactive/build.gradle.kts @@ -5,8 +5,8 @@ val reactiveStreamsVersion = property("reactive_streams_version") dependencies { - compile("org.reactivestreams:reactive-streams:$reactiveStreamsVersion") - testCompile("org.reactivestreams:reactive-streams-tck:$reactiveStreamsVersion") + api("org.reactivestreams:reactive-streams:$reactiveStreamsVersion") + testImplementation("org.reactivestreams:reactive-streams-tck:$reactiveStreamsVersion") } val testNG by tasks.registering(Test::class) { diff --git a/reactive/kotlinx-coroutines-reactor/build.gradle.kts b/reactive/kotlinx-coroutines-reactor/build.gradle.kts index 1a36ccec28..d4bb135f73 100644 --- a/reactive/kotlinx-coroutines-reactor/build.gradle.kts +++ b/reactive/kotlinx-coroutines-reactor/build.gradle.kts @@ -5,8 +5,8 @@ val reactorVersion = version("reactor") dependencies { - compile("io.projectreactor:reactor-core:$reactorVersion") - compile(project(":kotlinx-coroutines-reactive")) + api("io.projectreactor:reactor-core:$reactorVersion") + api(project(":kotlinx-coroutines-reactive")) } java { diff --git a/settings.gradle b/settings.gradle index d7673a64f9..28a30c7bc7 100644 --- a/settings.gradle +++ b/settings.gradle @@ -18,7 +18,6 @@ pluginManagement { } rootProject.name = 'kotlinx.coroutines' -enableFeaturePreview('GRADLE_METADATA') def module(String path) { int i = path.lastIndexOf('/') diff --git a/ui/kotlinx-coroutines-swing/build.gradle.kts b/ui/kotlinx-coroutines-swing/build.gradle.kts index b8ca906adf..157ce401b2 100644 --- a/ui/kotlinx-coroutines-swing/build.gradle.kts +++ b/ui/kotlinx-coroutines-swing/build.gradle.kts @@ -3,5 +3,5 @@ */ dependencies { - testCompile(project(":kotlinx-coroutines-jdk8")) + testImplementation(project(":kotlinx-coroutines-jdk8")) } From d14b9c5d847a95db73fb8e0432f68c1033613b0a Mon Sep 17 00:00:00 2001 From: agrieve Date: Wed, 18 May 2022 04:34:21 -0400 Subject: [PATCH 10/18] Remove unnecessary -keep rules for services (#3263) Fixes #3111 --- .../META-INF/com.android.tools/proguard/coroutines.pro | 3 ++- .../com.android.tools/r8-from-1.6.0/coroutines.pro | 4 +--- .../com.android.tools/r8-upto-1.6.0/coroutines.pro | 9 --------- .../com.android.tools/r8-upto-3.0.0/coroutines.pro | 10 ++++++++++ .../resources/META-INF/proguard/coroutines.pro | 3 ++- .../src/AndroidExceptionPreHandler.kt | 2 -- 6 files changed, 15 insertions(+), 16 deletions(-) delete mode 100644 ui/kotlinx-coroutines-android/resources/META-INF/com.android.tools/r8-upto-1.6.0/coroutines.pro create mode 100644 ui/kotlinx-coroutines-android/resources/META-INF/com.android.tools/r8-upto-3.0.0/coroutines.pro diff --git a/ui/kotlinx-coroutines-android/resources/META-INF/com.android.tools/proguard/coroutines.pro b/ui/kotlinx-coroutines-android/resources/META-INF/com.android.tools/proguard/coroutines.pro index c7cd15fe11..ef42483f56 100644 --- a/ui/kotlinx-coroutines-android/resources/META-INF/com.android.tools/proguard/coroutines.pro +++ b/ui/kotlinx-coroutines-android/resources/META-INF/com.android.tools/proguard/coroutines.pro @@ -1,5 +1,6 @@ # When editing this file, update the following files as well: -# - META-INF/com.android.tools/r8-upto-1.6.0/coroutines.pro +# - META-INF/com.android.tools/r8-upto-3.0.0/coroutines.pro # - META-INF/proguard/coroutines.pro -keep class kotlinx.coroutines.android.AndroidDispatcherFactory {*;} +-keep class kotlinx.coroutines.android.AndroidExceptionPreHandler {*;} diff --git a/ui/kotlinx-coroutines-android/resources/META-INF/com.android.tools/r8-from-1.6.0/coroutines.pro b/ui/kotlinx-coroutines-android/resources/META-INF/com.android.tools/r8-from-1.6.0/coroutines.pro index 0d04990ad9..cf317c41e3 100644 --- a/ui/kotlinx-coroutines-android/resources/META-INF/com.android.tools/r8-from-1.6.0/coroutines.pro +++ b/ui/kotlinx-coroutines-android/resources/META-INF/com.android.tools/r8-from-1.6.0/coroutines.pro @@ -9,8 +9,6 @@ boolean ANDROID_DETECTED return true; } --keep class kotlinx.coroutines.android.AndroidDispatcherFactory {*;} - # Disable support for "Missing Main Dispatcher", since we always have Android main dispatcher -assumenosideeffects class kotlinx.coroutines.internal.MainDispatchersKt { boolean SUPPORT_MISSING return false; @@ -21,4 +19,4 @@ boolean getASSERTIONS_ENABLED() return false; boolean getDEBUG() return false; boolean getRECOVER_STACK_TRACES() return false; -} \ No newline at end of file +} diff --git a/ui/kotlinx-coroutines-android/resources/META-INF/com.android.tools/r8-upto-1.6.0/coroutines.pro b/ui/kotlinx-coroutines-android/resources/META-INF/com.android.tools/r8-upto-1.6.0/coroutines.pro deleted file mode 100644 index 549d0e85a1..0000000000 --- a/ui/kotlinx-coroutines-android/resources/META-INF/com.android.tools/r8-upto-1.6.0/coroutines.pro +++ /dev/null @@ -1,9 +0,0 @@ -# When editing this file, update the following files as well: -# - META-INF/com.android.tools/proguard/coroutines.pro -# - META-INF/proguard/coroutines.pro - --keep class kotlinx.coroutines.android.AndroidDispatcherFactory {*;} - --assumenosideeffects class kotlinx.coroutines.internal.FastServiceLoader { - boolean ANDROID_DETECTED return true; -} \ No newline at end of file diff --git a/ui/kotlinx-coroutines-android/resources/META-INF/com.android.tools/r8-upto-3.0.0/coroutines.pro b/ui/kotlinx-coroutines-android/resources/META-INF/com.android.tools/r8-upto-3.0.0/coroutines.pro new file mode 100644 index 0000000000..1aa2b114c1 --- /dev/null +++ b/ui/kotlinx-coroutines-android/resources/META-INF/com.android.tools/r8-upto-3.0.0/coroutines.pro @@ -0,0 +1,10 @@ +# When editing this file, update the following files as well for AGP 3.6.0+: +# - META-INF/com.android.tools/proguard/coroutines.pro +# - META-INF/proguard/coroutines.pro + +# After R8 3.0.0 (or probably sometime before that), R8 learned how to optimize +# classes mentioned in META-INF/services files, and explicitly -keeping them +# disables these optimizations. +# https://github.com/Kotlin/kotlinx.coroutines/issues/3111 +-keep class kotlinx.coroutines.android.AndroidDispatcherFactory {*;} +-keep class kotlinx.coroutines.android.AndroidExceptionPreHandler {*;} diff --git a/ui/kotlinx-coroutines-android/resources/META-INF/proguard/coroutines.pro b/ui/kotlinx-coroutines-android/resources/META-INF/proguard/coroutines.pro index 6c918d49e7..087f1ce8e7 100644 --- a/ui/kotlinx-coroutines-android/resources/META-INF/proguard/coroutines.pro +++ b/ui/kotlinx-coroutines-android/resources/META-INF/proguard/coroutines.pro @@ -2,6 +2,7 @@ # When editing this file, update the following files as well for AGP 3.6.0+: # - META-INF/com.android.tools/proguard/coroutines.pro -# - META-INF/com.android.tools/r8-upto-1.6.0/coroutines.pro +# - META-INF/com.android.tools/r8-upto-3.0.0/coroutines.pro -keep class kotlinx.coroutines.android.AndroidDispatcherFactory {*;} +-keep class kotlinx.coroutines.android.AndroidExceptionPreHandler {*;} diff --git a/ui/kotlinx-coroutines-android/src/AndroidExceptionPreHandler.kt b/ui/kotlinx-coroutines-android/src/AndroidExceptionPreHandler.kt index af32191f53..0bc603ea1e 100644 --- a/ui/kotlinx-coroutines-android/src/AndroidExceptionPreHandler.kt +++ b/ui/kotlinx-coroutines-android/src/AndroidExceptionPreHandler.kt @@ -5,12 +5,10 @@ package kotlinx.coroutines.android import android.os.* -import androidx.annotation.* import kotlinx.coroutines.* import java.lang.reflect.* import kotlin.coroutines.* -@Keep internal class AndroidExceptionPreHandler : AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler { From 9e7c1baeb92682cbc9f28e2eb1c12ab16985b4f3 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Thu, 19 May 2022 13:46:59 +0600 Subject: [PATCH 11/18] Update guide example to better reflect the intent (#3292) * Update guide example to better reflect the intent Based on #3202 --- docs/topics/coroutine-context-and-dispatchers.md | 4 ++-- kotlinx-coroutines-core/jvm/test/guide/example-context-06.kt | 2 +- .../jvm/test/guide/test/DispatcherGuideTest.kt | 4 ++-- kotlinx-coroutines-core/jvm/test/guide/test/GuideTest.kt | 0 4 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 kotlinx-coroutines-core/jvm/test/guide/test/GuideTest.kt diff --git a/docs/topics/coroutine-context-and-dispatchers.md b/docs/topics/coroutine-context-and-dispatchers.md index 4e5ed02b87..6c06b14d07 100644 --- a/docs/topics/coroutine-context-and-dispatchers.md +++ b/docs/topics/coroutine-context-and-dispatchers.md @@ -334,8 +334,8 @@ fun main() = runBlocking { } delay(500) request.cancel() // cancel processing of the request - delay(1000) // delay a second to see what happens println("main: Who has survived request cancellation?") + delay(1000) // delay the main thread for a second to see what happens //sampleEnd } ``` @@ -350,8 +350,8 @@ The output of this code is: ```text job1: I run in my own Job and execute independently! job2: I am a child of the request coroutine -job1: I am not affected by cancellation of the request main: Who has survived request cancellation? +job1: I am not affected by cancellation of the request ``` diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-context-06.kt b/kotlinx-coroutines-core/jvm/test/guide/example-context-06.kt index c6ad4516da..6796532229 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-context-06.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-06.kt @@ -26,6 +26,6 @@ fun main() = runBlocking { } delay(500) request.cancel() // cancel processing of the request - delay(1000) // delay a second to see what happens println("main: Who has survived request cancellation?") + delay(1000) // delay the main thread for a second to see what happens } diff --git a/kotlinx-coroutines-core/jvm/test/guide/test/DispatcherGuideTest.kt b/kotlinx-coroutines-core/jvm/test/guide/test/DispatcherGuideTest.kt index 1a84fb9427..e18741e981 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/test/DispatcherGuideTest.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/test/DispatcherGuideTest.kt @@ -59,8 +59,8 @@ class DispatcherGuideTest { test("ExampleContext06") { kotlinx.coroutines.guide.exampleContext06.main() }.verifyLines( "job1: I run in my own Job and execute independently!", "job2: I am a child of the request coroutine", - "job1: I am not affected by cancellation of the request", - "main: Who has survived request cancellation?" + "main: Who has survived request cancellation?", + "job1: I am not affected by cancellation of the request" ) } diff --git a/kotlinx-coroutines-core/jvm/test/guide/test/GuideTest.kt b/kotlinx-coroutines-core/jvm/test/guide/test/GuideTest.kt deleted file mode 100644 index e69de29bb2..0000000000 From 20d47b7bf5e70a548fb982948998c5c616cddbbb Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Thu, 19 May 2022 16:31:50 +0600 Subject: [PATCH 12/18] Update binary-compatibility-validator (#3294) --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 320f41d0fd..404e79b4fc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,7 +22,7 @@ rxjava2_version=2.2.8 rxjava3_version=3.0.2 javafx_version=11.0.2 javafx_plugin_version=0.0.8 -binary_compatibility_validator_version=0.8.0 +binary_compatibility_validator_version=0.10.0 kover_version=0.5.0 blockhound_version=1.0.2.RELEASE jna_version=5.9.0 From 8cdb4d6f232dfcb563abfab42a1d3ca1de9f8624 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Mon, 23 May 2022 13:20:31 +0300 Subject: [PATCH 13/18] Fix potential data race in EventLoop (#3289) Fixes #3251 --- .../common/src/EventLoop.common.kt | 8 ++- .../common/src/internal/ThreadSafeHeap.kt | 2 - .../test/internal/ThreadSafeHeapStressTest.kt | 66 +++++++++++++++++++ .../jvm/test/internal/ThreadSafeHeapTest.kt | 2 +- 4 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 kotlinx-coroutines-core/jvm/test/internal/ThreadSafeHeapStressTest.kt diff --git a/kotlinx-coroutines-core/common/src/EventLoop.common.kt b/kotlinx-coroutines-core/common/src/EventLoop.common.kt index f4f61b25d4..12940c54e2 100644 --- a/kotlinx-coroutines-core/common/src/EventLoop.common.kt +++ b/kotlinx-coroutines-core/common/src/EventLoop.common.kt @@ -236,8 +236,13 @@ internal abstract class EventLoopImplBase: EventLoopImplPlatform(), Delay { if (timeNanos < MAX_DELAY_NS) { val now = nanoTime() DelayedResumeTask(now + timeNanos, continuation).also { task -> - continuation.disposeOnCancellation(task) + /* + * Order is important here: first we schedule the heap and only then + * publish it to continuation. Otherwise, `DelayedResumeTask` would + * have to know how to be disposed of even when it wasn't scheduled yet. + */ schedule(now, task) + continuation.disposeOnCancellation(task) } } } @@ -410,6 +415,7 @@ internal abstract class EventLoopImplBase: EventLoopImplPlatform(), Delay { */ @JvmField var nanoTime: Long ) : Runnable, Comparable, DisposableHandle, ThreadSafeHeapNode { + @Volatile private var _heap: Any? = null // null | ThreadSafeHeap | DISPOSED_TASK override var heap: ThreadSafeHeap<*>? diff --git a/kotlinx-coroutines-core/common/src/internal/ThreadSafeHeap.kt b/kotlinx-coroutines-core/common/src/internal/ThreadSafeHeap.kt index 43b7e9de51..2100454be3 100644 --- a/kotlinx-coroutines-core/common/src/internal/ThreadSafeHeap.kt +++ b/kotlinx-coroutines-core/common/src/internal/ThreadSafeHeap.kt @@ -47,7 +47,6 @@ public open class ThreadSafeHeap : SynchronizedObject() where T: ThreadSafeHe } } - // @Synchronized // NOTE! NOTE! NOTE! inline fun cannot be @Synchronized public inline fun removeFirstIf(predicate: (T) -> Boolean): T? = synchronized(this) { val first = firstImpl() ?: return null if (predicate(first)) { @@ -59,7 +58,6 @@ public open class ThreadSafeHeap : SynchronizedObject() where T: ThreadSafeHe public fun addLast(node: T): Unit = synchronized(this) { addImpl(node) } - // @Synchronized // NOTE! NOTE! NOTE! inline fun cannot be @Synchronized // Condition also receives current first node in the heap public inline fun addLastIf(node: T, cond: (T?) -> Boolean): Boolean = synchronized(this) { if (cond(firstImpl())) { diff --git a/kotlinx-coroutines-core/jvm/test/internal/ThreadSafeHeapStressTest.kt b/kotlinx-coroutines-core/jvm/test/internal/ThreadSafeHeapStressTest.kt new file mode 100644 index 0000000000..5bc0952ed4 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/internal/ThreadSafeHeapStressTest.kt @@ -0,0 +1,66 @@ +/* + * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.internal + +import kotlinx.coroutines.* +import java.util.concurrent.* +import java.util.concurrent.CancellationException +import kotlin.test.* + +class ThreadSafeHeapStressTest : TestBase() { + private class DisposableNode : EventLoopImplBase.DelayedTask(1L) { + override fun run() { + } + } + + @Test + fun testConcurrentRemoveDispose() = runTest { + val heap = EventLoopImplBase.DelayedTaskQueue(1) + repeat(10_000 * stressTestMultiplierSqrt) { + withContext(Dispatchers.Default) { + val node = DisposableNode() + val barrier = CyclicBarrier(2) + launch { + heap.addLast(node) + barrier.await() + heap.remove(node) + } + launch { + barrier.await() + Thread.yield() + node.dispose() + } + } + } + } + + @Test() + fun testConcurrentAddDispose() = runTest { + repeat(10_000 * stressTestMultiplierSqrt) { + val jobToCancel = Job() + val barrier = CyclicBarrier(2) + val jobToJoin = launch(Dispatchers.Default) { + barrier.await() + jobToCancel.cancelAndJoin() + } + + try { + runBlocking { // Use event loop impl + withContext(jobToCancel) { + // This one is to work around heap allocation optimization + launch(start = CoroutineStart.UNDISPATCHED) { + delay(100_000) + } + barrier.await() + delay(100_000) + } + } + } catch (e: CancellationException) { + // Expected exception + } + jobToJoin.join() + } + } +} diff --git a/kotlinx-coroutines-core/jvm/test/internal/ThreadSafeHeapTest.kt b/kotlinx-coroutines-core/jvm/test/internal/ThreadSafeHeapTest.kt index be7ed91a3f..ee0960c849 100644 --- a/kotlinx-coroutines-core/jvm/test/internal/ThreadSafeHeapTest.kt +++ b/kotlinx-coroutines-core/jvm/test/internal/ThreadSafeHeapTest.kt @@ -93,4 +93,4 @@ class ThreadSafeHeapTest : TestBase() { assertEquals(set.size, h.size) } } -} \ No newline at end of file +} From 59488d1d4c7b8a61170f2275ba78db10d575cd5b Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Mon, 23 May 2022 15:06:19 +0300 Subject: [PATCH 14/18] Remove TestCoroutineContext.kt completely (#3291) Fixes #3218 --- kotlinx-coroutines-core/README.md | 4 - .../api/kotlinx-coroutines-core.api | 33 --- kotlinx-coroutines-core/build.gradle | 3 +- .../jvm/src/test_/TestCoroutineContext.kt | 260 ------------------ .../src/CoroutinesBlockHoundIntegration.kt | 3 - 5 files changed, 1 insertion(+), 302 deletions(-) delete mode 100644 kotlinx-coroutines-core/jvm/src/test_/TestCoroutineContext.kt diff --git a/kotlinx-coroutines-core/README.md b/kotlinx-coroutines-core/README.md index c06cd358ad..38a112e89d 100644 --- a/kotlinx-coroutines-core/README.md +++ b/kotlinx-coroutines-core/README.md @@ -83,10 +83,6 @@ Select expression to perform multiple suspending operations simultaneously until Low-level primitives for finer-grained control of coroutines. -# Package kotlinx.coroutines.test - -Obsolete and deprecated module to test coroutines. Replaced with `kotlinx-coroutines-test` module. - diff --git a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api index 79f3cf4308..d227eb879b 100644 --- a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api +++ b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api @@ -1299,36 +1299,3 @@ public final class kotlinx/coroutines/sync/SemaphoreKt { public static final fun withPermit (Lkotlinx/coroutines/sync/Semaphore;Lkotlin/jvm/functions/Function0;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } -public final class kotlinx/coroutines/test/TestCoroutineContext : kotlin/coroutines/CoroutineContext { - public fun ()V - public fun (Ljava/lang/String;)V - public synthetic fun (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun advanceTimeBy (JLjava/util/concurrent/TimeUnit;)J - public static synthetic fun advanceTimeBy$default (Lkotlinx/coroutines/test/TestCoroutineContext;JLjava/util/concurrent/TimeUnit;ILjava/lang/Object;)J - public final fun advanceTimeTo (JLjava/util/concurrent/TimeUnit;)V - public static synthetic fun advanceTimeTo$default (Lkotlinx/coroutines/test/TestCoroutineContext;JLjava/util/concurrent/TimeUnit;ILjava/lang/Object;)V - public final fun assertAllUnhandledExceptions (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V - public static synthetic fun assertAllUnhandledExceptions$default (Lkotlinx/coroutines/test/TestCoroutineContext;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V - public final fun assertAnyUnhandledException (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V - public static synthetic fun assertAnyUnhandledException$default (Lkotlinx/coroutines/test/TestCoroutineContext;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V - public final fun assertExceptions (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V - public static synthetic fun assertExceptions$default (Lkotlinx/coroutines/test/TestCoroutineContext;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V - public final fun assertUnhandledException (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V - public static synthetic fun assertUnhandledException$default (Lkotlinx/coroutines/test/TestCoroutineContext;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V - public final fun cancelAllActions ()V - public fun fold (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; - public fun get (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; - public final fun getExceptions ()Ljava/util/List; - public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; - public final fun now (Ljava/util/concurrent/TimeUnit;)J - public static synthetic fun now$default (Lkotlinx/coroutines/test/TestCoroutineContext;Ljava/util/concurrent/TimeUnit;ILjava/lang/Object;)J - public fun plus (Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; - public fun toString ()Ljava/lang/String; - public final fun triggerActions ()V -} - -public final class kotlinx/coroutines/test/TestCoroutineContextKt { - public static final fun withTestContext (Lkotlinx/coroutines/test/TestCoroutineContext;Lkotlin/jvm/functions/Function1;)V - public static synthetic fun withTestContext$default (Lkotlinx/coroutines/test/TestCoroutineContext;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V -} - diff --git a/kotlinx-coroutines-core/build.gradle b/kotlinx-coroutines-core/build.gradle index 78d2af14b2..9791b445bf 100644 --- a/kotlinx-coroutines-core/build.gradle +++ b/kotlinx-coroutines-core/build.gradle @@ -274,8 +274,7 @@ tasks.jvmLincheckTest { } def commonKoverExcludes = - ["kotlinx.coroutines.test.*", // Deprecated package for removal - "kotlinx.coroutines.debug.*", // Tested by debug module + ["kotlinx.coroutines.debug.*", // Tested by debug module "kotlinx.coroutines.channels.ChannelsKt__DeprecatedKt.*", // Deprecated "kotlinx.coroutines.scheduling.LimitingDispatcher", // Deprecated "kotlinx.coroutines.scheduling.ExperimentalCoroutineDispatcher" // Deprecated diff --git a/kotlinx-coroutines-core/jvm/src/test_/TestCoroutineContext.kt b/kotlinx-coroutines-core/jvm/src/test_/TestCoroutineContext.kt deleted file mode 100644 index 6a71a4deb4..0000000000 --- a/kotlinx-coroutines-core/jvm/src/test_/TestCoroutineContext.kt +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.coroutines.test - -import kotlinx.coroutines.* -import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.internal.* -import java.util.concurrent.* -import kotlin.coroutines.* - -/** @suppress */ -@Deprecated("This API has been deprecated to integrate with Structured Concurrency.", - ReplaceWith("TestCoroutineScope", "kotlin.coroutines.test"), - level = DeprecationLevel.ERROR) // ERROR in 1.6.0, removed in 1.7.0 -public class TestCoroutineContext(private val name: String? = null) : CoroutineContext { - private val uncaughtExceptions = mutableListOf() - - private val ctxDispatcher = Dispatcher() - - private val ctxHandler = CoroutineExceptionHandler { _, exception -> - uncaughtExceptions += exception - } - - // The ordered queue for the runnable tasks. - private val queue = ThreadSafeHeap() - - // The per-scheduler global order counter. - private var counter = 0L - - // Storing time in nanoseconds internally. - private var time = 0L - - /** - * Exceptions that were caught during a [launch][CoroutineScope.launch] or a [async][CoroutineScope.async] + [Deferred.await]. - */ - public val exceptions: List get() = uncaughtExceptions - - // -- CoroutineContext implementation - - public override fun fold(initial: R, operation: (R, CoroutineContext.Element) -> R): R = - operation(operation(initial, ctxDispatcher), ctxHandler) - - @Suppress("UNCHECKED_CAST") - public override fun get(key: CoroutineContext.Key): E? = when { - key === ContinuationInterceptor -> ctxDispatcher as E - key === CoroutineExceptionHandler -> ctxHandler as E - else -> null - } - - public override fun minusKey(key: CoroutineContext.Key<*>): CoroutineContext = when { - key === ContinuationInterceptor -> ctxHandler - key === CoroutineExceptionHandler -> ctxDispatcher - else -> this - } - - /** - * Returns the current virtual clock-time as it is known to this CoroutineContext. - * - * @param unit The [TimeUnit] in which the clock-time must be returned. - * @return The virtual clock-time - */ - public fun now(unit: TimeUnit = TimeUnit.MILLISECONDS): Long= - unit.convert(time, TimeUnit.NANOSECONDS) - - /** - * Moves the CoroutineContext's virtual clock forward by a specified amount of time. - * - * The returned delay-time can be larger than the specified delay-time if the code - * under test contains *blocking* Coroutines. - * - * @param delayTime The amount of time to move the CoroutineContext's clock forward. - * @param unit The [TimeUnit] in which [delayTime] and the return value is expressed. - * @return The amount of delay-time that this CoroutineContext's clock has been forwarded. - */ - public fun advanceTimeBy(delayTime: Long, unit: TimeUnit = TimeUnit.MILLISECONDS): Long { - val oldTime = time - advanceTimeTo(oldTime + unit.toNanos(delayTime), TimeUnit.NANOSECONDS) - return unit.convert(time - oldTime, TimeUnit.NANOSECONDS) - } - - /** - * Moves the CoroutineContext's clock-time to a particular moment in time. - * - * @param targetTime The point in time to which to move the CoroutineContext's clock. - * @param unit The [TimeUnit] in which [targetTime] is expressed. - */ - public fun advanceTimeTo(targetTime: Long, unit: TimeUnit = TimeUnit.MILLISECONDS) { - val nanoTime = unit.toNanos(targetTime) - triggerActions(nanoTime) - if (nanoTime > time) time = nanoTime - } - - /** - * Triggers any actions that have not yet been triggered and that are scheduled to be triggered at or - * before this CoroutineContext's present virtual clock-time. - */ - public fun triggerActions(): Unit = triggerActions(time) - - /** - * Cancels all not yet triggered actions. Be careful calling this, since it can seriously - * mess with your coroutines work. This method should usually be called on tear-down of a - * unit test. - */ - public fun cancelAllActions() { - // An 'is-empty' test is required to avoid a NullPointerException in the 'clear()' method - if (!queue.isEmpty) queue.clear() - } - - /** - * This method does nothing if there is one unhandled exception that satisfies the given predicate. - * Otherwise it throws an [AssertionError] with the given message. - * - * (this method will clear the list of unhandled exceptions) - * - * @param message Message of the [AssertionError]. Defaults to an empty String. - * @param predicate The predicate that must be satisfied. - */ - public fun assertUnhandledException(message: String = "", predicate: (Throwable) -> Boolean) { - if (uncaughtExceptions.size != 1 || !predicate(uncaughtExceptions[0])) throw AssertionError(message) - uncaughtExceptions.clear() - } - - /** - * This method does nothing if there are no unhandled exceptions or all of them satisfy the given predicate. - * Otherwise it throws an [AssertionError] with the given message. - * - * (this method will clear the list of unhandled exceptions) - * - * @param message Message of the [AssertionError]. Defaults to an empty String. - * @param predicate The predicate that must be satisfied. - */ - public fun assertAllUnhandledExceptions(message: String = "", predicate: (Throwable) -> Boolean) { - if (!uncaughtExceptions.all(predicate)) throw AssertionError(message) - uncaughtExceptions.clear() - } - - /** - * This method does nothing if one or more unhandled exceptions satisfy the given predicate. - * Otherwise it throws an [AssertionError] with the given message. - * - * (this method will clear the list of unhandled exceptions) - * - * @param message Message of the [AssertionError]. Defaults to an empty String. - * @param predicate The predicate that must be satisfied. - */ - public fun assertAnyUnhandledException(message: String = "", predicate: (Throwable) -> Boolean) { - if (!uncaughtExceptions.any(predicate)) throw AssertionError(message) - uncaughtExceptions.clear() - } - - /** - * This method does nothing if the list of unhandled exceptions satisfy the given predicate. - * Otherwise it throws an [AssertionError] with the given message. - * - * (this method will clear the list of unhandled exceptions) - * - * @param message Message of the [AssertionError]. Defaults to an empty String. - * @param predicate The predicate that must be satisfied. - */ - public fun assertExceptions(message: String = "", predicate: (List) -> Boolean) { - if (!predicate(uncaughtExceptions)) throw AssertionError(message) - uncaughtExceptions.clear() - } - - private fun enqueue(block: Runnable) = - queue.addLast(TimedRunnableObsolete(block, counter++)) - - private fun postDelayed(block: Runnable, delayTime: Long) = - TimedRunnableObsolete(block, counter++, time + TimeUnit.MILLISECONDS.toNanos(delayTime)) - .also { - queue.addLast(it) - } - - private fun processNextEvent(): Long { - val current = queue.peek() - if (current != null) { - // Automatically advance time for EventLoop callbacks - triggerActions(current.time) - } - return if (queue.isEmpty) Long.MAX_VALUE else 0L - } - - private fun triggerActions(targetTime: Long) { - while (true) { - val current = queue.removeFirstIf { it.time <= targetTime } ?: break - // If the scheduled time is 0 (immediate) use current virtual time - if (current.time != 0L) time = current.time - current.run() - } - } - - public override fun toString(): String = name ?: "TestCoroutineContext@$hexAddress" - - private inner class Dispatcher : EventLoop(), Delay { - init { - incrementUseCount() // this event loop is never completed - } - - override fun dispatch(context: CoroutineContext, block: Runnable) { - this@TestCoroutineContext.enqueue(block) - } - - // override runBlocking to process this event loop - override fun shouldBeProcessedFromContext(): Boolean = true - - override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation) { - postDelayed(Runnable { - with(continuation) { resumeUndispatched(Unit) } - }, timeMillis) - } - - override fun invokeOnTimeout(timeMillis: Long, block: Runnable, context: CoroutineContext): DisposableHandle { - val node = postDelayed(block, timeMillis) - return object : DisposableHandle { - override fun dispose() { - queue.remove(node) - } - } - } - - override fun processNextEvent() = this@TestCoroutineContext.processNextEvent() - - public override fun toString(): String = "Dispatcher(${this@TestCoroutineContext})" - } -} - -private class TimedRunnableObsolete( - private val run: Runnable, - private val count: Long = 0, - @JvmField internal val time: Long = 0 -) : Comparable, Runnable by run, ThreadSafeHeapNode { - override var heap: ThreadSafeHeap<*>? = null - override var index: Int = 0 - - override fun run() = run.run() - - override fun compareTo(other: TimedRunnableObsolete) = if (time == other.time) { - count.compareTo(other.count) - } else { - time.compareTo(other.time) - } - - override fun toString() = "TimedRunnable(time=$time, run=$run)" -} - -/** @suppress */ -@Deprecated("This API has been deprecated to integrate with Structured Concurrency.", - ReplaceWith("testContext.runBlockingTest(testBody)", "kotlin.coroutines.test"), - level = DeprecationLevel.ERROR) // ERROR in 1.6.0, removed in 1.7.0 -@Suppress("DEPRECATION_ERROR") -public fun withTestContext(testContext: TestCoroutineContext = TestCoroutineContext(), testBody: TestCoroutineContext.() -> Unit) { - with (testContext) { - testBody() - if (!exceptions.all { it is CancellationException }) { - throw AssertionError("Coroutine encountered unhandled exceptions:\n$exceptions") - } - } -} diff --git a/kotlinx-coroutines-debug/src/CoroutinesBlockHoundIntegration.kt b/kotlinx-coroutines-debug/src/CoroutinesBlockHoundIntegration.kt index 9cafffb038..28e3831f9e 100644 --- a/kotlinx-coroutines-debug/src/CoroutinesBlockHoundIntegration.kt +++ b/kotlinx-coroutines-debug/src/CoroutinesBlockHoundIntegration.kt @@ -77,9 +77,6 @@ public class CoroutinesBlockHoundIntegration : BlockHoundIntegration { for (method in listOf("clear", "peek", "removeFirstOrNull", "addLast")) { allowBlockingCallsInside("kotlinx.coroutines.internal.ThreadSafeHeap", method) } - // [addLastIf] is only used in [EventLoop.common]. Users of [removeFirstIf]: - allowBlockingCallsInside("kotlinx.coroutines.test.TestCoroutineDispatcher", "doActionsUntil") - allowBlockingCallsInside("kotlinx.coroutines.test.TestCoroutineContext", "triggerActions") } private fun BlockHound.Builder.allowBlockingCallsInFlow() { From c5b5c660830489d6bd6f426de3b78b79fcdae41d Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Tue, 24 May 2022 15:17:01 +0300 Subject: [PATCH 15/18] Give a bigger opportunity window for cancellation in our flaky tests (#3301) --- .../common/test/WithTimeoutOrNullDurationTest.kt | 4 ++-- kotlinx-coroutines-core/common/test/WithTimeoutOrNullTest.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/kotlinx-coroutines-core/common/test/WithTimeoutOrNullDurationTest.kt b/kotlinx-coroutines-core/common/test/WithTimeoutOrNullDurationTest.kt index 992a0b52cc..92dba7b32d 100644 --- a/kotlinx-coroutines-core/common/test/WithTimeoutOrNullDurationTest.kt +++ b/kotlinx-coroutines-core/common/test/WithTimeoutOrNullDurationTest.kt @@ -132,9 +132,9 @@ class WithTimeoutOrNullDurationTest : TestBase() { @Test fun testOuterTimeout() = runTest { var counter = 0 - val result = withTimeoutOrNull(250.milliseconds) { + val result = withTimeoutOrNull(320.milliseconds) { while (true) { - val inner = withTimeoutOrNull(100.milliseconds) { + val inner = withTimeoutOrNull(150.milliseconds) { while (true) { yield() } diff --git a/kotlinx-coroutines-core/common/test/WithTimeoutOrNullTest.kt b/kotlinx-coroutines-core/common/test/WithTimeoutOrNullTest.kt index 90bcf2dac3..ee896c9bf0 100644 --- a/kotlinx-coroutines-core/common/test/WithTimeoutOrNullTest.kt +++ b/kotlinx-coroutines-core/common/test/WithTimeoutOrNullTest.kt @@ -129,9 +129,9 @@ class WithTimeoutOrNullTest : TestBase() { @Test fun testOuterTimeout() = runTest { var counter = 0 - val result = withTimeoutOrNull(250) { + val result = withTimeoutOrNull(320) { while (true) { - val inner = withTimeoutOrNull(100) { + val inner = withTimeoutOrNull(150) { while (true) { yield() } From 937f430ff564abc408e50f997caf8b0051878230 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Tue, 24 May 2022 15:54:07 +0300 Subject: [PATCH 16/18] Tweak timeout for another Windows test --- kotlinx-coroutines-core/jvm/test/WithTimeoutOrNullJvmTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotlinx-coroutines-core/jvm/test/WithTimeoutOrNullJvmTest.kt b/kotlinx-coroutines-core/jvm/test/WithTimeoutOrNullJvmTest.kt index 377fcf462e..cce77bb617 100644 --- a/kotlinx-coroutines-core/jvm/test/WithTimeoutOrNullJvmTest.kt +++ b/kotlinx-coroutines-core/jvm/test/WithTimeoutOrNullJvmTest.kt @@ -59,7 +59,7 @@ class WithTimeoutOrNullJvmTest : TestBase() { @Test fun testIgnoredTimeoutOnNullThrowsOnYield() = runTest { val value = withTimeoutOrNull(1) { - Thread.sleep(10) + Thread.sleep(75) yield() } assertNull(value) From 9ea4fea065613cbebf04dc09a64186450831805a Mon Sep 17 00:00:00 2001 From: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com> Date: Wed, 25 May 2022 12:54:57 +0300 Subject: [PATCH 17/18] Mention swallowing CancellationExceptions in the docs (#3302) --- docs/topics/cancellation-and-timeouts.md | 50 ++++++++++++++++--- .../jvm/test/guide/example-cancel-03.kt | 16 +++--- .../jvm/test/guide/example-cancel-04.kt | 16 +++--- .../jvm/test/guide/example-cancel-05.kt | 6 +-- .../jvm/test/guide/example-cancel-06.kt | 20 ++++++-- .../jvm/test/guide/example-cancel-07.kt | 4 +- .../jvm/test/guide/example-cancel-08.kt | 26 +++------- .../jvm/test/guide/example-cancel-09.kt | 13 ++--- .../jvm/test/guide/example-cancel-10.kt | 36 +++++++++++++ .../test/guide/test/CancellationGuideTest.kt | 24 ++++----- 10 files changed, 137 insertions(+), 74 deletions(-) create mode 100644 kotlinx-coroutines-core/jvm/test/guide/example-cancel-10.kt diff --git a/docs/topics/cancellation-and-timeouts.md b/docs/topics/cancellation-and-timeouts.md index 43977a9b44..f2e642ab22 100644 --- a/docs/topics/cancellation-and-timeouts.md +++ b/docs/topics/cancellation-and-timeouts.md @@ -103,6 +103,42 @@ job: I'm sleeping 4 ... main: Now I can quit. --> +The same problem can be observed by catching a [CancellationException] and not rethrowing it: + +```kotlin +import kotlinx.coroutines.* + +fun main() = runBlocking { +//sampleStart + val job = launch(Dispatchers.Default) { + repeat(5) { i -> + try { + // print a message twice a second + println("job: I'm sleeping $i ...") + delay(500) + } catch (e: Exception) { + // log the exception + println(e) + } + } + } + delay(1300L) // delay a bit + println("main: I'm tired of waiting!") + job.cancelAndJoin() // cancels the job and waits for its completion + println("main: Now I can quit.") +//sampleEnd +} +``` +{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} + +> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-03.kt). +> +{type="note"} + +While catching `Exception` is an anti-pattern, this issue may surface in more subtle ways, like when using the +[`runCatching`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/run-catching.html) function, +which does not know rethrow [CancellationException]. + ## Making computation code cancellable There are two approaches to making computation code cancellable. The first one is to periodically @@ -137,7 +173,7 @@ fun main() = runBlocking { ``` {kotlin-runnable="true" kotlin-min-compiler-version="1.3"} -> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-03.kt). +> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-04.kt). > {type="note"} @@ -182,7 +218,7 @@ fun main() = runBlocking { ``` {kotlin-runnable="true" kotlin-min-compiler-version="1.3"} -> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-04.kt). +> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-05.kt). > {type="note"} @@ -237,7 +273,7 @@ fun main() = runBlocking { ``` {kotlin-runnable="true" kotlin-min-compiler-version="1.3"} -> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-05.kt). +> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-06.kt). > {type="note"} @@ -275,7 +311,7 @@ fun main() = runBlocking { ``` {kotlin-runnable="true" kotlin-min-compiler-version="1.3"} -> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-06.kt). +> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-07.kt). > {type="note"} @@ -318,7 +354,7 @@ fun main() = runBlocking { ``` {kotlin-runnable="true" kotlin-min-compiler-version="1.3"} -> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-07.kt). +> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-08.kt). > {type="note"} @@ -378,7 +414,7 @@ fun main() { ``` {kotlin-runnable="true" kotlin-min-compiler-version="1.3"} -> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-08.kt). +> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-09.kt). > {type="note"} @@ -431,7 +467,7 @@ fun main() { ``` {kotlin-runnable="true" kotlin-min-compiler-version="1.3"} -> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-09.kt). +> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-10.kt). > {type="note"} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-03.kt b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-03.kt index 3daaf49b36..2cf0770a7d 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-03.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-03.kt @@ -8,15 +8,15 @@ package kotlinx.coroutines.guide.exampleCancel03 import kotlinx.coroutines.* fun main() = runBlocking { - val startTime = currentTimeMillis() val job = launch(Dispatchers.Default) { - var nextPrintTime = startTime - var i = 0 - while (isActive) { // cancellable computation loop - // print a message twice a second - if (currentTimeMillis() >= nextPrintTime) { - println("job: I'm sleeping ${i++} ...") - nextPrintTime += 500L + repeat(5) { i -> + try { + // print a message twice a second + println("job: I'm sleeping $i ...") + delay(500) + } catch (e: Exception) { + // log the exception + println(e) } } } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-04.kt b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-04.kt index b1b9a9bfd5..7d7f3bdee2 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-04.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-04.kt @@ -8,14 +8,16 @@ package kotlinx.coroutines.guide.exampleCancel04 import kotlinx.coroutines.* fun main() = runBlocking { - val job = launch { - try { - repeat(1000) { i -> - println("job: I'm sleeping $i ...") - delay(500L) + val startTime = currentTimeMillis() + val job = launch(Dispatchers.Default) { + var nextPrintTime = startTime + var i = 0 + while (isActive) { // cancellable computation loop + // print a message twice a second + if (currentTimeMillis() >= nextPrintTime) { + println("job: I'm sleeping ${i++} ...") + nextPrintTime += 500L } - } finally { - println("job: I'm running finally") } } delay(1300L) // delay a bit diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-05.kt b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-05.kt index 9772ae5739..97ea956ec3 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-05.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-05.kt @@ -15,11 +15,7 @@ fun main() = runBlocking { delay(500L) } } finally { - withContext(NonCancellable) { - println("job: I'm running finally") - delay(1000L) - println("job: And I've just delayed for 1 sec because I'm non-cancellable") - } + println("job: I'm running finally") } } delay(1300L) // delay a bit diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-06.kt b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-06.kt index e1afd057c4..ef66f5df09 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-06.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-06.kt @@ -8,10 +8,22 @@ package kotlinx.coroutines.guide.exampleCancel06 import kotlinx.coroutines.* fun main() = runBlocking { - withTimeout(1300L) { - repeat(1000) { i -> - println("I'm sleeping $i ...") - delay(500L) + val job = launch { + try { + repeat(1000) { i -> + println("job: I'm sleeping $i ...") + delay(500L) + } + } finally { + withContext(NonCancellable) { + println("job: I'm running finally") + delay(1000L) + println("job: And I've just delayed for 1 sec because I'm non-cancellable") + } } } + delay(1300L) // delay a bit + println("main: I'm tired of waiting!") + job.cancelAndJoin() // cancels the job and waits for its completion + println("main: Now I can quit.") } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-07.kt b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-07.kt index 8c57b42903..ea4a10353f 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-07.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-07.kt @@ -8,12 +8,10 @@ package kotlinx.coroutines.guide.exampleCancel07 import kotlinx.coroutines.* fun main() = runBlocking { - val result = withTimeoutOrNull(1300L) { + withTimeout(1300L) { repeat(1000) { i -> println("I'm sleeping $i ...") delay(500L) } - "Done" // will get cancelled before it produces this result } - println("Result is $result") } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-08.kt b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-08.kt index e7def132ae..79d08091c2 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-08.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-08.kt @@ -7,25 +7,13 @@ package kotlinx.coroutines.guide.exampleCancel08 import kotlinx.coroutines.* -var acquired = 0 - -class Resource { - init { acquired++ } // Acquire the resource - fun close() { acquired-- } // Release the resource -} - -fun main() { - runBlocking { - repeat(100_000) { // Launch 100K coroutines - launch { - val resource = withTimeout(60) { // Timeout of 60 ms - delay(50) // Delay for 50 ms - Resource() // Acquire a resource and return it from withTimeout block - } - resource.close() // Release the resource - } +fun main() = runBlocking { + val result = withTimeoutOrNull(1300L) { + repeat(1000) { i -> + println("I'm sleeping $i ...") + delay(500L) } + "Done" // will get cancelled before it produces this result } - // Outside of runBlocking all coroutines have completed - println(acquired) // Print the number of resources still acquired + println("Result is $result") } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-09.kt b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-09.kt index 95424f5108..13910a6c8a 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-09.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-09.kt @@ -18,16 +18,11 @@ fun main() { runBlocking { repeat(100_000) { // Launch 100K coroutines launch { - var resource: Resource? = null // Not acquired yet - try { - withTimeout(60) { // Timeout of 60 ms - delay(50) // Delay for 50 ms - resource = Resource() // Store a resource to the variable if acquired - } - // We can do something else with the resource here - } finally { - resource?.close() // Release the resource if it was acquired + val resource = withTimeout(60) { // Timeout of 60 ms + delay(50) // Delay for 50 ms + Resource() // Acquire a resource and return it from withTimeout block } + resource.close() // Release the resource } } } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-10.kt b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-10.kt new file mode 100644 index 0000000000..336f5e3a3a --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-10.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from cancellation-and-timeouts.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleCancel10 + +import kotlinx.coroutines.* + +var acquired = 0 + +class Resource { + init { acquired++ } // Acquire the resource + fun close() { acquired-- } // Release the resource +} + +fun main() { + runBlocking { + repeat(100_000) { // Launch 100K coroutines + launch { + var resource: Resource? = null // Not acquired yet + try { + withTimeout(60) { // Timeout of 60 ms + delay(50) // Delay for 50 ms + resource = Resource() // Store a resource to the variable if acquired + } + // We can do something else with the resource here + } finally { + resource?.close() // Release the resource if it was acquired + } + } + } + } + // Outside of runBlocking all coroutines have completed + println(acquired) // Print the number of resources still acquired +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/test/CancellationGuideTest.kt b/kotlinx-coroutines-core/jvm/test/guide/test/CancellationGuideTest.kt index 0cff63a834..871e4911e8 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/test/CancellationGuideTest.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/test/CancellationGuideTest.kt @@ -34,8 +34,8 @@ class CancellationGuideTest { } @Test - fun testExampleCancel03() { - test("ExampleCancel03") { kotlinx.coroutines.guide.exampleCancel03.main() }.verifyLines( + fun testExampleCancel04() { + test("ExampleCancel04") { kotlinx.coroutines.guide.exampleCancel04.main() }.verifyLines( "job: I'm sleeping 0 ...", "job: I'm sleeping 1 ...", "job: I'm sleeping 2 ...", @@ -45,8 +45,8 @@ class CancellationGuideTest { } @Test - fun testExampleCancel04() { - test("ExampleCancel04") { kotlinx.coroutines.guide.exampleCancel04.main() }.verifyLines( + fun testExampleCancel05() { + test("ExampleCancel05") { kotlinx.coroutines.guide.exampleCancel05.main() }.verifyLines( "job: I'm sleeping 0 ...", "job: I'm sleeping 1 ...", "job: I'm sleeping 2 ...", @@ -57,8 +57,8 @@ class CancellationGuideTest { } @Test - fun testExampleCancel05() { - test("ExampleCancel05") { kotlinx.coroutines.guide.exampleCancel05.main() }.verifyLines( + fun testExampleCancel06() { + test("ExampleCancel06") { kotlinx.coroutines.guide.exampleCancel06.main() }.verifyLines( "job: I'm sleeping 0 ...", "job: I'm sleeping 1 ...", "job: I'm sleeping 2 ...", @@ -70,8 +70,8 @@ class CancellationGuideTest { } @Test - fun testExampleCancel06() { - test("ExampleCancel06") { kotlinx.coroutines.guide.exampleCancel06.main() }.verifyLinesStartWith( + fun testExampleCancel07() { + test("ExampleCancel07") { kotlinx.coroutines.guide.exampleCancel07.main() }.verifyLinesStartWith( "I'm sleeping 0 ...", "I'm sleeping 1 ...", "I'm sleeping 2 ...", @@ -80,8 +80,8 @@ class CancellationGuideTest { } @Test - fun testExampleCancel07() { - test("ExampleCancel07") { kotlinx.coroutines.guide.exampleCancel07.main() }.verifyLines( + fun testExampleCancel08() { + test("ExampleCancel08") { kotlinx.coroutines.guide.exampleCancel08.main() }.verifyLines( "I'm sleeping 0 ...", "I'm sleeping 1 ...", "I'm sleeping 2 ...", @@ -90,8 +90,8 @@ class CancellationGuideTest { } @Test - fun testExampleCancel09() { - test("ExampleCancel09") { kotlinx.coroutines.guide.exampleCancel09.main() }.verifyLines( + fun testExampleCancel10() { + test("ExampleCancel10") { kotlinx.coroutines.guide.exampleCancel10.main() }.verifyLines( "0" ) } From eb9dd554df5d069f68af741988427168a8428abe Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 25 May 2022 23:01:14 +0300 Subject: [PATCH 18/18] Version 1.6.2 --- CHANGES.md | 10 ++++++++++ README.md | 12 ++++++------ gradle.properties | 2 +- gradle/publish-npm-js.gradle | 22 ++++++++++------------ kotlinx-coroutines-debug/README.md | 2 +- kotlinx-coroutines-test/README.md | 2 +- ui/coroutines-guide-ui.md | 2 +- 7 files changed, 30 insertions(+), 22 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 2aba4abf59..024d1d0807 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,15 @@ # Change log for kotlinx.coroutines +## Version 1.6.2 + +* Fixed a bug with `ThreadLocalElement` not being correctly updated when the most outer `suspend` function was called directly without `kotlinx.coroutines` (#2930). +* Fixed multiple data races: one that might have been affecting `runBlocking` event loop, and a benign data race in `Mutex` (#3250, #3251). +* Obsolete `TestCoroutineContext` is removed, which fixes the `kotlinx-coroutines-test` JPMS package being split between `kotlinx-coroutines-core` and `kotlinx-coroutines-test` (#3218). +* Updated the ProGuard rules to further shrink the size of the resulting DEX file with coroutines (#3111, #3263). Thanks, @agrieve! +* Atomicfu is updated to `0.17.2`, which includes a more efficient and robust JS IR transformer (#3255). +* Kotlin is updated to `1.6.21`, Gradle version is updated to `7.4.2` (#3281). Thanks, @wojtek-kalicinski! +* Various documentation improvements. + ## Version 1.6.1 * Rollback of time-related functions dispatching on `Dispatchers.Main`. diff --git a/README.md b/README.md index 6fb614c084..41f461dc90 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.6.1)](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.6.1/pom) +[![Download](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.6.2)](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.6.2/pom) [![Kotlin](https://img.shields.io/badge/kotlin-1.6.0-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/) @@ -84,7 +84,7 @@ Add dependencies (you can also add other modules that you need): org.jetbrains.kotlinx kotlinx-coroutines-core - 1.6.1 + 1.6.2 ``` @@ -102,7 +102,7 @@ Add dependencies (you can also add other modules that you need): ```kotlin dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.2") } ``` @@ -132,7 +132,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.6.1") +implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.2") ``` This gives you access to the Android [Dispatchers.Main] @@ -167,7 +167,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.6.1") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.2") } } ``` @@ -179,7 +179,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://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core-js/1.6.1/jar) +[`kotlinx-coroutines-core-js`](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core-js/1.6.2/jar) (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 404e79b4fc..8e96d1605d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ # # Kotlin -version=1.6.1-SNAPSHOT +version=1.6.2-SNAPSHOT group=org.jetbrains.kotlinx kotlin_version=1.6.21 diff --git a/gradle/publish-npm-js.gradle b/gradle/publish-npm-js.gradle index 382c6749ca..9d4152770c 100644 --- a/gradle/publish-npm-js.gradle +++ b/gradle/publish-npm-js.gradle @@ -40,17 +40,15 @@ task preparePublishNpm(type: Copy) { task publishNpm(type: NpmTask, dependsOn: [preparePublishNpm]) { workingDir = npmDeployDir - - doFirst { - def npmDeployTag = distTag(version) - def deployArgs = ['publish', - "--//registry.npmjs.org/:_authToken=$authToken", - "--tag=$npmDeployTag"] - if (dryRun == "true") { - println("$npmDeployDir \$ npm arguments: $deployArgs") - args = ['pack'] - } else { - args = deployArgs - } + + def npmDeployTag = distTag(version) + def deployArgs = ['publish', + "--//registry.npmjs.org/:_authToken=$authToken", + "--tag=$npmDeployTag"] + if (dryRun == "true") { + println("$npmDeployDir \$ npm arguments: $deployArgs") + args = ['pack'] + } else { + args = deployArgs } } diff --git a/kotlinx-coroutines-debug/README.md b/kotlinx-coroutines-debug/README.md index 78a217e692..ab8704fb28 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.6.1.jar`. +You can run your application with an additional argument: `-javaagent:kotlinx-coroutines-debug-1.6.2.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 360c3bf178..403a89a4d3 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.6.1' + testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.2' } ``` diff --git a/ui/coroutines-guide-ui.md b/ui/coroutines-guide-ui.md index fd4fcf0f72..a9ebc1f940 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.6.1" +implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.2" ``` You can clone [kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) project from GitHub onto your