diff --git a/CHANGES.md b/CHANGES.md index 656a818ea0..510c35accf 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,23 @@ # Change log for kotlinx.coroutines +## Version 1.5.1 + +* Atomic `update`, `getAndUpdate`, and `updateAndGet` operations of `MutableStateFlow` (#2720). +* `Executor.asCoroutineDispatcher` implementation improvements (#2601): + * If the target executor is `ScheduledExecutorService`, then its `schedule` API is used for time-related coroutine operations. + * `RemoveOnCancelPolicy` is now part of the public contract. +* Introduced overloads for `Task.asDeferred` and `Task.await` that accept `CancellationTokenSource` for bidirectional cancellation (#2527). +* Reactive streams are updated to `1.0.3` (#2740). +* `CopyableThrowable` is allowed to modify the exception message during stacktrace recovery (#1931). +* `CoroutineDispatcher.releaseInterceptedContinuation` is now a `final` method (#2785). +* Closing a Handler underlying `Handler.asCoroutineDispatcher` now causes the dispatched coroutines to be canceled on `Dispatchers.IO (#2778)`. +* Kotlin is updated to 1.5.20. +* Fixed a spurious `ClassCastException` in `releaseInterceptedContinuation` and `IllegalStateException` from `tryReleaseClaimedContinuation` (#2736, #2768). +* Fixed inconsistent exception message during stacktrace recovery for non-suspending channel iterators (#2749). +* Fixed linear stack usage for `CompletableFuture.asDeferred` when the target future has a long chain of listeners (#2730). +* Any exceptions from `CoroutineDispatcher.isDispatchNeeded` are now considered as fatal and are propagated to the caller (#2733). +* Internal `DebugProbesKt` (used in the debugger implementation) are moved from `debug` to `core` module. + ## Version 1.5.0 Note that this is a full changelog relative to 1.4.3 version. Changelog relative to 1.5.0-RC can be found in the end. diff --git a/README.md b/README.md index fab386fa43..3cdce4387b 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,12 @@ [![official JetBrains 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.5.0)](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.5.0/pom) -[![Kotlin](https://img.shields.io/badge/kotlin-1.5.0-blue.svg?logo=kotlin)](http://kotlinlang.org) +[![Download](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.5.1)](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.5.1/pom) +[![Kotlin](https://img.shields.io/badge/kotlin-1.5.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/) Library support for Kotlin coroutines with [multiplatform](#multiplatform) support. -This is a companion version for the Kotlin `1.5.0` release. +This is a companion version for the Kotlin `1.5.20` release. ```kotlin suspend fun main() = coroutineScope { @@ -35,7 +35,7 @@ suspend fun main() = coroutineScope { * [select] expression support and more. * [core/jvm](kotlinx-coroutines-core/jvm/) — additional core features available on Kotlin/JVM: * [Dispatchers.IO] dispatcher for blocking coroutines; - * [Executor.asCoroutineDispatcher] extension, custom thread pools, and more. + * [Executor.asCoroutineDispatcher][asCoroutineDispatcher] extension, custom thread pools, and more. * [core/js](kotlinx-coroutines-core/js/) — additional core features available on Kotlin/JS: * Integration with `Promise` via [Promise.await] and [promise] builder; * Integration with `Window` via [Window.asCoroutineDispatcher], etc. @@ -83,7 +83,7 @@ Add dependencies (you can also add other modules that you need): org.jetbrains.kotlinx kotlinx-coroutines-core - 1.5.0 + 1.5.1 ``` @@ -91,7 +91,7 @@ And make sure that you use the latest Kotlin version: ```xml - 1.5.0 + 1.5.20 ``` @@ -101,7 +101,7 @@ Add dependencies (you can also add other modules that you need): ```groovy dependencies { - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1' } ``` @@ -109,7 +109,7 @@ And make sure that you use the latest Kotlin version: ```groovy buildscript { - ext.kotlin_version = '1.5.0' + ext.kotlin_version = '1.5.20' } ``` @@ -127,7 +127,7 @@ Add dependencies (you can also add other modules that you need): ```groovy dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1") } ``` @@ -135,7 +135,7 @@ And make sure that you use the latest Kotlin version: ```groovy plugins { - kotlin("jvm") version "1.5.0" + kotlin("jvm") version "1.5.20" } ``` @@ -147,7 +147,7 @@ Add [`kotlinx-coroutines-android`](ui/kotlinx-coroutines-android) module as a dependency when using `kotlinx.coroutines` on Android: ```groovy -implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0' +implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.1' ``` This gives you access to the Android [Dispatchers.Main] @@ -180,7 +180,7 @@ In common code that should get compiled for different platforms, you can add a d ```groovy commonMain { dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1") } } ``` @@ -192,7 +192,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.5.0/jar) +[`kotlinx-coroutines-core-js`](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core-js/1.5.1/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 @@ -233,10 +233,10 @@ See [Contributing Guidelines](CONTRIBUTING.md). [SupervisorJob()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-supervisor-job.html [CoroutineExceptionHandler]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-exception-handler/index.html [Dispatchers.IO]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-i-o.html -[Executor.asCoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/java.util.concurrent.-executor/as-coroutine-dispatcher.html -[Promise.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/kotlin.js.-promise/await.html +[asCoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/as-coroutine-dispatcher.html +[Promise.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/await.html [promise]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/promise.html -[Window.asCoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/org.w3c.dom.-window/as-coroutine-dispatcher.html +[Window.asCoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/as-coroutine-dispatcher.html @@ -261,7 +261,7 @@ See [Contributing Guidelines](CONTRIBUTING.md). -[Dispatchers.setMain]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/kotlinx.coroutines.-dispatchers/set-main.html +[Dispatchers.setMain]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/set-main.html [TestCoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-scope/index.html @@ -281,23 +281,23 @@ See [Contributing Guidelines](CONTRIBUTING.md). -[CompletionStage.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-jdk8/kotlinx.coroutines.future/java.util.concurrent.-completion-stage/await.html +[CompletionStage.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-jdk8/kotlinx.coroutines.future/await.html -[ListenableFuture.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-guava/kotlinx.coroutines.guava/com.google.common.util.concurrent.-listenable-future/await.html +[ListenableFuture.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-guava/kotlinx.coroutines.guava/await.html -[Task.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-play-services/kotlinx.coroutines.tasks/com.google.android.gms.tasks.-task/await.html +[Task.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-play-services/kotlinx.coroutines.tasks/await.html -[Publisher.collect]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactive/kotlinx.coroutines.reactive/org.reactivestreams.-publisher/collect.html -[Publisher.awaitSingle]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactive/kotlinx.coroutines.reactive/org.reactivestreams.-publisher/await-single.html +[Publisher.collect]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactive/kotlinx.coroutines.reactive/collect.html +[Publisher.awaitSingle]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactive/kotlinx.coroutines.reactive/await-single.html [kotlinx.coroutines.reactive.publish]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactive/kotlinx.coroutines.reactive/publish.html diff --git a/build.gradle b/build.gradle index fc52d8cc53..e4b12ff3ad 100644 --- a/build.gradle +++ b/build.gradle @@ -1,16 +1,20 @@ /* * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ + +import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.konan.target.HostManager import org.gradle.util.VersionNumber +import org.jetbrains.dokka.gradle.DokkaTaskPartial +import org.jetbrains.dokka.gradle.DokkaMultiModuleTask apply plugin: 'jdk-convention' -apply from: rootProject.file("gradle/experimental.gradle") +apply from: rootProject.file("gradle/opt-in.gradle") def coreModule = "kotlinx-coroutines-core" // Not applicable for Kotlin plugin -def sourceless = ['kotlinx.coroutines', 'site', 'kotlinx-coroutines-bom', 'integration-testing'] -def internal = ['kotlinx.coroutines', 'site', 'benchmarks', 'js-stub', 'stdlib-stubs', 'integration-testing'] +def sourceless = ['kotlinx.coroutines', 'kotlinx-coroutines-bom', 'integration-testing'] +def internal = ['kotlinx.coroutines', 'benchmarks', 'integration-testing'] // Not published def unpublished = internal + ['example-frontend-js', 'android-unit-tests'] @@ -32,9 +36,6 @@ buildscript { throw new IllegalArgumentException("'kotlin_snapshot_version' should be defined when building with snapshot compiler") } } - // These three flags are enabled in train builds for JVM IR compiler testing - ext.jvm_ir_enabled = rootProject.properties['enable_jvm_ir'] != null - ext.jvm_ir_api_check_enabled = rootProject.properties['enable_jvm_ir_api_check'] != null ext.native_targets_enabled = rootProject.properties['disable_native_targets'] == null // Determine if any project dependency is using a snapshot version @@ -42,7 +43,7 @@ buildscript { rootProject.properties.each { key, value -> if (key.endsWith("_version") && value instanceof String && value.endsWith("-SNAPSHOT")) { println("NOTE: USING SNAPSHOT VERSION: $key=$value") - ext.using_snapshot_version=true + ext.using_snapshot_version = true } } @@ -53,11 +54,10 @@ buildscript { } repositories { - // Leftover until we migrated to Dokka 1.4.30 - maven { url "https://kotlin.bintray.com/kotlin-dev" } - maven { url "https://jetbrains.bintray.com/kotlin-native-dependencies" } + mavenCentral() maven { url "https://plugins.gradle.org/m2/" } maven { url "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" } + mavenLocal() } dependencies { @@ -74,9 +74,6 @@ buildscript { CacheRedirector.configureBuildScript(buildscript, rootProject) } - -import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType - // todo:KLUDGE: Hierarchical project structures are not fully supported in IDEA, enable only for a regular built if (!Idea.active) { ext.set("kotlin.mpp.enableGranularSourceSetsMetadata", "true") @@ -127,7 +124,6 @@ apply plugin: "binary-compatibility-validator" apiValidation { ignoredProjects += unpublished + ["kotlinx-coroutines-bom"] if (build_snapshot_train) { - ignoredProjects.remove("site") ignoredProjects.remove("example-frontend-js") ignoredProjects.add("kotlinx-coroutines-core") } @@ -166,9 +162,8 @@ configure(subprojects.findAll { !sourceless.contains(it.name) }) { // Configure options for all Kotlin compilation tasks tasks.withType(org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile).all { - kotlinOptions.freeCompilerArgs += experimentalAnnotations.collect { "-Xuse-experimental=" + it } + kotlinOptions.freeCompilerArgs += optInAnnotations.collect { "-Xopt-in=" + it } kotlinOptions.freeCompilerArgs += "-progressive" - kotlinOptions.freeCompilerArgs += "-XXLanguage:+InlineClasses" // Disable KT-36770 for RxJava2 integration kotlinOptions.freeCompilerArgs += "-XXLanguage:-ProhibitUsingNullableTypeParameterAgainstNotNullAnnotated" // Remove null assertions to get smaller bytecode on Android @@ -227,11 +222,12 @@ configure(subprojects.findAll { } def core_docs_url = "https://kotlin.github.io/kotlinx.coroutines/$coreModule/" -def core_docs_file = "$projectDir/kotlinx-coroutines-core/build/dokka/kotlinx-coroutines-core/package-list" +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) }) { if (it.name != 'kotlinx-coroutines-bom') { - apply from: rootProject.file('gradle/dokka.gradle') + apply from: rootProject.file('gradle/dokka.gradle.kts') } apply from: rootProject.file('gradle/publish.gradle') } @@ -239,11 +235,12 @@ configure(subprojects.findAll { !unpublished.contains(it.name) }) { configure(subprojects.findAll { !unpublished.contains(it.name) }) { if (it.name != "kotlinx-coroutines-bom") { if (it.name != coreModule) { - dokka.dependsOn project(":$coreModule").dokka - tasks.withType(dokka.getClass()) { - externalDocumentationLink { - url = new URL(core_docs_url) - packageListUrl = new File(core_docs_file).toURI().toURL() + tasks.withType(DokkaTaskPartial.class) { + dokkaSourceSets.configureEach { + externalDocumentationLink { + url.set(new URL(core_docs_url)) + packageListUrl.set(new File(core_docs_file).toURI().toURL()) + } } } } @@ -276,15 +273,43 @@ apply plugin: 'kotlinx-knit' knit { siteRoot = "https://kotlin.github.io/kotlinx.coroutines" moduleRoots = [".", "integration", "reactive", "ui"] + moduleDocs = "build/dokka/htmlPartial" + dokkaMultiModuleRoot = "build/dokka/htmlMultiModule/" } -knitPrepare.dependsOn getTasksByName("dokka", true) +knitPrepare.dependsOn getTasksByName("dokkaHtmlMultiModule", true) -// Disable binary compatibility check for JVM IR compiler output by default -if (jvm_ir_enabled) { - subprojects { project -> - configure(tasks.matching { it.name == "apiCheck" }) { - enabled = enabled && jvm_ir_api_check_enabled - } +dependencies { + dokkaHtmlMultiModulePlugin("org.jetbrains.kotlinx:dokka-pathsaver-plugin:$knit_version") +} + +// Opt-in for build scan in order to troubleshoot Gradle on TC +if (hasProperty('buildScan')) { + buildScan { + termsOfServiceUrl = 'https://gradle.com/terms-of-service' + termsOfServiceAgree = 'yes' } } + +/* + * kotlinx-coroutines-core dependency leaks into test runtime classpath via kotlin-compiler-embeddable + * and conflicts with our own test/runtime incompatibilities (e.g. when class is moved from a main to test), + * so we do substitution here + */ +allprojects { subProject -> + subProject + .configurations + .matching { + // Excluding substituted project itself because of circular dependencies, but still do it + // for "*Test*" configurations + subProject.name != "kotlinx-coroutines-core" || it.name.contains("Test") + } + .configureEach { conf -> + conf.resolutionStrategy.dependencySubstitution { + substitute(module("org.jetbrains.kotlinx:kotlinx-coroutines-core")) + .using(project(":kotlinx-coroutines-core")) + .because("Because Kotlin compiler embeddable leaks coroutines into the runtime classpath, " + + "triggering all sort of incompatible class changes errors") + } + } +} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 80214522b8..c54e226af1 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -12,13 +12,11 @@ val cacheRedirectorEnabled = System.getenv("CACHE_REDIRECTOR")?.toBoolean() == t val buildSnapshotTrain = properties["build_snapshot_train"]?.toString()?.toBoolean() == true repositories { + mavenCentral() if (cacheRedirectorEnabled) { maven("https://cache-redirector.jetbrains.com/plugins.gradle.org/m2") - maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-dev") } else { maven("https://plugins.gradle.org/m2") - // Leftover until we migrated to Dokka 1.4.30 - maven("https://dl.bintray.com/kotlin/kotlin-dev") } maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev") @@ -47,4 +45,5 @@ fun version(target: String): String { dependencies { implementation(kotlin("gradle-plugin", version("kotlin"))) implementation("org.jetbrains.dokka:dokka-gradle-plugin:${version("dokka")}") + implementation("org.jetbrains.dokka:dokka-core:${version("dokka")}") } diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts index c2e859f65d..e30c3ee597 100644 --- a/buildSrc/settings.gradle.kts +++ b/buildSrc/settings.gradle.kts @@ -4,6 +4,7 @@ pluginManagement { val build_snapshot_train: String? by settings repositories { + maven(url = "https://maven.pkg.jetbrains.space/kotlin/p/dokka/dev/") val cacheRedirectorEnabled = System.getenv("CACHE_REDIRECTOR")?.toBoolean() == true if (cacheRedirectorEnabled) { println("Redirecting repositories for buildSrc buildscript") diff --git a/buildSrc/src/main/kotlin/CacheRedirector.kt b/buildSrc/src/main/kotlin/CacheRedirector.kt index 9f6efd2cdc..bcadd7364e 100644 --- a/buildSrc/src/main/kotlin/CacheRedirector.kt +++ b/buildSrc/src/main/kotlin/CacheRedirector.kt @@ -21,46 +21,15 @@ private val cacheRedirectorEnabled = System.getenv("CACHE_REDIRECTOR")?.toBoolea private val mirroredUrls = listOf( "https://cdn.azul.com/zulu/bin", "https://clojars.org/repo", - "https://dl.bintray.com/d10xa/maven", - "https://dl.bintray.com/groovy/maven", - "https://dl.bintray.com/jetbrains/maven-patched", - "https://dl.bintray.com/jetbrains/scala-plugin-deps", - "https://dl.bintray.com/kodein-framework/Kodein-DI", - "https://dl.bintray.com/konsoletyper/teavm", - "https://dl.bintray.com/kotlin/kotlin-dev", - "https://dl.bintray.com/kotlin/kotlin-eap", - "https://dl.bintray.com/kotlin/kotlinx.html", - "https://dl.bintray.com/kotlin/kotlinx", - "https://dl.bintray.com/kotlin/ktor", - "https://dl.bintray.com/scalacenter/releases", - "https://dl.bintray.com/scalamacros/maven", - "https://dl.bintray.com/kotlin/exposed", - "https://dl.bintray.com/cy6ergn0m/maven", - "https://dl.bintray.com/kotlin/kotlin-js-wrappers", "https://dl.google.com/android/repository", "https://dl.google.com/dl/android/maven2", "https://dl.google.com/dl/android/studio/ide-zips", "https://dl.google.com/go", "https://download.jetbrains.com", - "https://jcenter.bintray.com", - "https://jetbrains.bintray.com/dekaf", - "https://jetbrains.bintray.com/intellij-jbr", - "https://jetbrains.bintray.com/intellij-jdk", - "https://jetbrains.bintray.com/intellij-plugin-service", - "https://jetbrains.bintray.com/intellij-terraform", - "https://jetbrains.bintray.com/intellij-third-party-dependencies", - "https://jetbrains.bintray.com/jediterm", - "https://jetbrains.bintray.com/kotlin-native-dependencies", - "https://jetbrains.bintray.com/markdown", - "https://jetbrains.bintray.com/teamcity-rest-client", - "https://jetbrains.bintray.com/test-discovery", - "https://jetbrains.bintray.com/wormhole", "https://jitpack.io", "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev", "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap", "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/eap", - "https://kotlin.bintray.com/dukat", - "https://kotlin.bintray.com/kotlin-dependencies", "https://oss.sonatype.org/content/repositories/releases", "https://oss.sonatype.org/content/repositories/snapshots", "https://oss.sonatype.org/content/repositories/staging", @@ -85,10 +54,7 @@ private val mirroredUrls = listOf( ) private val aliases = mapOf( - "https://repo.maven.apache.org/maven2" to "https://repo1.maven.org/maven2", // Maven Central - "https://kotlin.bintray.com/kotlin-dev" to "https://dl.bintray.com/kotlin/kotlin-dev", - "https://kotlin.bintray.com/kotlin-eap" to "https://dl.bintray.com/kotlin/kotlin-eap", - "https://kotlin.bintray.com/kotlinx" to "https://dl.bintray.com/kotlin/kotlinx" + "https://repo.maven.apache.org/maven2" to "https://repo1.maven.org/maven2" // Maven Central ) private fun URI.toCacheRedirectorUri() = URI("https://cache-redirector.jetbrains.com/$host/$path") diff --git a/buildSrc/src/main/kotlin/Dokka.kt b/buildSrc/src/main/kotlin/Dokka.kt index f37aa7c151..a6b06ee1a9 100644 --- a/buildSrc/src/main/kotlin/Dokka.kt +++ b/buildSrc/src/main/kotlin/Dokka.kt @@ -2,13 +2,11 @@ * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -import org.gradle.api.Project -import org.gradle.kotlin.dsl.delegateClosureOf -import org.gradle.kotlin.dsl.withType -import org.jetbrains.dokka.DokkaConfiguration.ExternalDocumentationLink.Builder -import org.jetbrains.dokka.gradle.DokkaTask -import java.io.File -import java.net.URL +import org.gradle.api.* +import org.gradle.kotlin.dsl.* +import org.jetbrains.dokka.gradle.* +import java.io.* +import java.net.* /** * Package-list by external URL for documentation generation. @@ -17,10 +15,12 @@ fun Project.externalDocumentationLink( url: String, packageList: File = projectDir.resolve("package.list") ) { - tasks.withType().configureEach { - externalDocumentationLink(delegateClosureOf { - this.url = URL(url) - packageListUrl = packageList.toPath().toUri().toURL() - }) + tasks.withType().configureEach { + dokkaSourceSets.configureEach { + externalDocumentationLink { + this.url.set(URL(url)) + packageListUrl.set(packageList.toPath().toUri().toURL()) + } + } } } diff --git a/buildSrc/src/main/kotlin/Publishing.kt b/buildSrc/src/main/kotlin/Publishing.kt index e0124cd966..8c6dd5de3d 100644 --- a/buildSrc/src/main/kotlin/Publishing.kt +++ b/buildSrc/src/main/kotlin/Publishing.kt @@ -40,12 +40,8 @@ fun MavenPom.configureMavenCentralMetadata(project: Project) { } fun mavenRepositoryUri(): URI { - // TODO -SNAPSHOT detection can be made here as well val repositoryId: String? = System.getenv("libs.repository.id") return if (repositoryId == null) { - // Using implicitly created staging, for MPP it's likely to be a mistake because - // publication on TeamCity will create 3 independent staging repositories - System.err.println("Warning: using an implicitly created staging for coroutines") URI("https://oss.sonatype.org/service/local/staging/deploy/maven2/") } else { URI("https://oss.sonatype.org/service/local/staging/deployByRepositoryId/$repositoryId") diff --git a/buildSrc/src/main/kotlin/RunR8.kt b/buildSrc/src/main/kotlin/RunR8.kt deleted file mode 100644 index b0fccf89fc..0000000000 --- a/buildSrc/src/main/kotlin/RunR8.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.InputFiles -import org.gradle.api.tasks.JavaExec -import org.gradle.api.tasks.OutputDirectory -import org.gradle.api.tasks.bundling.Zip -import org.gradle.kotlin.dsl.get -import org.gradle.kotlin.dsl.named -import java.io.File - -/* - * Task used by our ui/android tests to test minification results - * and keep track of size of the binary. - * TODO move back to kotlinx-coroutines-android when it's migrated to the kts - */ -open class RunR8 : JavaExec() { - - @OutputDirectory - lateinit var outputDex: File - - @InputFile - lateinit var inputConfig: File - - @InputFile - val inputConfigCommon: File = File("testdata/r8-test-common.pro") - - @InputFiles - val jarFile: File = project.tasks.named("jar").get().archivePath - - init { - classpath = project.configurations["r8"] - main = "com.android.tools.r8.R8" - } - - override fun exec() { - // Resolve classpath only during execution - val arguments = mutableListOf( - "--release", - "--no-desugaring", - "--output", outputDex.absolutePath, - "--pg-conf", inputConfig.absolutePath - ) - arguments.addAll(project.configurations["runtimeClasspath"].files.map { it.absolutePath }) - arguments.add(jarFile.absolutePath) - - args = arguments - - project.delete(outputDex) - outputDex.mkdirs() - - super.exec() - } -} diff --git a/buildSrc/src/main/kotlin/kotlin-jvm-conventions.gradle.kts b/buildSrc/src/main/kotlin/kotlin-jvm-conventions.gradle.kts index 5f3193428c..c7744f8702 100644 --- a/buildSrc/src/main/kotlin/kotlin-jvm-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/kotlin-jvm-conventions.gradle.kts @@ -15,12 +15,6 @@ java { targetCompatibility = JavaVersion.VERSION_1_6 } -if (rootProject.extra.get("jvm_ir_enabled") as Boolean) { - kotlin.target.compilations.configureEach { - kotlinOptions.useIR = true - } -} - dependencies { testImplementation(kotlin("test")) // Workaround to make addSuppressed work in tests diff --git a/docs/topics/cancellation-and-timeouts.md b/docs/topics/cancellation-and-timeouts.md index 7aa51e2642..5221db922a 100644 --- a/docs/topics/cancellation-and-timeouts.md +++ b/docs/topics/cancellation-and-timeouts.md @@ -447,15 +447,15 @@ This example always prints zero. Resources do not leak. [launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html [Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html [cancelAndJoin]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/cancel-and-join.html -[Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/cancel.html +[Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/cancel.html [Job.join]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/join.html [CancellationException]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-cancellation-exception/index.html [yield]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/yield.html [isActive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/is-active.html [CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html [withContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-context.html -[NonCancellable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-non-cancellable.html +[NonCancellable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-non-cancellable/index.html [withTimeout]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-timeout.html [withTimeoutOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-timeout-or-null.html - \ No newline at end of file + diff --git a/docs/topics/channels.md b/docs/topics/channels.md index 9380b2bdfb..7f41eaec2b 100644 --- a/docs/topics/channels.md +++ b/docs/topics/channels.md @@ -626,7 +626,7 @@ delay between elements. [CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html [runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html -[kotlin.coroutines.CoroutineContext.cancelChildren]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/kotlin.coroutines.-coroutine-context/cancel-children.html +[kotlin.coroutines.CoroutineContext.cancelChildren]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/cancel-children.html [Dispatchers.Default]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html @@ -640,7 +640,7 @@ delay between elements. [Channel()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-channel.html [ticker]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/ticker.html [ReceiveChannel.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/cancel.html -[TickerMode.FIXED_DELAY]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-ticker-mode/-f-i-x-e-d_-d-e-l-a-y.html +[TickerMode.FIXED_DELAY]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-ticker-mode/-f-i-x-e-d_-d-e-l-a-y/index.html diff --git a/docs/topics/composing-suspending-functions.md b/docs/topics/composing-suspending-functions.md index 9c1a26a910..e244d8c218 100644 --- a/docs/topics/composing-suspending-functions.md +++ b/docs/topics/composing-suspending-functions.md @@ -406,7 +406,7 @@ Computation failed with ArithmeticException [launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html [Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html [Deferred]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/index.html -[CoroutineStart.LAZY]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-start/-l-a-z-y.html +[CoroutineStart.LAZY]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-start/-l-a-z-y/index.html [Deferred.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/await.html [Job.start]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/start.html [GlobalScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/index.html diff --git a/docs/topics/coroutine-context-and-dispatchers.md b/docs/topics/coroutine-context-and-dispatchers.md index 402db103b4..c35de0e084 100644 --- a/docs/topics/coroutine-context-and-dispatchers.md +++ b/docs/topics/coroutine-context-and-dispatchers.md @@ -678,8 +678,8 @@ that should be implemented. [CoroutineScope()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope.html [MainScope()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-main-scope.html [Dispatchers.Main]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-main.html -[asContextElement]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/java.lang.-thread-local/as-context-element.html -[ensurePresent]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/java.lang.-thread-local/ensure-present.html +[asContextElement]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/as-context-element.html +[ensurePresent]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/ensure-present.html [ThreadContextElement]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-thread-context-element/index.html diff --git a/docs/topics/debugging.md b/docs/topics/debugging.md index d18df7f465..5ff4d549e0 100644 --- a/docs/topics/debugging.md +++ b/docs/topics/debugging.md @@ -63,7 +63,10 @@ Exception copy logic is straightforward: 1) If the exception class implements [CopyableThrowable], [CopyableThrowable.createCopy] is used. `null` can be returned from `createCopy` to opt-out specific exception from being recovered. 2) If the exception class has class-specific fields not inherited from Throwable, the exception is not copied. - 3) Otherwise, one of the public exception's constructor is invoked reflectively with an optional `initCause` call. + 3) Otherwise, one of the public exception's constructor is invoked reflectively with an optional `initCause` call. + 4) If the reflective copy has a changed message (exception constructor passed a modified `message` parameter to the superclass), + the exception is not copied in order to preserve a human-readable message. [CopyableThrowable] does not have such a limitation + and allows the copy to have a `message` different from that of the original. ## Debug agent diff --git a/docs/topics/exception-handling.md b/docs/topics/exception-handling.md index 4cff42f357..3facd51a22 100644 --- a/docs/topics/exception-handling.md +++ b/docs/topics/exception-handling.md @@ -508,7 +508,7 @@ The scope is completed [CoroutineExceptionHandler]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-exception-handler/index.html [Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html [Deferred]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/index.html -[Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/cancel.html +[Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/cancel.html [runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html [SupervisorJob()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-supervisor-job.html [Job()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job.html diff --git a/docs/topics/flow.md b/docs/topics/flow.md index 9dd84f3f4b..7acc4f99a8 100644 --- a/docs/topics/flow.md +++ b/docs/topics/flow.md @@ -1852,7 +1852,7 @@ Integration modules include conversions from and to `Flow`, integration with Rea [CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html [runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html [Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html -[Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/cancel.html +[Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/cancel.html [Job.join]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/join.html [ensureActive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/ensure-active.html [CancellationException]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-cancellation-exception/index.html @@ -1890,7 +1890,7 @@ Integration modules include conversions from and to `Flow`, integration with Rea [catch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/catch.html [onCompletion]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/on-completion.html [launchIn]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/launch-in.html -[IntRange.asFlow]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/kotlin.ranges.-int-range/as-flow.html +[IntRange.asFlow]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/as-flow.html [cancellable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/cancellable.html diff --git a/gradle.properties b/gradle.properties index 5fbf33229e..2983dd11a0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,26 +3,26 @@ # # Kotlin -version=1.5.0-SNAPSHOT +version=1.5.1-SNAPSHOT group=org.jetbrains.kotlinx -kotlin_version=1.5.0 +kotlin_version=1.5.20 # Dependencies junit_version=4.12 junit5_version=5.7.0 -atomicfu_version=0.16.1 -knit_version=0.2.3 +atomicfu_version=0.16.2 +knit_version=0.3.0 html_version=0.7.2 -lincheck_version=2.12 -dokka_version=0.9.16-rdev-2-mpp-hacks +lincheck_version=2.14 +dokka_version=1.5.0 byte_buddy_version=1.10.9 reactor_version=3.4.1 -reactive_streams_version=1.0.2 +reactive_streams_version=1.0.3 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.5.0 +binary_compatibility_validator_version=0.6.0 blockhound_version=1.0.2.RELEASE jna_version=5.5.0 @@ -53,6 +53,6 @@ jekyll_version=4.0 # JS IR backend sometimes crashes with out-of-memory # TODO: Remove once KT-37187 is fixed -org.gradle.jvmargs=-Xmx2g +org.gradle.jvmargs=-Xmx4g kotlin.mpp.enableCompatibilityMetadataVariant=true diff --git a/gradle/compile-js-multiplatform.gradle b/gradle/compile-js-multiplatform.gradle index 1e885db0c5..d6df7e403a 100644 --- a/gradle/compile-js-multiplatform.gradle +++ b/gradle/compile-js-multiplatform.gradle @@ -46,7 +46,7 @@ compileJsLegacy.configure { kotlinOptions { // drop -js suffix from outputFile def baseName = project.name - "-js" - outputFile = new File(outputFile.parent, baseName + ".js") + outputFile = new File(outputFileProperty.get().parent, baseName + ".js") } } diff --git a/gradle/compile-jvm-multiplatform.gradle b/gradle/compile-jvm-multiplatform.gradle index 1f861f800c..5e65042746 100644 --- a/gradle/compile-jvm-multiplatform.gradle +++ b/gradle/compile-jvm-multiplatform.gradle @@ -6,13 +6,7 @@ sourceCompatibility = 1.6 targetCompatibility = 1.6 kotlin { - jvm { - if (rootProject.ext.jvm_ir_enabled) { - compilations.all { - kotlinOptions.useIR = true - } - } - } + jvm {} sourceSets { jvmTest.dependencies { api "org.jetbrains.kotlin:kotlin-test:$kotlin_version" diff --git a/gradle/dokka.gradle b/gradle/dokka.gradle deleted file mode 100644 index f0cad15442..0000000000 --- a/gradle/dokka.gradle +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -// Configures generation of JavaDoc & Dokka artifacts - -def makeLinkMapping(dokka, projectDir) { - dokka.linkMapping { - def relPath = rootProject.projectDir.toPath().relativize(projectDir.toPath()) - dir = "$projectDir/src" - url = "https://github.com/kotlin/kotlinx.coroutines/tree/master/$relPath/src" - suffix = "#L" - } -} - -configurations { - dokkaStubs.extendsFrom compileOnly - configureKotlinJvmPlatform(dokkaStubs) -} - -apply plugin: 'org.jetbrains.dokka' - -tasks.withType(dokka.getClass()) { - jdkVersion = 8 - includes = ['README.md'] -} - -dependencies { - dokkaStubs project(":stdlib-stubs") -} - - -dokka { - kotlinTasks { [] } - outputFormat = 'kotlin-website' - dependsOn(project.configurations.dokkaStubs) - - noStdlibLink = true - - externalDocumentationLink { - packageListUrl = rootProject.projectDir.toPath().resolve("site/stdlib.package.list").toUri().toURL() - url = new URL("https://kotlinlang.org/api/latest/jvm/stdlib/") - } - - if (project.name != "kotlinx-coroutines-core") { - dependsOn(project.configurations.compileClasspath) - dependsOn(project.sourceSets.main.output) - doFirst { - // resolve classpath only during execution - classpath = project.configurations.dokkaStubs.files + project.configurations.compileClasspath.files + project.sourceSets.main.output.files - } - } -} - -if (project.name == "kotlinx-coroutines-core") { - // Custom configuration for MPP modules - dependencies { - dokkaStubs project(":js-stub") // so that JS library reference can resolve properly - dokkaStubs project(":kotlinx-coroutines-core") - } - - dokka { - kotlinTasks { [] } - suppressedModifiers = ['actual'] - makeLinkMapping(it, projectDir) - makeLinkMapping(it, project.file("js")) - makeLinkMapping(it, project.file("jvm")) - makeLinkMapping(it, project.file("native")) - makeLinkMapping(it, project.file("common")) - // source roots - impliedPlatforms = ['JVM', 'JS', 'Native'] - sourceRoot { - path = rootProject.file("$project.name/common/src") - } - sourceRoot { - path = rootProject.file("$project.name/jvm/src") - platforms = ['JVM'] - } - sourceRoot { - path = rootProject.file("$project.name/js/src") - platforms = ['JS'] - } - sourceRoot { - path = rootProject.file("$project.name/native/src") - platforms = ['Native'] - } - doFirst { - classpath = project.configurations.dokkaStubs.files + - project.configurations.jvmCompileClasspath.files + - project.kotlin.targets.jvm.compilations.main.output.allOutputs - } - } -} diff --git a/gradle/dokka.gradle.kts b/gradle/dokka.gradle.kts new file mode 100644 index 0000000000..659890a30b --- /dev/null +++ b/gradle/dokka.gradle.kts @@ -0,0 +1,75 @@ +import org.jetbrains.dokka.gradle.* +import java.net.* + +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// Configures generation of JavaDoc & Dokka artifacts +apply() +//apply() + +fun GradleDokkaSourceSetBuilder.makeLinkMapping(projectDir: File) { + sourceLink { + val relPath = rootProject.projectDir.toPath().relativize(projectDir.toPath()) + localDirectory.set(projectDir.resolve("src")) + remoteUrl.set(URL("https://github.com/kotlin/kotlinx.coroutines/tree/master/$relPath/src")) + remoteLineSuffix.set("#L") + } +} + +val knit_version: String by project +tasks.withType(DokkaTaskPartial::class).configureEach { + dependencies { + plugins("org.jetbrains.kotlinx:dokka-pathsaver-plugin:$knit_version") + } +} + +tasks.withType(DokkaTaskPartial::class).configureEach { + suppressInheritedMembers.set(true) + dokkaSourceSets.configureEach { + jdkVersion.set(11) + includes.from("README.md") + noStdlibLink.set(true) + + externalDocumentationLink { + url.set(URL("https://kotlinlang.org/api/latest/jvm/stdlib/")) + packageListUrl.set(rootProject.projectDir.toPath().resolve("site/stdlib.package.list").toUri().toURL()) + } + + if (project.name != "kotlinx-coroutines-core") { + dependsOn(project.configurations["compileClasspath"]) + doFirst { + // resolve classpath only during execution + classpath.from(project.configurations["compileClasspath"].files)// + project.sourceSets.main.output.files) + } + } + } +} + +if (project.name == "kotlinx-coroutines-core") { + // Custom configuration for MPP modules + tasks.withType(DokkaTaskPartial::class).configureEach { + dokkaSourceSets { + val commonMain by getting { + makeLinkMapping(project.file("common")) + } + + val nativeMain by getting { + makeLinkMapping(project.file("native")) + } + + val jsMain by getting { + makeLinkMapping(project.file("js")) + } + + val jvmMain by getting { + makeLinkMapping(project.file("jvm")) + } + + configureEach { + classpath.from(project.configurations["jvmCompileClasspath"].files) + } + } + } +} diff --git a/gradle/experimental.gradle b/gradle/opt-in.gradle similarity index 84% rename from gradle/experimental.gradle rename to gradle/opt-in.gradle index 11aeb6d8a6..bcf6bebe94 100644 --- a/gradle/experimental.gradle +++ b/gradle/opt-in.gradle @@ -2,11 +2,10 @@ * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// For new mpp -ext.experimentalAnnotations = [ - "kotlin.Experimental", +ext.optInAnnotations = [ "kotlin.experimental.ExperimentalTypeInference", "kotlin.ExperimentalMultiplatform", + "kotlinx.coroutines.DelicateCoroutinesApi", "kotlinx.coroutines.ExperimentalCoroutinesApi", "kotlinx.coroutines.ObsoleteCoroutinesApi", "kotlinx.coroutines.InternalCoroutinesApi", diff --git a/integration/kotlinx-coroutines-guava/README.md b/integration/kotlinx-coroutines-guava/README.md index 130cf0a058..34b8e5818f 100644 --- a/integration/kotlinx-coroutines-guava/README.md +++ b/integration/kotlinx-coroutines-guava/README.md @@ -56,9 +56,12 @@ Integration with Guava [ListenableFuture](https://github.com/google/guava/wiki/L -[future]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-guava/kotlinx.coroutines.guava/kotlinx.coroutines.-coroutine-scope/future.html -[com.google.common.util.concurrent.ListenableFuture]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-guava/kotlinx.coroutines.guava/com.google.common.util.concurrent.-listenable-future/index.html -[com.google.common.util.concurrent.ListenableFuture.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-guava/kotlinx.coroutines.guava/com.google.common.util.concurrent.-listenable-future/await.html -[kotlinx.coroutines.Deferred.asListenableFuture]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-guava/kotlinx.coroutines.guava/kotlinx.coroutines.-deferred/as-listenable-future.html +[future]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-guava/kotlinx.coroutines.guava/future.html +[com.google.common.util.concurrent.ListenableFuture.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-guava/kotlinx.coroutines.guava/await.html +[kotlinx.coroutines.Deferred.asListenableFuture]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-guava/kotlinx.coroutines.guava/as-listenable-future.html + + + +[com.google.common.util.concurrent.ListenableFuture]: https://kotlin.github.io/kotlinx.coroutines/https://google.github.io/guava/releases/28.0-jre/api/docs/com/google/common/util/concurrent/ListenableFuture.html diff --git a/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt b/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt index 35e0aeb379..8f11e0a916 100644 --- a/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt +++ b/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt @@ -136,11 +136,13 @@ public fun ListenableFuture.asDeferred(): Deferred { override fun onSuccess(result: T?) { // Here we work with flexible types, so we unchecked cast to trick the type system @Suppress("UNCHECKED_CAST") - deferred.complete(result as T) + runCatching { deferred.complete(result as T) } + .onFailure { handleCoroutineException(EmptyCoroutineContext, it) } } override fun onFailure(t: Throwable) { - deferred.completeExceptionally(t) + runCatching { deferred.completeExceptionally(t) } + .onFailure { handleCoroutineException(EmptyCoroutineContext, it) } } }, MoreExecutors.directExecutor()) diff --git a/integration/kotlinx-coroutines-guava/test/FutureAsDeferredUnhandledCompletionExceptionTest.kt b/integration/kotlinx-coroutines-guava/test/FutureAsDeferredUnhandledCompletionExceptionTest.kt new file mode 100644 index 0000000000..d6469a947e --- /dev/null +++ b/integration/kotlinx-coroutines-guava/test/FutureAsDeferredUnhandledCompletionExceptionTest.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.guava + +import com.google.common.util.concurrent.* +import kotlinx.coroutines.* +import org.junit.* +import org.junit.Test +import kotlin.test.* + +class FutureAsDeferredUnhandledCompletionExceptionTest : TestBase() { + + // This is a separate test in order to avoid interference with uncaught exception handlers in other tests + private val exceptionHandler = Thread.getDefaultUncaughtExceptionHandler() + private lateinit var caughtException: Throwable + + @Before + fun setUp() { + Thread.setDefaultUncaughtExceptionHandler { _, e -> caughtException = e } + } + + @After + fun tearDown() { + Thread.setDefaultUncaughtExceptionHandler(exceptionHandler) + } + + @Test + fun testLostExceptionOnSuccess() = runTest { + val future = SettableFuture.create() + val deferred = future.asDeferred() + deferred.invokeOnCompletion { throw TestException() } + future.set(1) + assertTrue { caughtException is CompletionHandlerException && caughtException.cause is TestException } + } + + @Test + fun testLostExceptionOnFailure() = runTest { + val future = SettableFuture.create() + val deferred = future.asDeferred() + deferred.invokeOnCompletion { throw TestException() } + future.setException(TestException2()) + assertTrue { caughtException is CompletionHandlerException && caughtException.cause is TestException } + } +} diff --git a/integration/kotlinx-coroutines-guava/test/ListenableFutureTest.kt b/integration/kotlinx-coroutines-guava/test/ListenableFutureTest.kt index c463174a8d..69ba193071 100644 --- a/integration/kotlinx-coroutines-guava/test/ListenableFutureTest.kt +++ b/integration/kotlinx-coroutines-guava/test/ListenableFutureTest.kt @@ -11,6 +11,7 @@ import org.junit.Ignore import org.junit.Test import java.util.concurrent.* import java.util.concurrent.CancellationException +import java.util.concurrent.atomic.* import kotlin.test.* class ListenableFutureTest : TestBase() { @@ -755,4 +756,23 @@ class ListenableFutureTest : TestBase() { future(start = CoroutineStart.ATOMIC) { } future(start = CoroutineStart.UNDISPATCHED) { } } + + @Test + fun testStackOverflow() = runTest { + val future = SettableFuture.create() + val completed = AtomicLong() + val count = 10000L + val children = ArrayList() + for (i in 0 until count) { + children += launch(Dispatchers.Default) { + future.asDeferred().await() + completed.incrementAndGet() + } + } + future.set(1) + withTimeout(60_000) { + children.forEach { it.join() } + assertEquals(count, completed.get()) + } + } } diff --git a/integration/kotlinx-coroutines-jdk8/README.md b/integration/kotlinx-coroutines-jdk8/README.md index aebd90f06a..35808c6f3d 100644 --- a/integration/kotlinx-coroutines-jdk8/README.md +++ b/integration/kotlinx-coroutines-jdk8/README.md @@ -60,9 +60,9 @@ Integration with JDK8 [CompletableFuture] (Android API level 24). -[future]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-jdk8/kotlinx.coroutines.future/kotlinx.coroutines.-coroutine-scope/future.html -[java.util.concurrent.CompletionStage.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-jdk8/kotlinx.coroutines.future/java.util.concurrent.-completion-stage/await.html -[java.util.concurrent.CompletionStage.asDeferred]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-jdk8/kotlinx.coroutines.future/java.util.concurrent.-completion-stage/as-deferred.html -[kotlinx.coroutines.Deferred.asCompletableFuture]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-jdk8/kotlinx.coroutines.future/kotlinx.coroutines.-deferred/as-completable-future.html +[future]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-jdk8/kotlinx.coroutines.future/future.html +[java.util.concurrent.CompletionStage.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-jdk8/kotlinx.coroutines.future/await.html +[java.util.concurrent.CompletionStage.asDeferred]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-jdk8/kotlinx.coroutines.future/as-deferred.html +[kotlinx.coroutines.Deferred.asCompletableFuture]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-jdk8/kotlinx.coroutines.future/as-completable-future.html diff --git a/integration/kotlinx-coroutines-jdk8/src/future/Future.kt b/integration/kotlinx-coroutines-jdk8/src/future/Future.kt index 7e9c349c66..caf2a3c359 100644 --- a/integration/kotlinx-coroutines-jdk8/src/future/Future.kt +++ b/integration/kotlinx-coroutines-jdk8/src/future/Future.kt @@ -126,13 +126,18 @@ public fun CompletionStage.asDeferred(): Deferred { } val result = CompletableDeferred() whenComplete { value, exception -> - if (exception == null) { - // the future has completed normally - result.complete(value) - } else { - // the future has completed with an exception, unwrap it consistently with fast path - // Note: In the fast-path the implementation of CompletableFuture.get() does unwrapping - result.completeExceptionally((exception as? CompletionException)?.cause ?: exception) + try { + if (exception == null) { + // the future has completed normally + result.complete(value) + } else { + // the future has completed with an exception, unwrap it consistently with fast path + // Note: In the fast-path the implementation of CompletableFuture.get() does unwrapping + result.completeExceptionally((exception as? CompletionException)?.cause ?: exception) + } + } catch (e: Throwable) { + // We come here iff the internals of Deferred threw an exception during its completion + handleCoroutineException(EmptyCoroutineContext, e) } } result.cancelFutureOnCompletion(future) diff --git a/integration/kotlinx-coroutines-jdk8/test/future/FutureAsDeferredUnhandledCompletionExceptionTest.kt b/integration/kotlinx-coroutines-jdk8/test/future/FutureAsDeferredUnhandledCompletionExceptionTest.kt new file mode 100644 index 0000000000..bf810af7aa --- /dev/null +++ b/integration/kotlinx-coroutines-jdk8/test/future/FutureAsDeferredUnhandledCompletionExceptionTest.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package future + +import kotlinx.coroutines.* +import kotlinx.coroutines.future.* +import org.junit.* +import org.junit.Test +import java.util.concurrent.* +import kotlin.test.* + +class FutureAsDeferredUnhandledCompletionExceptionTest : TestBase() { + + // This is a separate test in order to avoid interference with uncaught exception handlers in other tests + private val exceptionHandler = Thread.getDefaultUncaughtExceptionHandler() + private lateinit var caughtException: Throwable + + @Before + fun setUp() { + Thread.setDefaultUncaughtExceptionHandler { _, e -> caughtException = e } + } + + @After + fun tearDown() { + Thread.setDefaultUncaughtExceptionHandler(exceptionHandler) + } + + @Test + fun testLostException() = runTest { + val future = CompletableFuture() + val deferred = future.asDeferred() + deferred.invokeOnCompletion { throw TestException() } + future.complete(1) + assertTrue { caughtException is CompletionHandlerException && caughtException.cause is TestException } + } +} diff --git a/integration/kotlinx-coroutines-jdk8/test/future/FutureTest.kt b/integration/kotlinx-coroutines-jdk8/test/future/FutureTest.kt index 08e5cdad93..372e79ef1d 100644 --- a/integration/kotlinx-coroutines-jdk8/test/future/FutureTest.kt +++ b/integration/kotlinx-coroutines-jdk8/test/future/FutureTest.kt @@ -575,4 +575,23 @@ class FutureTest : TestBase() { future(start = CoroutineStart.ATOMIC) { } future(start = CoroutineStart.UNDISPATCHED) { } } + + @Test + fun testStackOverflow() = runTest { + val future = CompletableFuture() + val completed = AtomicLong() + val count = 10000L + val children = ArrayList() + for (i in 0 until count) { + children += launch(Dispatchers.Default) { + future.asDeferred().await() + completed.incrementAndGet() + } + } + future.complete(1) + withTimeout(60_000) { + children.forEach { it.join() } + assertEquals(count, completed.get()) + } + } } diff --git a/integration/kotlinx-coroutines-play-services/README.md b/integration/kotlinx-coroutines-play-services/README.md index 4ee6bf427c..e5e0e613b3 100644 --- a/integration/kotlinx-coroutines-play-services/README.md +++ b/integration/kotlinx-coroutines-play-services/README.md @@ -6,6 +6,7 @@ Extension functions: | **Name** | **Description** | -------- | --------------- +| [Task.asDeferred][asDeferred] | Converts a Task into a Deferred | [Task.await][await] | Awaits for completion of the Task (cancellable) | [Deferred.asTask][asTask] | Converts a deferred value to a Task @@ -25,5 +26,14 @@ val snapshot = try { // Do stuff ``` +If the `Task` supports cancellation via passing a `CancellationToken`, pass the corresponding `CancellationTokenSource` to `asDeferred` or `await` to support bi-directional cancellation: + +```kotlin +val cancellationTokenSource = CancellationTokenSource() +val currentLocationTask = fusedLocationProviderClient.getCurrentLocation(PRIORITY_HIGH_ACCURACY, cancellationTokenSource.token) +val currentLocation = currentLocationTask.await(cancellationTokenSource) // cancelling `await` also cancels `currentLocationTask`, and vice versa +``` + +[asDeferred]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-play-services/kotlinx.coroutines.tasks/com.google.android.gms.tasks.-task/as-deferred.html [await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-play-services/kotlinx.coroutines.tasks/com.google.android.gms.tasks.-task/await.html [asTask]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-play-services/kotlinx.coroutines.tasks/kotlinx.coroutines.-deferred/as-task.html diff --git a/integration/kotlinx-coroutines-play-services/api/kotlinx-coroutines-play-services.api b/integration/kotlinx-coroutines-play-services/api/kotlinx-coroutines-play-services.api index 9b2c4dd388..cc23e8db2e 100644 --- a/integration/kotlinx-coroutines-play-services/api/kotlinx-coroutines-play-services.api +++ b/integration/kotlinx-coroutines-play-services/api/kotlinx-coroutines-play-services.api @@ -1,6 +1,8 @@ public final class kotlinx/coroutines/tasks/TasksKt { public static final fun asDeferred (Lcom/google/android/gms/tasks/Task;)Lkotlinx/coroutines/Deferred; + public static final fun asDeferred (Lcom/google/android/gms/tasks/Task;Lcom/google/android/gms/tasks/CancellationTokenSource;)Lkotlinx/coroutines/Deferred; public static final fun asTask (Lkotlinx/coroutines/Deferred;)Lcom/google/android/gms/tasks/Task; + public static final fun await (Lcom/google/android/gms/tasks/Task;Lcom/google/android/gms/tasks/CancellationTokenSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun await (Lcom/google/android/gms/tasks/Task;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } diff --git a/integration/kotlinx-coroutines-play-services/src/Tasks.kt b/integration/kotlinx-coroutines-play-services/src/Tasks.kt index d89d1aec7c..c37ac7a02d 100644 --- a/integration/kotlinx-coroutines-play-services/src/Tasks.kt +++ b/integration/kotlinx-coroutines-play-services/src/Tasks.kt @@ -6,15 +6,8 @@ package kotlinx.coroutines.tasks -import com.google.android.gms.tasks.CancellationTokenSource -import com.google.android.gms.tasks.RuntimeExecutionException -import com.google.android.gms.tasks.Task -import com.google.android.gms.tasks.TaskCompletionSource -import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.CompletableDeferred -import kotlinx.coroutines.Deferred -import kotlinx.coroutines.Job -import kotlinx.coroutines.suspendCancellableCoroutine +import com.google.android.gms.tasks.* +import kotlinx.coroutines.* import kotlin.coroutines.* /** @@ -45,39 +38,85 @@ public fun Deferred.asTask(): Task { /** * Converts this task to an instance of [Deferred]. * If task is cancelled then resulting deferred will be cancelled as well. + * However, the opposite is not true: if the deferred is cancelled, the [Task] will not be cancelled. + * For bi-directional cancellation, an overload that accepts [CancellationTokenSource] can be used. */ -public fun Task.asDeferred(): Deferred { +public fun Task.asDeferred(): Deferred = asDeferredImpl(null) + +/** + * Converts this task to an instance of [Deferred] with a [CancellationTokenSource] to control cancellation. + * The cancellation of this function is bi-directional: + * * If the given task is cancelled, the resulting deferred will be cancelled. + * * If the resulting deferred is cancelled, the provided [cancellationTokenSource] will be cancelled. + * + * Providing a [CancellationTokenSource] that is unrelated to the receiving [Task] is not supported and + * leads to an unspecified behaviour. + */ +@ExperimentalCoroutinesApi // Since 1.5.1, tentatively until 1.6.0 +public fun Task.asDeferred(cancellationTokenSource: CancellationTokenSource): Deferred = + asDeferredImpl(cancellationTokenSource) + +private fun Task.asDeferredImpl(cancellationTokenSource: CancellationTokenSource?): Deferred { + val deferred = CompletableDeferred() if (isComplete) { val e = exception - return if (e == null) { - @Suppress("UNCHECKED_CAST") - CompletableDeferred().apply { if (isCanceled) cancel() else complete(result as T) } + if (e == null) { + if (isCanceled) { + deferred.cancel() + } else { + @Suppress("UNCHECKED_CAST") + deferred.complete(result as T) + } } else { - CompletableDeferred().apply { completeExceptionally(e) } + deferred.completeExceptionally(e) + } + } else { + addOnCompleteListener { + val e = it.exception + if (e == null) { + @Suppress("UNCHECKED_CAST") + if (it.isCanceled) deferred.cancel() else deferred.complete(it.result as T) + } else { + deferred.completeExceptionally(e) + } } } - val result = CompletableDeferred() - addOnCompleteListener { - val e = it.exception - if (e == null) { - @Suppress("UNCHECKED_CAST") - if (isCanceled) result.cancel() else result.complete(it.result as T) - } else { - result.completeExceptionally(e) + if (cancellationTokenSource != null) { + deferred.invokeOnCompletion { + cancellationTokenSource.cancel() } } - return result + // Prevent casting to CompletableDeferred and manual completion. + return object : Deferred by deferred {} } /** - * Awaits for completion of the task without blocking a thread. + * Awaits the completion of the task without blocking a thread. * * This suspending function is cancellable. * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function * stops waiting for the completion stage and immediately resumes with [CancellationException]. + * + * For bi-directional cancellation, an overload that accepts [CancellationTokenSource] can be used. + */ +public suspend fun Task.await(): T = awaitImpl(null) + +/** + * Awaits the completion of the task that is linked to the given [CancellationTokenSource] to control cancellation. + * + * This suspending function is cancellable and cancellation is bi-directional: + * * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function + * cancels the [cancellationTokenSource] and throws a [CancellationException]. + * * If the task is cancelled, then this function will throw a [CancellationException]. + * + * Providing a [CancellationTokenSource] that is unrelated to the receiving [Task] is not supported and + * leads to an unspecified behaviour. */ -public suspend fun Task.await(): T { +@ExperimentalCoroutinesApi // Since 1.5.1, tentatively until 1.6.0 +public suspend fun Task.await(cancellationTokenSource: CancellationTokenSource): T = awaitImpl(cancellationTokenSource) + +private suspend fun Task.awaitImpl(cancellationTokenSource: CancellationTokenSource?): T { // fast path if (isComplete) { val e = exception @@ -95,13 +134,19 @@ public suspend fun Task.await(): T { return suspendCancellableCoroutine { cont -> addOnCompleteListener { - val e = exception + val e = it.exception if (e == null) { @Suppress("UNCHECKED_CAST") - if (isCanceled) cont.cancel() else cont.resume(result as T) + if (it.isCanceled) cont.cancel() else cont.resume(it.result as T) } else { cont.resumeWithException(e) } } + + if (cancellationTokenSource != null) { + cont.invokeOnCancellation { + cancellationTokenSource.cancel() + } + } } } diff --git a/integration/kotlinx-coroutines-play-services/test/TaskTest.kt b/integration/kotlinx-coroutines-play-services/test/TaskTest.kt index 0f125ac98c..b125192e93 100644 --- a/integration/kotlinx-coroutines-play-services/test/TaskTest.kt +++ b/integration/kotlinx-coroutines-play-services/test/TaskTest.kt @@ -149,5 +149,270 @@ class TaskTest : TestBase() { } } + @Test + fun testCancellableTaskAsDeferred() = runTest { + val cancellationTokenSource = CancellationTokenSource() + val deferred = Tasks.forResult(42).asDeferred(cancellationTokenSource) + assertEquals(42, deferred.await()) + assertTrue(cancellationTokenSource.token.isCancellationRequested) + } + + @Test + fun testNullResultCancellableTaskAsDeferred() = runTest { + val cancellationTokenSource = CancellationTokenSource() + assertNull(Tasks.forResult(null).asDeferred(cancellationTokenSource).await()) + assertTrue(cancellationTokenSource.token.isCancellationRequested) + } + + @Test + fun testCancelledCancellableTaskAsDeferred() = runTest { + val cancellationTokenSource = CancellationTokenSource() + val deferred = Tasks.forCanceled().asDeferred(cancellationTokenSource) + + assertTrue(deferred.isCancelled) + try { + deferred.await() + fail("deferred.await() should be cancelled") + } catch (e: Exception) { + assertTrue(e is CancellationException) + } + assertTrue(cancellationTokenSource.token.isCancellationRequested) + } + + @Test + fun testCancellingCancellableTaskAsDeferred() = runTest { + val cancellationTokenSource = CancellationTokenSource() + val task = TaskCompletionSource(cancellationTokenSource.token).task + val deferred = task.asDeferred(cancellationTokenSource) + + deferred.cancel() + try { + deferred.await() + fail("deferred.await() should be cancelled") + } catch (e: Exception) { + assertTrue(e is CancellationException) + } + assertTrue(cancellationTokenSource.token.isCancellationRequested) + } + + @Test + fun testExternallyCancelledCancellableTaskAsDeferred() = runTest { + val cancellationTokenSource = CancellationTokenSource() + val task = TaskCompletionSource(cancellationTokenSource.token).task + val deferred = task.asDeferred(cancellationTokenSource) + + cancellationTokenSource.cancel() + + try { + deferred.await() + fail("deferred.await() should be cancelled") + } catch (e: Exception) { + assertTrue(e is CancellationException) + } + assertTrue(cancellationTokenSource.token.isCancellationRequested) + } + + @Test + fun testSeparatelyCancelledCancellableTaskAsDeferred() = runTest { + val cancellationTokenSource = CancellationTokenSource() + val task = TaskCompletionSource().task + task.asDeferred(cancellationTokenSource) + + cancellationTokenSource.cancel() + + assertTrue(cancellationTokenSource.token.isCancellationRequested) + } + + @Test + fun testFailedCancellableTaskAsDeferred() = runTest { + val cancellationTokenSource = CancellationTokenSource() + val deferred = Tasks.forException(TestException("something went wrong")).asDeferred(cancellationTokenSource) + + assertTrue(deferred.isCancelled && deferred.isCompleted) + val completionException = deferred.getCompletionExceptionOrNull()!! + assertTrue(completionException is TestException) + assertEquals("something went wrong", completionException.message) + + try { + deferred.await() + fail("deferred.await() should throw an exception") + } catch (e: Exception) { + assertTrue(e is TestException) + assertEquals("something went wrong", e.message) + } + assertTrue(cancellationTokenSource.token.isCancellationRequested) + } + + @Test + fun testFailingCancellableTaskAsDeferred() = runTest { + val cancellationTokenSource = CancellationTokenSource() + val lock = ReentrantLock().apply { lock() } + + val deferred: Deferred = Tasks.call { + lock.withLock { throw TestException("something went wrong") } + }.asDeferred(cancellationTokenSource) + + assertFalse(deferred.isCompleted) + lock.unlock() + + try { + deferred.await() + fail("deferred.await() should throw an exception") + } catch (e: Exception) { + assertTrue(e is TestException) + assertEquals("something went wrong", e.message) + assertSame(e.cause, deferred.getCompletionExceptionOrNull()) // debug mode stack augmentation + } + assertTrue(cancellationTokenSource.token.isCancellationRequested) + } + + @Test + fun testFastPathCompletedTaskWithCancelledTokenSourceAsDeferred() = runTest { + val cancellationTokenSource = CancellationTokenSource() + val deferred = Tasks.forResult(42).asDeferred(cancellationTokenSource) + cancellationTokenSource.cancel() + assertEquals(42, deferred.await()) + } + + @Test + fun testAwaitCancellableTask() = runTest { + val cancellationTokenSource = CancellationTokenSource() + val taskCompletionSource = TaskCompletionSource(cancellationTokenSource.token) + + val deferred: Deferred = async(start = CoroutineStart.UNDISPATCHED) { + taskCompletionSource.task.await(cancellationTokenSource) + } + + assertFalse(deferred.isCompleted) + taskCompletionSource.setResult(42) + + assertEquals(42, deferred.await()) + assertTrue(deferred.isCompleted) + } + + @Test + fun testFailedAwaitTask() = runTest(expected = { it is TestException }) { + val cancellationTokenSource = CancellationTokenSource() + val taskCompletionSource = TaskCompletionSource(cancellationTokenSource.token) + + val deferred: Deferred = async(start = CoroutineStart.UNDISPATCHED) { + taskCompletionSource.task.await(cancellationTokenSource) + } + + assertFalse(deferred.isCompleted) + taskCompletionSource.setException(TestException("something went wrong")) + + deferred.await() + } + + @Test + fun testCancelledAwaitCancellableTask() = runTest { + val cancellationTokenSource = CancellationTokenSource() + val taskCompletionSource = TaskCompletionSource(cancellationTokenSource.token) + + val deferred: Deferred = async(start = CoroutineStart.UNDISPATCHED) { + taskCompletionSource.task.await(cancellationTokenSource) + } + + assertFalse(deferred.isCompleted) + // Cancel the deferred + deferred.cancel() + + try { + deferred.await() + fail("deferred.await() should be cancelled") + } catch (e: Exception) { + assertTrue(e is CancellationException) + } + + assertTrue(cancellationTokenSource.token.isCancellationRequested) + } + + @Test + fun testExternallyCancelledAwaitCancellableTask() = runTest { + val cancellationTokenSource = CancellationTokenSource() + val taskCompletionSource = TaskCompletionSource(cancellationTokenSource.token) + + val deferred: Deferred = async(start = CoroutineStart.UNDISPATCHED) { + taskCompletionSource.task.await(cancellationTokenSource) + } + + assertFalse(deferred.isCompleted) + // Cancel the cancellation token source + cancellationTokenSource.cancel() + + try { + deferred.await() + fail("deferred.await() should be cancelled") + } catch (e: Exception) { + assertTrue(e is CancellationException) + } + + assertTrue(cancellationTokenSource.token.isCancellationRequested) + } + + @Test + fun testFastPathCancellationTokenSourceCancelledAwaitCancellableTask() = runTest { + val cancellationTokenSource = CancellationTokenSource() + // Construct a task without the cancellation token source + val taskCompletionSource = TaskCompletionSource() + + val deferred: Deferred = async(start = CoroutineStart.LAZY) { + taskCompletionSource.task.await(cancellationTokenSource) + } + + assertFalse(deferred.isCompleted) + cancellationTokenSource.cancel() + + // Cancelling the token doesn't cancel the deferred + assertTrue(cancellationTokenSource.token.isCancellationRequested) + assertFalse(deferred.isCompleted) + + // Cleanup + deferred.cancel() + } + + @Test + fun testSlowPathCancellationTokenSourceCancelledAwaitCancellableTask() = runTest { + val cancellationTokenSource = CancellationTokenSource() + // Construct a task without the cancellation token source + val taskCompletionSource = TaskCompletionSource() + + val deferred: Deferred = async(start = CoroutineStart.UNDISPATCHED) { + taskCompletionSource.task.await(cancellationTokenSource) + } + + assertFalse(deferred.isCompleted) + cancellationTokenSource.cancel() + + // Cancelling the token doesn't cancel the deferred + assertTrue(cancellationTokenSource.token.isCancellationRequested) + assertFalse(deferred.isCompleted) + + // Cleanup + deferred.cancel() + } + + @Test + fun testFastPathWithCompletedTaskAndCanceledTokenSourceAwaitTask() = runTest { + val firstCancellationTokenSource = CancellationTokenSource() + val secondCancellationTokenSource = CancellationTokenSource() + // Construct a task with a different cancellation token source + val taskCompletionSource = TaskCompletionSource(firstCancellationTokenSource.token) + + val deferred: Deferred = async(start = CoroutineStart.LAZY) { + taskCompletionSource.task.await(secondCancellationTokenSource) + } + + assertFalse(deferred.isCompleted) + secondCancellationTokenSource.cancel() + + assertFalse(deferred.isCompleted) + taskCompletionSource.setResult(42) + + assertEquals(42, deferred.await()) + assertTrue(deferred.isCompleted) + } + class TestException(message: String) : Exception(message) } diff --git a/js/js-stub/README.md b/js/js-stub/README.md deleted file mode 100644 index 46d18670f2..0000000000 --- a/js/js-stub/README.md +++ /dev/null @@ -1 +0,0 @@ -This is a workaround for Dokka to generate proper references for JS modules. \ No newline at end of file diff --git a/js/js-stub/build.gradle.kts b/js/js-stub/build.gradle.kts deleted file mode 100644 index 201ac43cb0..0000000000 --- a/js/js-stub/build.gradle.kts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - -tasks.named("compileKotlin") { - kotlinOptions { - freeCompilerArgs += "-Xallow-kotlin-package" - } -} diff --git a/js/js-stub/src/Performance.kt b/js/js-stub/src/Performance.kt deleted file mode 100644 index eefb1d749b..0000000000 --- a/js/js-stub/src/Performance.kt +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.w3c.performance - -public abstract class Performance { - public abstract fun now(): Double -} diff --git a/js/js-stub/src/Promise.kt b/js/js-stub/src/Promise.kt deleted file mode 100644 index 243d0c9e33..0000000000 --- a/js/js-stub/src/Promise.kt +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlin.js - -public open class Promise diff --git a/js/js-stub/src/Window.kt b/js/js-stub/src/Window.kt deleted file mode 100644 index 8b2bb8000b..0000000000 --- a/js/js-stub/src/Window.kt +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.w3c.dom - -public abstract class Window diff --git a/kotlinx-coroutines-core/README.md b/kotlinx-coroutines-core/README.md index 9fdf418233..c21e5048f6 100644 --- a/kotlinx-coroutines-core/README.md +++ b/kotlinx-coroutines-core/README.md @@ -4,45 +4,45 @@ Core primitives to work with coroutines. Coroutine builder functions: -| **Name** | **Result** | **Scope** | **Description** -| ------------- | ------------- | ---------------- | --------------- -| [launch] | [Job] | [CoroutineScope] | Launches coroutine that does not have any result -| [async] | [Deferred] | [CoroutineScope] | Returns a single value with the future result -| [produce][kotlinx.coroutines.channels.produce] | [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [ProducerScope][kotlinx.coroutines.channels.ProducerScope] | Produces a stream of elements -| [runBlocking] | `T` | [CoroutineScope] | Blocks the thread while the coroutine runs +| **Name** | **Result** | **Scope** | **Description** +| ---------------------------------------------- | ------------------------------------------------------------ | ---------------------------------------------------------- | --------------- +| [launch][kotlinx.coroutines.launch] | [Job][kotlinx.coroutines.Job] | [CoroutineScope][kotlinx.coroutines.CoroutineScope] | Launches coroutine that does not have any result +| [async][kotlinx.coroutines.async] | [Deferred][kotlinx.coroutines.Deferred] | [CoroutineScope][kotlinx.coroutines.CoroutineScope] | Returns a single value with the future result +| [produce][kotlinx.coroutines.channels.produce] | [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [ProducerScope][kotlinx.coroutines.channels.ProducerScope] | Produces a stream of elements +| [runBlocking][kotlinx.coroutines.runBlocking] | `T` | [CoroutineScope][kotlinx.coroutines.CoroutineScope] | Blocks the thread while the coroutine runs Coroutine dispatchers implementing [CoroutineDispatcher]: -| **Name** | **Description** -| --------------------------- | --------------- -| [Dispatchers.Default] | Confines coroutine execution to a shared pool of background threads -| [Dispatchers.Unconfined] | Does not confine coroutine execution in any way +| **Name** | **Description** +| ------------------------------------------------------------------- | --------------- +| [Dispatchers.Default][kotlinx.coroutines.Dispatchers.Default] | Confines coroutine execution to a shared pool of background threads +| [Dispatchers.Unconfined][kotlinx.coroutines.Dispatchers.Unconfined] | Does not confine coroutine execution in any way More context elements: -| **Name** | **Description** -| --------------------------- | --------------- -| [NonCancellable] | A non-cancelable job that is always active -| [CoroutineExceptionHandler] | Handler for uncaught exception +| **Name** | **Description** +| ------------------------------------------------------------------------- | --------------- +| [NonCancellable][kotlinx.coroutines.NonCancellable] | A non-cancelable job that is always active +| [CoroutineExceptionHandler][kotlinx.coroutines.CoroutineExceptionHandler] | Handler for uncaught exception Synchronization primitives for coroutines: -| **Name** | **Suspending functions** | **Description** -| ---------- | ----------------------------------------------------------- | --------------- -| [Mutex][kotlinx.coroutines.sync.Mutex] | [lock][kotlinx.coroutines.sync.Mutex.lock] | Mutual exclusion +| **Name** | **Suspending functions** | **Description** +| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | --------------- +| [Mutex][kotlinx.coroutines.sync.Mutex] | [lock][kotlinx.coroutines.sync.Mutex.lock] | Mutual exclusion | [Channel][kotlinx.coroutines.channels.Channel] | [send][kotlinx.coroutines.channels.SendChannel.send], [receive][kotlinx.coroutines.channels.ReceiveChannel.receive] | Communication channel (aka queue or exchanger) Top-level suspending functions: -| **Name** | **Description** -| ------------------- | --------------- -| [delay] | Non-blocking sleep -| [yield] | Yields thread in single-threaded dispatchers -| [withContext] | Switches to a different context -| [withTimeout] | Set execution time-limit with exception on timeout -| [withTimeoutOrNull] | Set execution time-limit will null result on timeout -| [awaitAll] | Awaits for successful completion of all given jobs or exceptional completion of any -| [joinAll] | Joins on all given jobs +| **Name** | **Description** +| --------------------------------------------------------- | --------------- +| [delay][kotlinx.coroutines.delay] | Non-blocking sleep +| [yield][kotlinx.coroutines.yield] | Yields thread in single-threaded dispatchers +| [withContext][kotlinx.coroutines.withContext] | Switches to a different context +| [withTimeout][kotlinx.coroutines.withTimeout] | Set execution time-limit with exception on timeout +| [withTimeoutOrNull][kotlinx.coroutines.withTimeoutOrNull] | Set execution time-limit will null result on timeout +| [awaitAll][kotlinx.coroutines.awaitAll] | Awaits for successful completion of all given jobs or exceptional completion of any +| [joinAll][kotlinx.coroutines.joinAll] | Joins on all given jobs Cancellation support for user-defined suspending functions is available with [suspendCancellableCoroutine] helper function. [NonCancellable] job object is provided to suppress cancellation with @@ -50,15 +50,15 @@ helper function. [NonCancellable] job object is provided to suppress cancellatio [Select][kotlinx.coroutines.selects.select] expression waits for the result of multiple suspending functions simultaneously: -| **Receiver** | **Suspending function** | **Select clause** | **Non-suspending version** -| ---------------- | --------------------------------------------- | ------------------------------------------------ | -------------------------- -| [Job] | [join][Job.join] | [onJoin][Job.onJoin] | [isCompleted][Job.isCompleted] -| [Deferred] | [await][Deferred.await] | [onAwait][Deferred.onAwait] | [isCompleted][Job.isCompleted] -| [SendChannel][kotlinx.coroutines.channels.SendChannel] | [send][kotlinx.coroutines.channels.SendChannel.send] | [onSend][kotlinx.coroutines.channels.SendChannel.onSend] | [trySend][kotlinx.coroutines.channels.SendChannel.trySend] -| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receive][kotlinx.coroutines.channels.ReceiveChannel.receive] | [onReceive][kotlinx.coroutines.channels.ReceiveChannel.onReceive] | [tryReceive][kotlinx.coroutines.channels.ReceiveChannel.tryReceive] -| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receiveCatching][kotlinx.coroutines.channels.ReceiveChannel.receiveCatching] | [onReceiveCatching][kotlinx.coroutines.channels.ReceiveChannel.onReceiveCatching] | [tryReceive][kotlinx.coroutines.channels.ReceiveChannel.tryReceive] -| [Mutex][kotlinx.coroutines.sync.Mutex] | [lock][kotlinx.coroutines.sync.Mutex.lock] | [onLock][kotlinx.coroutines.sync.Mutex.onLock] | [tryLock][kotlinx.coroutines.sync.Mutex.tryLock] -| none | [delay] | [onTimeout][kotlinx.coroutines.selects.SelectBuilder.onTimeout] | none +| **Receiver** | **Suspending function** | **Select clause** | **Non-suspending version** +| ------------------------------------------------------------ | --------------------------------------------------------------- | ----------------------------------------------------------------- | -------------------------- +| [Job][kotlinx.coroutines.Job] | [join][kotlinx.coroutines.Job.join] | [onJoin][kotlinx.coroutines.Job.onJoin] | [isCompleted][kotlinx.coroutines.Job.isCompleted] +| [Deferred][kotlinx.coroutines.Deferred] | [await][kotlinx.coroutines.Deferred.await] | [onAwait][kotlinx.coroutines.Deferred.onAwait] | [isCompleted][kotlinx.coroutines.Job.isCompleted] +| [SendChannel][kotlinx.coroutines.channels.SendChannel] | [send][kotlinx.coroutines.channels.SendChannel.send] | [onSend][kotlinx.coroutines.channels.SendChannel.onSend] | [trySend][kotlinx.coroutines.channels.SendChannel.trySend] +| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receive][kotlinx.coroutines.channels.ReceiveChannel.receive] | [onReceive][kotlinx.coroutines.channels.ReceiveChannel.onReceive] | [tryReceive][kotlinx.coroutines.channels.ReceiveChannel.tryReceive] +| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receiveCatching][kotlinx.coroutines.channels.receiveCatching] | [onReceiveCatching][kotlinx.coroutines.channels.onReceiveCatching] | [tryReceive][kotlinx.coroutines.channels.ReceiveChannel.tryReceive] +| [Mutex][kotlinx.coroutines.sync.Mutex] | [lock][kotlinx.coroutines.sync.Mutex.lock] | [onLock][kotlinx.coroutines.sync.Mutex.onLock] | [tryLock][kotlinx.coroutines.sync.Mutex.tryLock] +| none | [delay][kotlinx.coroutines.delay] | [onTimeout][kotlinx.coroutines.selects.SelectBuilder.onTimeout] | none # Package kotlinx.coroutines @@ -91,30 +91,31 @@ Obsolete and deprecated module to test coroutines. Replaced with `kotlinx-corout -[launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html -[Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html -[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html -[async]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html -[Deferred]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/index.html -[runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html +[kotlinx.coroutines.launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html +[kotlinx.coroutines.Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html +[kotlinx.coroutines.CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html +[kotlinx.coroutines.async]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html +[kotlinx.coroutines.Deferred]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/index.html +[kotlinx.coroutines.runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html [CoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-dispatcher/index.html -[Dispatchers.Default]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html -[Dispatchers.Unconfined]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-unconfined.html -[NonCancellable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-non-cancellable.html -[CoroutineExceptionHandler]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-exception-handler/index.html -[delay]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/delay.html -[yield]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/yield.html -[withContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-context.html -[withTimeout]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-timeout.html -[withTimeoutOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-timeout-or-null.html -[awaitAll]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/await-all.html -[joinAll]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/join-all.html +[kotlinx.coroutines.Dispatchers.Default]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html +[kotlinx.coroutines.Dispatchers.Unconfined]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-unconfined.html +[kotlinx.coroutines.NonCancellable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-non-cancellable/index.html +[kotlinx.coroutines.CoroutineExceptionHandler]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-exception-handler/index.html +[kotlinx.coroutines.delay]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/delay.html +[kotlinx.coroutines.yield]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/yield.html +[kotlinx.coroutines.withContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-context.html +[kotlinx.coroutines.withTimeout]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-timeout.html +[kotlinx.coroutines.withTimeoutOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-timeout-or-null.html +[kotlinx.coroutines.awaitAll]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/await-all.html +[kotlinx.coroutines.joinAll]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/join-all.html [suspendCancellableCoroutine]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/suspend-cancellable-coroutine.html -[Job.join]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/join.html -[Job.onJoin]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/on-join.html -[Job.isCompleted]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/is-completed.html -[Deferred.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/await.html -[Deferred.onAwait]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/on-await.html +[NonCancellable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-non-cancellable/index.html +[kotlinx.coroutines.Job.join]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/join.html +[kotlinx.coroutines.Job.onJoin]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/on-join.html +[kotlinx.coroutines.Job.isCompleted]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/is-completed.html +[kotlinx.coroutines.Deferred.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/await.html +[kotlinx.coroutines.Deferred.onAwait]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/on-await.html @@ -136,13 +137,13 @@ Obsolete and deprecated module to test coroutines. Replaced with `kotlinx-corout [kotlinx.coroutines.channels.SendChannel.trySend]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/try-send.html [kotlinx.coroutines.channels.ReceiveChannel.onReceive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive.html [kotlinx.coroutines.channels.ReceiveChannel.tryReceive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/try-receive.html -[kotlinx.coroutines.channels.ReceiveChannel.receiveCatching]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive-catching.html -[kotlinx.coroutines.channels.ReceiveChannel.onReceiveCatching]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive-catching.html +[kotlinx.coroutines.channels.receiveCatching]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive-catching.html +[kotlinx.coroutines.channels.onReceiveCatching]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive-catching.html [kotlinx.coroutines.selects.select]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.selects/select.html -[kotlinx.coroutines.selects.SelectBuilder.onTimeout]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.selects/-select-builder/on-timeout.html +[kotlinx.coroutines.selects.SelectBuilder.onTimeout]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.selects/on-timeout.html diff --git a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api index ec161ce28e..50bfb60d62 100644 --- a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api +++ b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api @@ -158,7 +158,7 @@ public abstract class kotlinx/coroutines/CoroutineDispatcher : kotlin/coroutines public fun isDispatchNeeded (Lkotlin/coroutines/CoroutineContext;)Z public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; public final fun plus (Lkotlinx/coroutines/CoroutineDispatcher;)Lkotlinx/coroutines/CoroutineDispatcher; - public fun releaseInterceptedContinuation (Lkotlin/coroutines/Continuation;)V + public final fun releaseInterceptedContinuation (Lkotlin/coroutines/Continuation;)V public fun toString ()Ljava/lang/String; } @@ -1100,6 +1100,9 @@ public abstract interface class kotlinx/coroutines/flow/StateFlow : kotlinx/coro public final class kotlinx/coroutines/flow/StateFlowKt { public static final fun MutableStateFlow (Ljava/lang/Object;)Lkotlinx/coroutines/flow/MutableStateFlow; + public static final fun getAndUpdate (Lkotlinx/coroutines/flow/MutableStateFlow;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public static final fun update (Lkotlinx/coroutines/flow/MutableStateFlow;Lkotlin/jvm/functions/Function1;)V + public static final fun updateAndGet (Lkotlinx/coroutines/flow/MutableStateFlow;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; } public abstract class kotlinx/coroutines/flow/internal/ChannelFlow : kotlinx/coroutines/flow/internal/FusibleFlow { diff --git a/kotlinx-coroutines-core/build.gradle b/kotlinx-coroutines-core/build.gradle index 4ea81d3de3..c45ca08cef 100644 --- a/kotlinx-coroutines-core/build.gradle +++ b/kotlinx-coroutines-core/build.gradle @@ -3,6 +3,7 @@ */ apply plugin: 'org.jetbrains.kotlin.multiplatform' +apply plugin: 'org.jetbrains.dokka' apply from: rootProject.file("gradle/compile-jvm-multiplatform.gradle") apply from: rootProject.file("gradle/compile-common.gradle") @@ -80,7 +81,7 @@ kotlin { } languageSettings { progressiveMode = true - experimentalAnnotations.each { useExperimentalAnnotation(it) } + optInAnnotations.each { useExperimentalAnnotation(it) } } } @@ -200,12 +201,6 @@ jvmTest { // Configure the IDEA runner for Lincheck configureJvmForLincheck(jvmTest) } - // TODO: JVM IR generates different stacktrace so temporary disable stacktrace tests - if (rootProject.ext.jvm_ir_enabled) { - filter { - excludeTestsMatching('kotlinx.coroutines.exceptions.StackTraceRecovery*') - } - } } // Setup manifest for kotlinx-coroutines-core-jvm.jar diff --git a/kotlinx-coroutines-core/common/README.md b/kotlinx-coroutines-core/common/README.md index e8503d0d16..fcfe334c62 100644 --- a/kotlinx-coroutines-core/common/README.md +++ b/kotlinx-coroutines-core/common/README.md @@ -19,7 +19,7 @@ Coroutine dispatchers implementing [CoroutineDispatcher]: | [Dispatchers.Unconfined] | Does not confine coroutine execution in any way | [newSingleThreadContext] | Creates a single-threaded coroutine context | [newFixedThreadPoolContext] | Creates a thread pool of a fixed size -| [Executor.asCoroutineDispatcher][java.util.concurrent.Executor.asCoroutineDispatcher] | Extension to convert any executor +| [Executor.asCoroutineDispatcher][asCoroutineDispatcher] | Extension to convert any executor More context elements: @@ -57,9 +57,9 @@ helper function. [NonCancellable] job object is provided to suppress cancellatio | ---------------- | --------------------------------------------- | ------------------------------------------------ | -------------------------- | [Job] | [join][Job.join] | [onJoin][Job.onJoin] | [isCompleted][Job.isCompleted] | [Deferred] | [await][Deferred.await] | [onAwait][Deferred.onAwait] | [isCompleted][Job.isCompleted] -| [SendChannel][kotlinx.coroutines.channels.SendChannel] | [send][kotlinx.coroutines.channels.SendChannel.send] | [onSend][kotlinx.coroutines.channels.SendChannel.onSend] | [offer][kotlinx.coroutines.channels.SendChannel.offer] -| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receive][kotlinx.coroutines.channels.ReceiveChannel.receive] | [onReceive][kotlinx.coroutines.channels.ReceiveChannel.onReceive] | [poll][kotlinx.coroutines.channels.ReceiveChannel.poll] -| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receiveCatching][kotlinx.coroutines.channels.ReceiveChannel.receiveCatching] | [onReceiveCatching][kotlinx.coroutines.channels.ReceiveChannel.onReceiveCatching] | [poll][kotlinx.coroutines.channels.ReceiveChannel.poll] +| [SendChannel][kotlinx.coroutines.channels.SendChannel] | [send][kotlinx.coroutines.channels.SendChannel.send] | [onSend][kotlinx.coroutines.channels.SendChannel.onSend] | [trySend][kotlinx.coroutines.channels.SendChannel.trySend] +| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receive][kotlinx.coroutines.channels.ReceiveChannel.receive] | [onReceive][kotlinx.coroutines.channels.ReceiveChannel.onReceive] | [tryReceive][kotlinx.coroutines.channels.ReceiveChannel.tryReceive] +| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receiveCatching][kotlinx.coroutines.channels.ReceiveChannel.receiveCatching] | [onReceiveCatching][kotlinx.coroutines.channels.ReceiveChannel.onReceiveCatching] | [tryReceive][kotlinx.coroutines.channels.ReceiveChannel.tryReceive] | [Mutex][kotlinx.coroutines.sync.Mutex] | [lock][kotlinx.coroutines.sync.Mutex.lock] | [onLock][kotlinx.coroutines.sync.Mutex.onLock] | [tryLock][kotlinx.coroutines.sync.Mutex.tryLock] | none | [delay] | [onTimeout][kotlinx.coroutines.selects.SelectBuilder.onTimeout] | none @@ -108,8 +108,8 @@ Low-level primitives for finer-grained control of coroutines. [Dispatchers.Unconfined]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-unconfined.html [newSingleThreadContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/new-single-thread-context.html [newFixedThreadPoolContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/new-fixed-thread-pool-context.html -[java.util.concurrent.Executor.asCoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/java.util.concurrent.-executor/as-coroutine-dispatcher.html -[NonCancellable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-non-cancellable.html +[asCoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/as-coroutine-dispatcher.html +[NonCancellable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-non-cancellable/index.html [CoroutineExceptionHandler]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-exception-handler/index.html [delay]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/delay.html [yield]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/yield.html @@ -146,16 +146,16 @@ Low-level primitives for finer-grained control of coroutines. [kotlinx.coroutines.channels.SendChannel.send]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/send.html [kotlinx.coroutines.channels.ReceiveChannel.receive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive.html [kotlinx.coroutines.channels.SendChannel.onSend]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/on-send.html -[kotlinx.coroutines.channels.SendChannel.offer]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/offer.html +[kotlinx.coroutines.channels.SendChannel.trySend]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/try-send.html [kotlinx.coroutines.channels.ReceiveChannel.onReceive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive.html -[kotlinx.coroutines.channels.ReceiveChannel.poll]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/poll.html +[kotlinx.coroutines.channels.ReceiveChannel.tryReceive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/try-receive.html [kotlinx.coroutines.channels.ReceiveChannel.receiveCatching]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive-catching.html [kotlinx.coroutines.channels.ReceiveChannel.onReceiveCatching]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive-catching.html [kotlinx.coroutines.selects.select]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.selects/select.html -[kotlinx.coroutines.selects.SelectBuilder.onTimeout]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.selects/-select-builder/on-timeout.html +[kotlinx.coroutines.selects.SelectBuilder.onTimeout]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.selects/on-timeout.html diff --git a/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt b/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt index f74155eb8f..1a0169b65d 100644 --- a/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt +++ b/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt @@ -107,7 +107,7 @@ internal open class CancellableContinuationImpl( } } - private fun isReusable(): Boolean = delegate is DispatchedContinuation<*> && delegate.isReusable(this) + private fun isReusable(): Boolean = resumeMode.isReusableMode && (delegate as DispatchedContinuation<*>).isReusable() /** * Resets cancellability state in order to [suspendCancellableCoroutineReusable] to work. @@ -115,7 +115,7 @@ internal open class CancellableContinuationImpl( */ @JvmName("resetStateReusable") // Prettier stack traces internal fun resetStateReusable(): Boolean { - assert { resumeMode == MODE_CANCELLABLE_REUSABLE } // invalid mode for CancellableContinuationImpl + assert { resumeMode == MODE_CANCELLABLE_REUSABLE } assert { parentHandle !== NonDisposableHandle } val state = _state.value assert { state !is NotCompleted } @@ -164,8 +164,7 @@ internal open class CancellableContinuationImpl( * Attempt to postpone cancellation for reusable cancellable continuation */ private fun cancelLater(cause: Throwable): Boolean { - if (!resumeMode.isReusableMode) return false - // Ensure that we are postponing cancellation to the right instance + // Ensure that we are postponing cancellation to the right reusable instance if (!isReusable()) return false val dispatched = delegate as DispatchedContinuation<*> return dispatched.postponeCancellation(cause) diff --git a/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt b/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt index 10be4a3d9e..d5613d4110 100644 --- a/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt +++ b/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt @@ -99,8 +99,7 @@ public abstract class CoroutineDispatcher : public final override fun interceptContinuation(continuation: Continuation): Continuation = DispatchedContinuation(this, continuation) - @InternalCoroutinesApi - public override fun releaseInterceptedContinuation(continuation: Continuation<*>) { + public final override fun releaseInterceptedContinuation(continuation: Continuation<*>) { /* * Unconditional cast is safe here: we only return DispatchedContinuation from `interceptContinuation`, * any ClassCastException can only indicate compiler bug diff --git a/kotlinx-coroutines-core/common/src/Debug.common.kt b/kotlinx-coroutines-core/common/src/Debug.common.kt index 1381ecd882..185ad295d8 100644 --- a/kotlinx-coroutines-core/common/src/Debug.common.kt +++ b/kotlinx-coroutines-core/common/src/Debug.common.kt @@ -32,10 +32,15 @@ public interface CopyableThrowable where T : Throwable, T : CopyableThrowable /** * Creates a copy of the current instance. + * * For better debuggability, it is recommended to use original exception as [cause][Throwable.cause] of the resulting one. * Stacktrace of copied exception will be overwritten by stacktrace recovery machinery by [Throwable.setStackTrace] call. * An exception can opt-out of copying by returning `null` from this function. * Suppressed exceptions of the original exception should not be copied in order to avoid circular exceptions. + * + * This function is allowed to create a copy with a modified [message][Throwable.message], but it should be noted + * that the copy can be later recovered as well and message modification code should handle this situation correctly + * (e.g. by also storing the original message and checking it) to produce a human-readable result. */ public fun createCopy(): T? } diff --git a/kotlinx-coroutines-core/common/src/Delay.kt b/kotlinx-coroutines-core/common/src/Delay.kt index 53dadf9730..4543c5dda1 100644 --- a/kotlinx-coroutines-core/common/src/Delay.kt +++ b/kotlinx-coroutines-core/common/src/Delay.kt @@ -150,4 +150,4 @@ internal val CoroutineContext.delay: Delay get() = get(ContinuationInterceptor) */ @ExperimentalTime internal fun Duration.toDelayMillis(): Long = - if (this > Duration.ZERO) toLongMilliseconds().coerceAtLeast(1) else 0 + if (this > Duration.ZERO) inWholeMilliseconds.coerceAtLeast(1) else 0 diff --git a/kotlinx-coroutines-core/common/src/NonCancellable.kt b/kotlinx-coroutines-core/common/src/NonCancellable.kt index 5d3644ba91..c278109224 100644 --- a/kotlinx-coroutines-core/common/src/NonCancellable.kt +++ b/kotlinx-coroutines-core/common/src/NonCancellable.kt @@ -24,40 +24,45 @@ import kotlin.coroutines.* * when the parent is cancelled, the whole parent-child relation between parent and child is severed. * The parent will not wait for the child's completion, nor will be cancelled when the child crashed. */ +@Suppress("DeprecatedCallableAddReplaceWith") public object NonCancellable : AbstractCoroutineContextElement(Job), Job { + + private const val message = "NonCancellable can be used only as an argument for 'withContext', direct usages of its API are prohibited" + /** * Always returns `true`. * @suppress **This an internal API and should not be used from general code.** */ - @InternalCoroutinesApi - override val isActive: Boolean get() = true + @Deprecated(level = DeprecationLevel.WARNING, message = message) + override val isActive: Boolean + get() = true /** * Always returns `false`. * @suppress **This an internal API and should not be used from general code.** */ - @InternalCoroutinesApi + @Deprecated(level = DeprecationLevel.WARNING, message = message) override val isCompleted: Boolean get() = false /** * Always returns `false`. * @suppress **This an internal API and should not be used from general code.** */ - @InternalCoroutinesApi + @Deprecated(level = DeprecationLevel.WARNING, message = message) override val isCancelled: Boolean get() = false /** * Always returns `false`. * @suppress **This an internal API and should not be used from general code.** */ - @InternalCoroutinesApi + @Deprecated(level = DeprecationLevel.WARNING, message = message) override fun start(): Boolean = false /** * Always throws [UnsupportedOperationException]. * @suppress **This an internal API and should not be used from general code.** */ - @InternalCoroutinesApi + @Deprecated(level = DeprecationLevel.WARNING, message = message) override suspend fun join() { throw UnsupportedOperationException("This job is always active") } @@ -66,6 +71,7 @@ public object NonCancellable : AbstractCoroutineContextElement(Job), Job { * Always throws [UnsupportedOperationException]. * @suppress **This an internal API and should not be used from general code.** */ + @Deprecated(level = DeprecationLevel.WARNING, message = message) override val onJoin: SelectClause0 get() = throw UnsupportedOperationException("This job is always active") @@ -73,14 +79,13 @@ public object NonCancellable : AbstractCoroutineContextElement(Job), Job { * Always throws [IllegalStateException]. * @suppress **This an internal API and should not be used from general code.** */ - @InternalCoroutinesApi + @Deprecated(level = DeprecationLevel.WARNING, message = message) override fun getCancellationException(): CancellationException = throw IllegalStateException("This job is always active") /** * @suppress **This an internal API and should not be used from general code.** */ - @Suppress("OverridingDeprecatedMember") - @InternalCoroutinesApi + @Deprecated(level = DeprecationLevel.WARNING, message = message) override fun invokeOnCompletion(handler: CompletionHandler): DisposableHandle = NonDisposableHandle @@ -88,7 +93,7 @@ public object NonCancellable : AbstractCoroutineContextElement(Job), Job { * Always returns no-op handle. * @suppress **This an internal API and should not be used from general code.** */ - @InternalCoroutinesApi + @Deprecated(level = DeprecationLevel.WARNING, message = message) override fun invokeOnCompletion(onCancelling: Boolean, invokeImmediately: Boolean, handler: CompletionHandler): DisposableHandle = NonDisposableHandle @@ -96,7 +101,7 @@ public object NonCancellable : AbstractCoroutineContextElement(Job), Job { * Does nothing. * @suppress **This an internal API and should not be used from general code.** */ - @InternalCoroutinesApi + @Deprecated(level = DeprecationLevel.WARNING, message = message) override fun cancel(cause: CancellationException?) {} /** @@ -110,7 +115,7 @@ public object NonCancellable : AbstractCoroutineContextElement(Job), Job { * Always returns [emptySequence]. * @suppress **This an internal API and should not be used from general code.** */ - @InternalCoroutinesApi + @Deprecated(level = DeprecationLevel.WARNING, message = message) override val children: Sequence get() = emptySequence() @@ -118,7 +123,7 @@ public object NonCancellable : AbstractCoroutineContextElement(Job), Job { * Always returns [NonDisposableHandle] and does not do anything. * @suppress **This an internal API and should not be used from general code.** */ - @InternalCoroutinesApi + @Deprecated(level = DeprecationLevel.WARNING, message = message) override fun attachChild(child: ChildJob): ChildHandle = NonDisposableHandle /** @suppress */ diff --git a/kotlinx-coroutines-core/common/src/Supervisor.kt b/kotlinx-coroutines-core/common/src/Supervisor.kt index 01a8e70522..8411c5c65a 100644 --- a/kotlinx-coroutines-core/common/src/Supervisor.kt +++ b/kotlinx-coroutines-core/common/src/Supervisor.kt @@ -42,11 +42,15 @@ public fun SupervisorJob0(parent: Job? = null) : Job = SupervisorJob(parent) * Creates a [CoroutineScope] with [SupervisorJob] and calls the specified suspend block with this scope. * The provided scope inherits its [coroutineContext][CoroutineScope.coroutineContext] from the outer scope, but overrides * context's [Job] with [SupervisorJob]. + * This function returns as soon as the given block and all its child coroutines are completed. * - * A failure of a child does not cause this scope to fail and does not affect its other children, - * so a custom policy for handling failures of its children can be implemented. See [SupervisorJob] for details. - * A failure of the scope itself (exception thrown in the [block] or cancellation) fails the scope with all its children, + * Unlike [coroutineScope], a failure of a child does not cause this scope to fail and does not affect its other children, + * so a custom policy for handling failures of its children can be implemented. See [SupervisorJob] for additional details. + * A failure of the scope itself (exception thrown in the [block] or external cancellation) fails the scope with all its children, * but does not cancel parent job. + * + * The method may throw a [CancellationException] if the current job was cancelled externally, + * or rethrow an exception thrown by the given [block]. */ public suspend fun supervisorScope(block: suspend CoroutineScope.() -> R): R { contract { diff --git a/kotlinx-coroutines-core/common/src/channels/Channel.kt b/kotlinx-coroutines-core/common/src/channels/Channel.kt index 8c69cbfe87..b15c4262ef 100644 --- a/kotlinx-coroutines-core/common/src/channels/Channel.kt +++ b/kotlinx-coroutines-core/common/src/channels/Channel.kt @@ -154,6 +154,8 @@ public interface SendChannel { * ``` * * See https://github.com/Kotlin/kotlinx.coroutines/issues/974 for more context. + * + * @suppress **Deprecated**. */ @Deprecated( level = DeprecationLevel.WARNING, @@ -308,6 +310,8 @@ public interface ReceiveChannel { * The replacement `tryReceive().getOrNull()` is a default that ignores all close exceptions and * proceeds with `null`, while `poll` throws an exception if the channel was closed with an exception. * Replacement with the very same 'poll' semantics is `tryReceive().onClosed { if (it != null) throw it }.getOrNull()` + * + * @suppress **Deprecated**. */ @Deprecated( level = DeprecationLevel.WARNING, @@ -336,6 +340,8 @@ public interface ReceiveChannel { * The replacement `receiveCatching().getOrNull()` is a safe default that ignores all close exceptions and * proceeds with `null`, while `receiveOrNull` throws an exception if the channel was closed with an exception. * Replacement with the very same `receiveOrNull` semantics is `receiveCatching().onClosed { if (it != null) throw it }.getOrNull()`. + * + * @suppress **Deprecated** */ @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") @LowPriorityInOverloadResolution diff --git a/kotlinx-coroutines-core/common/src/channels/ConflatedChannel.kt b/kotlinx-coroutines-core/common/src/channels/ConflatedChannel.kt index e1e2b140f5..f7f60cf97d 100644 --- a/kotlinx-coroutines-core/common/src/channels/ConflatedChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/ConflatedChannel.kt @@ -123,6 +123,7 @@ internal open class ConflatedChannel(onUndeliveredElement: OnUndeliveredEleme undeliveredElementException?.let { throw it } // throw UndeliveredElementException at the end if there was one } + @Suppress("UNCHECKED_CAST") private fun updateValueLocked(element: Any?): UndeliveredElementException? { val old = value val undeliveredElementException = if (old === EMPTY) null else diff --git a/kotlinx-coroutines-core/common/src/channels/Deprecated.kt b/kotlinx-coroutines-core/common/src/channels/Deprecated.kt index 963c168c7b..2b9ed42dd1 100644 --- a/kotlinx-coroutines-core/common/src/channels/Deprecated.kt +++ b/kotlinx-coroutines-core/common/src/channels/Deprecated.kt @@ -11,6 +11,7 @@ import kotlinx.coroutines.* import kotlin.coroutines.* import kotlin.jvm.* +/** @suppress **/ @PublishedApi // Binary compatibility internal fun consumesAll(vararg channels: ReceiveChannel<*>): CompletionHandler = { cause: Throwable? -> @@ -28,18 +29,21 @@ internal fun consumesAll(vararg channels: ReceiveChannel<*>): CompletionHandler exception?.let { throw it } } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) public suspend fun ReceiveChannel.elementAt(index: Int): E = consume { if (index < 0) throw IndexOutOfBoundsException("ReceiveChannel doesn't contain element at index $index.") var count = 0 for (element in this) { + @Suppress("UNUSED_CHANGED_VALUE") // KT-47628 if (index == count++) return element } throw IndexOutOfBoundsException("ReceiveChannel doesn't contain element at index $index.") } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) public suspend fun ReceiveChannel.elementAtOrNull(index: Int): E? = consume { @@ -53,6 +57,7 @@ public suspend fun ReceiveChannel.elementAtOrNull(index: Int): E? = return null } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) public suspend fun ReceiveChannel.first(): E = consume { @@ -62,6 +67,7 @@ public suspend fun ReceiveChannel.first(): E = return iterator.next() } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) public suspend fun ReceiveChannel.firstOrNull(): E? = consume { @@ -71,6 +77,7 @@ public suspend fun ReceiveChannel.firstOrNull(): E? = return iterator.next() } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) public suspend fun ReceiveChannel.indexOf(element: E): Int { var index = 0 @@ -82,6 +89,7 @@ public suspend fun ReceiveChannel.indexOf(element: E): Int { return -1 } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) public suspend fun ReceiveChannel.last(): E = consume { @@ -94,6 +102,7 @@ public suspend fun ReceiveChannel.last(): E = return last } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) public suspend fun ReceiveChannel.lastIndexOf(element: E): Int { var lastIndex = -1 @@ -106,6 +115,7 @@ public suspend fun ReceiveChannel.lastIndexOf(element: E): Int { return lastIndex } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) public suspend fun ReceiveChannel.lastOrNull(): E? = consume { @@ -118,6 +128,7 @@ public suspend fun ReceiveChannel.lastOrNull(): E? = return last } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) public suspend fun ReceiveChannel.single(): E = consume { @@ -130,6 +141,7 @@ public suspend fun ReceiveChannel.single(): E = return single } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) public suspend fun ReceiveChannel.singleOrNull(): E? = consume { @@ -142,6 +154,7 @@ public suspend fun ReceiveChannel.singleOrNull(): E? = return single } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) public fun ReceiveChannel.drop(n: Int, context: CoroutineContext = Dispatchers.Unconfined): ReceiveChannel = GlobalScope.produce(context, onCompletion = consumes()) { @@ -158,8 +171,12 @@ public fun ReceiveChannel.drop(n: Int, context: CoroutineContext = Dispat } } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) -public fun ReceiveChannel.dropWhile(context: CoroutineContext = Dispatchers.Unconfined, predicate: suspend (E) -> Boolean): ReceiveChannel = +public fun ReceiveChannel.dropWhile( + context: CoroutineContext = Dispatchers.Unconfined, + predicate: suspend (E) -> Boolean +): ReceiveChannel = GlobalScope.produce(context, onCompletion = consumes()) { for (e in this@dropWhile) { if (!predicate(e)) { @@ -173,15 +190,22 @@ public fun ReceiveChannel.dropWhile(context: CoroutineContext = Dispatche } @PublishedApi -internal fun ReceiveChannel.filter(context: CoroutineContext = Dispatchers.Unconfined, predicate: suspend (E) -> Boolean): ReceiveChannel = +internal fun ReceiveChannel.filter( + context: CoroutineContext = Dispatchers.Unconfined, + predicate: suspend (E) -> Boolean +): ReceiveChannel = GlobalScope.produce(context, onCompletion = consumes()) { for (e in this@filter) { if (predicate(e)) send(e) } } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) -public fun ReceiveChannel.filterIndexed(context: CoroutineContext = Dispatchers.Unconfined, predicate: suspend (index: Int, E) -> Boolean): ReceiveChannel = +public fun ReceiveChannel.filterIndexed( + context: CoroutineContext = Dispatchers.Unconfined, + predicate: suspend (index: Int, E) -> Boolean +): ReceiveChannel = GlobalScope.produce(context, onCompletion = consumes()) { var index = 0 for (e in this@filterIndexed) { @@ -189,8 +213,12 @@ public fun ReceiveChannel.filterIndexed(context: CoroutineContext = Dispa } } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) -public fun ReceiveChannel.filterNot(context: CoroutineContext = Dispatchers.Unconfined, predicate: suspend (E) -> Boolean): ReceiveChannel = +public fun ReceiveChannel.filterNot( + context: CoroutineContext = Dispatchers.Unconfined, + predicate: suspend (E) -> Boolean +): ReceiveChannel = filter(context) { !predicate(it) } @PublishedApi @@ -198,6 +226,7 @@ public fun ReceiveChannel.filterNot(context: CoroutineContext = Dispatche internal fun ReceiveChannel.filterNotNull(): ReceiveChannel = filter { it != null } as ReceiveChannel +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) public suspend fun > ReceiveChannel.filterNotNullTo(destination: C): C { consumeEach { @@ -206,6 +235,7 @@ public suspend fun > ReceiveChannel.fil return destination } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) public suspend fun > ReceiveChannel.filterNotNullTo(destination: C): C { consumeEach { @@ -214,6 +244,7 @@ public suspend fun > ReceiveChannel.filterNotNul return destination } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) public fun ReceiveChannel.take(n: Int, context: CoroutineContext = Dispatchers.Unconfined): ReceiveChannel = GlobalScope.produce(context, onCompletion = consumes()) { @@ -228,8 +259,12 @@ public fun ReceiveChannel.take(n: Int, context: CoroutineContext = Dispat } } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) -public fun ReceiveChannel.takeWhile(context: CoroutineContext = Dispatchers.Unconfined, predicate: suspend (E) -> Boolean): ReceiveChannel = +public fun ReceiveChannel.takeWhile( + context: CoroutineContext = Dispatchers.Unconfined, + predicate: suspend (E) -> Boolean +): ReceiveChannel = GlobalScope.produce(context, onCompletion = consumes()) { for (e in this@takeWhile) { if (!predicate(e)) return@produce @@ -253,6 +288,7 @@ internal suspend fun > ReceiveChannel.toCollec return destination } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) public suspend fun ReceiveChannel>.toMap(): Map = toMap(LinkedHashMap()) @@ -265,16 +301,22 @@ internal suspend fun > ReceiveChannel ReceiveChannel.toMutableList(): MutableList = toCollection(ArrayList()) +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) public suspend fun ReceiveChannel.toSet(): Set = this.toMutableSet() +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) -public fun ReceiveChannel.flatMap(context: CoroutineContext = Dispatchers.Unconfined, transform: suspend (E) -> ReceiveChannel): ReceiveChannel = +public fun ReceiveChannel.flatMap( + context: CoroutineContext = Dispatchers.Unconfined, + transform: suspend (E) -> ReceiveChannel +): ReceiveChannel = GlobalScope.produce(context, onCompletion = consumes()) { for (e in this@flatMap) { transform(e).toChannel(this) @@ -282,7 +324,10 @@ public fun ReceiveChannel.flatMap(context: CoroutineContext = Dispatch } @PublishedApi -internal fun ReceiveChannel.map(context: CoroutineContext = Dispatchers.Unconfined, transform: suspend (E) -> R): ReceiveChannel = +internal fun ReceiveChannel.map( + context: CoroutineContext = Dispatchers.Unconfined, + transform: suspend (E) -> R +): ReceiveChannel = GlobalScope.produce(context, onCompletion = consumes()) { consumeEach { send(transform(it)) @@ -290,7 +335,10 @@ internal fun ReceiveChannel.map(context: CoroutineContext = Dispatcher } @PublishedApi -internal fun ReceiveChannel.mapIndexed(context: CoroutineContext = Dispatchers.Unconfined, transform: suspend (index: Int, E) -> R): ReceiveChannel = +internal fun ReceiveChannel.mapIndexed( + context: CoroutineContext = Dispatchers.Unconfined, + transform: suspend (index: Int, E) -> R +): ReceiveChannel = GlobalScope.produce(context, onCompletion = consumes()) { var index = 0 for (e in this@mapIndexed) { @@ -298,14 +346,23 @@ internal fun ReceiveChannel.mapIndexed(context: CoroutineContext = Dis } } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) -public fun ReceiveChannel.mapIndexedNotNull(context: CoroutineContext = Dispatchers.Unconfined, transform: suspend (index: Int, E) -> R?): ReceiveChannel = +public fun ReceiveChannel.mapIndexedNotNull( + context: CoroutineContext = Dispatchers.Unconfined, + transform: suspend (index: Int, E) -> R? +): ReceiveChannel = mapIndexed(context, transform).filterNotNull() +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) -public fun ReceiveChannel.mapNotNull(context: CoroutineContext = Dispatchers.Unconfined, transform: suspend (E) -> R?): ReceiveChannel = +public fun ReceiveChannel.mapNotNull( + context: CoroutineContext = Dispatchers.Unconfined, + transform: suspend (E) -> R? +): ReceiveChannel = map(context, transform).filterNotNull() +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) public fun ReceiveChannel.withIndex(context: CoroutineContext = Dispatchers.Unconfined): ReceiveChannel> = GlobalScope.produce(context, onCompletion = consumes()) { @@ -315,12 +372,16 @@ public fun ReceiveChannel.withIndex(context: CoroutineContext = Dispatche } } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) public fun ReceiveChannel.distinct(): ReceiveChannel = this.distinctBy { it } @PublishedApi -internal fun ReceiveChannel.distinctBy(context: CoroutineContext = Dispatchers.Unconfined, selector: suspend (E) -> K): ReceiveChannel = +internal fun ReceiveChannel.distinctBy( + context: CoroutineContext = Dispatchers.Unconfined, + selector: suspend (E) -> K +): ReceiveChannel = GlobalScope.produce(context, onCompletion = consumes()) { val keys = HashSet() for (e in this@distinctBy) { @@ -336,12 +397,14 @@ internal fun ReceiveChannel.distinctBy(context: CoroutineContext = Dis internal suspend fun ReceiveChannel.toMutableSet(): MutableSet = toCollection(LinkedHashSet()) +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) public suspend fun ReceiveChannel.any(): Boolean = consume { return iterator().hasNext() } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) public suspend fun ReceiveChannel.count(): Int { var count = 0 @@ -349,6 +412,7 @@ public suspend fun ReceiveChannel.count(): Int { return count } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) public suspend fun ReceiveChannel.maxWith(comparator: Comparator): E? = consume { @@ -362,6 +426,7 @@ public suspend fun ReceiveChannel.maxWith(comparator: Comparator): return max } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) public suspend fun ReceiveChannel.minWith(comparator: Comparator): E? = consume { @@ -375,22 +440,29 @@ public suspend fun ReceiveChannel.minWith(comparator: Comparator): return min } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) public suspend fun ReceiveChannel.none(): Boolean = consume { return !iterator().hasNext() } +/** @suppress **/ @Deprecated(message = "Left for binary compatibility", level = DeprecationLevel.HIDDEN) public fun ReceiveChannel.requireNoNulls(): ReceiveChannel = map { it ?: throw IllegalArgumentException("null element found in $this.") } +/** @suppress **/ @Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) public infix fun ReceiveChannel.zip(other: ReceiveChannel): ReceiveChannel> = zip(other) { t1, t2 -> t1 to t2 } @PublishedApi // Binary compatibility -internal fun ReceiveChannel.zip(other: ReceiveChannel, context: CoroutineContext = Dispatchers.Unconfined, transform: (a: E, b: R) -> V): ReceiveChannel = +internal fun ReceiveChannel.zip( + other: ReceiveChannel, + context: CoroutineContext = Dispatchers.Unconfined, + transform: (a: E, b: R) -> V +): ReceiveChannel = GlobalScope.produce(context, onCompletion = consumesAll(this, other)) { val otherIterator = other.iterator() this@zip.consumeEach { element1 -> diff --git a/kotlinx-coroutines-core/common/src/flow/SharingStarted.kt b/kotlinx-coroutines-core/common/src/flow/SharingStarted.kt index ce568fb46d..f4c6f2ee8d 100644 --- a/kotlinx-coroutines-core/common/src/flow/SharingStarted.kt +++ b/kotlinx-coroutines-core/common/src/flow/SharingStarted.kt @@ -140,7 +140,7 @@ public fun SharingStarted.Companion.WhileSubscribed( stopTimeout: Duration = Duration.ZERO, replayExpiration: Duration = Duration.INFINITE ): SharingStarted = - StartedWhileSubscribed(stopTimeout.toLongMilliseconds(), replayExpiration.toLongMilliseconds()) + StartedWhileSubscribed(stopTimeout.inWholeMilliseconds, replayExpiration.inWholeMilliseconds) // -------------------------------- implementation -------------------------------- diff --git a/kotlinx-coroutines-core/common/src/flow/StateFlow.kt b/kotlinx-coroutines-core/common/src/flow/StateFlow.kt index da06ec73b9..9e82e78771 100644 --- a/kotlinx-coroutines-core/common/src/flow/StateFlow.kt +++ b/kotlinx-coroutines-core/common/src/flow/StateFlow.kt @@ -37,7 +37,7 @@ import kotlin.native.concurrent.* * val counter = _counter.asStateFlow() // publicly exposed as read-only state flow * * fun inc() { - * _counter.value++ + * _counter.update { count -> count + 1 } // atomic, safe for concurrent use * } * } * ``` @@ -186,6 +186,56 @@ public interface MutableStateFlow : StateFlow, MutableSharedFlow { @Suppress("FunctionName") public fun MutableStateFlow(value: T): MutableStateFlow = StateFlowImpl(value ?: NULL) +// ------------------------------------ Update methods ------------------------------------ + +/** + * Updates the [MutableStateFlow.value] atomically using the specified [function] of its value, and returns the new + * value. + * + * [function] may be evaluated multiple times, if [value] is being concurrently updated. + */ +public inline fun MutableStateFlow.updateAndGet(function: (T) -> T): T { + while (true) { + val prevValue = value + val nextValue = function(prevValue) + if (compareAndSet(prevValue, nextValue)) { + return nextValue + } + } +} + +/** + * Updates the [MutableStateFlow.value] atomically using the specified [function] of its value, and returns its + * prior value. + * + * [function] may be evaluated multiple times, if [value] is being concurrently updated. + */ +public inline fun MutableStateFlow.getAndUpdate(function: (T) -> T): T { + while (true) { + val prevValue = value + val nextValue = function(prevValue) + if (compareAndSet(prevValue, nextValue)) { + return prevValue + } + } +} + + +/** + * Updates the [MutableStateFlow.value] atomically using the specified [function] of its value. + * + * [function] may be evaluated multiple times, if [value] is being concurrently updated. + */ +public inline fun MutableStateFlow.update(function: (T) -> T) { + while (true) { + val prevValue = value + val nextValue = function(prevValue) + if (compareAndSet(prevValue, nextValue)) { + return + } + } +} + // ------------------------------------ Implementation ------------------------------------ @SharedImmutable @@ -366,10 +416,7 @@ private class StateFlowImpl( } internal fun MutableStateFlow.increment(delta: Int) { - while (true) { // CAS loop - val current = value - if (compareAndSet(current, current + delta)) return - } + update { it + delta } } internal fun StateFlow.fuseStateFlow( diff --git a/kotlinx-coroutines-core/common/src/flow/operators/Context.kt b/kotlinx-coroutines-core/common/src/flow/operators/Context.kt index 6686896766..04342ed074 100644 --- a/kotlinx-coroutines-core/common/src/flow/operators/Context.kt +++ b/kotlinx-coroutines-core/common/src/flow/operators/Context.kt @@ -309,6 +309,8 @@ private class CancellableFlowImpl(private val flow: Flow) : CancellableFlo * 3) It defers the execution of declarative [builder] until the moment of [collection][Flow.collect] similarly * to `Observable.defer`. But it is unexpected because nothing in the name `flowWith` reflects this fact. * 4) It can be confused with [flowOn] operator, though [flowWith] is much rarer. + * + * @suppress */ @FlowPreview @Deprecated(message = "flowWith is deprecated without replacement, please refer to its KDoc for an explanation", level = DeprecationLevel.ERROR) // Error in beta release, removal in 1.4 diff --git a/kotlinx-coroutines-core/common/src/flow/operators/Lint.kt b/kotlinx-coroutines-core/common/src/flow/operators/Lint.kt index 2f7bc358e8..858c885c1e 100644 --- a/kotlinx-coroutines-core/common/src/flow/operators/Lint.kt +++ b/kotlinx-coroutines-core/common/src/flow/operators/Lint.kt @@ -2,7 +2,7 @@ * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -@file:Suppress("unused", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") +@file:Suppress("unused", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "UNUSED_PARAMETER") package kotlinx.coroutines.flow diff --git a/kotlinx-coroutines-core/common/src/internal/ConcurrentLinkedList.kt b/kotlinx-coroutines-core/common/src/internal/ConcurrentLinkedList.kt index 0e765838ab..638ec43200 100644 --- a/kotlinx-coroutines-core/common/src/internal/ConcurrentLinkedList.kt +++ b/kotlinx-coroutines-core/common/src/internal/ConcurrentLinkedList.kt @@ -6,6 +6,7 @@ package kotlinx.coroutines.internal import kotlinx.atomicfu.* import kotlinx.coroutines.* +import kotlin.jvm.* import kotlin.native.concurrent.SharedImmutable /** @@ -227,8 +228,8 @@ private inline fun AtomicInt.addConditionally(delta: Int, condition: (cur: Int) } } -@Suppress("EXPERIMENTAL_FEATURE_WARNING") // We are using inline class only internally, so it is Ok -internal inline class SegmentOrClosed>(private val value: Any?) { +@JvmInline +internal value class SegmentOrClosed>(private val value: Any?) { val isClosed: Boolean get() = value === CLOSED @Suppress("UNCHECKED_CAST") val segment: S get() = if (value === CLOSED) error("Does not contain segment") else value as S diff --git a/kotlinx-coroutines-core/common/src/internal/DispatchedContinuation.kt b/kotlinx-coroutines-core/common/src/internal/DispatchedContinuation.kt index 45b9699c84..c689a38186 100644 --- a/kotlinx-coroutines-core/common/src/internal/DispatchedContinuation.kt +++ b/kotlinx-coroutines-core/common/src/internal/DispatchedContinuation.kt @@ -58,40 +58,30 @@ internal class DispatchedContinuation( */ private val _reusableCancellableContinuation = atomic(null) - public val reusableCancellableContinuation: CancellableContinuationImpl<*>? + private val reusableCancellableContinuation: CancellableContinuationImpl<*>? get() = _reusableCancellableContinuation.value as? CancellableContinuationImpl<*> - public fun isReusable(requester: CancellableContinuationImpl<*>): Boolean { + fun isReusable(): Boolean { /* + Invariant: caller.resumeMode.isReusableMode * Reusability control: * `null` -> no reusability at all, `false` - * If current state is not CCI, then we are within `suspendCancellableCoroutineReusable`, true - * Else, if result is CCI === requester, then it's our reusable continuation - * Identity check my fail for the following pattern: - * ``` - * loop: - * suspendCancellableCoroutineReusable { } // Reusable, outer coroutine stores the child handle - * suspendCancellableCoroutine { } // **Not reusable**, handle should be disposed after {}, otherwise - * it will leak because it won't be freed by `releaseInterceptedContinuation` - * ``` + * anything else -> reusable. */ - val value = _reusableCancellableContinuation.value ?: return false - if (value is CancellableContinuationImpl<*>) return value === requester - return true + return _reusableCancellableContinuation.value != null } - /** * Awaits until previous call to `suspendCancellableCoroutineReusable` will * stop mutating cached instance */ - public fun awaitReusability() { - _reusableCancellableContinuation.loop { it -> + fun awaitReusability() { + _reusableCancellableContinuation.loop { if (it !== REUSABLE_CLAIMED) return } } - public fun release() { + fun release() { /* * Called from `releaseInterceptedContinuation`, can be concurrent with * the code in `getResult` right after `trySuspend` returned `true`, so we have diff --git a/kotlinx-coroutines-core/common/src/internal/InlineList.kt b/kotlinx-coroutines-core/common/src/internal/InlineList.kt index 34c1e893ee..61bf6d01ad 100644 --- a/kotlinx-coroutines-core/common/src/internal/InlineList.kt +++ b/kotlinx-coroutines-core/common/src/internal/InlineList.kt @@ -7,14 +7,16 @@ package kotlinx.coroutines.internal import kotlinx.coroutines.assert +import kotlin.jvm.* /* * Inline class that represents a mutable list, but does not allocate an underlying storage * for zero and one elements. * Cannot be parametrized with `List<*>`. */ -internal inline class InlineList(private val holder: Any? = null) { - public operator fun plus(element: E): InlineList { +@JvmInline +internal value class InlineList(private val holder: Any? = null) { + operator fun plus(element: E): InlineList { assert { element !is List<*> } // Lists are prohibited return when (holder) { null -> InlineList(element) @@ -31,7 +33,7 @@ internal inline class InlineList(private val holder: Any? = null) { } } - public inline fun forEachReversed(action: (E) -> Unit) { + inline fun forEachReversed(action: (E) -> Unit) { when (holder) { null -> return !is ArrayList<*> -> action(holder as E) diff --git a/kotlinx-coroutines-core/common/src/intrinsics/Cancellable.kt b/kotlinx-coroutines-core/common/src/intrinsics/Cancellable.kt index 173f0afb65..f5b96a8d88 100644 --- a/kotlinx-coroutines-core/common/src/intrinsics/Cancellable.kt +++ b/kotlinx-coroutines-core/common/src/intrinsics/Cancellable.kt @@ -49,6 +49,19 @@ private inline fun runSafely(completion: Continuation<*>, block: () -> Unit) { try { block() } catch (e: Throwable) { - completion.resumeWith(Result.failure(e)) + dispatcherFailure(completion, e) } } + +private fun dispatcherFailure(completion: Continuation<*>, e: Throwable) { + /* + * This method is invoked when we failed to start a coroutine due to the throwing + * dispatcher implementation or missing Dispatchers.Main. + * This situation is not recoverable, so we are trying to deliver the exception by all means: + * 1) Resume the coroutine with an exception, so it won't prevent its parent from completion + * 2) Rethrow the exception immediately, so it will crash the caller (e.g. when the coroutine had + * no parent or it was async/produce over MainScope). + */ + completion.resumeWith(Result.failure(e)) + throw e +} diff --git a/kotlinx-coroutines-core/common/test/flow/sharing/StateFlowTest.kt b/kotlinx-coroutines-core/common/test/flow/sharing/StateFlowTest.kt index 0a2c0458c4..be4f8c536b 100644 --- a/kotlinx-coroutines-core/common/test/flow/sharing/StateFlowTest.kt +++ b/kotlinx-coroutines-core/common/test/flow/sharing/StateFlowTest.kt @@ -174,23 +174,11 @@ class StateFlowTest : TestBase() { } @Test - fun testReferenceUpdatesAndCAS() { - val d0 = Data(0) - val d0_1 = Data(0) - val d1 = Data(1) - val d1_1 = Data(1) - val d1_2 = Data(1) - val state = MutableStateFlow(d0) - assertSame(d0, state.value) - state.value = d0_1 // equal, nothing changes - assertSame(d0, state.value) - state.value = d1 // updates - assertSame(d1, state.value) - assertFalse(state.compareAndSet(d0, d0)) // wrong value - assertSame(d1, state.value) - assertTrue(state.compareAndSet(d1_1, d1_2)) // "updates", but ref stays - assertSame(d1, state.value) - assertTrue(state.compareAndSet(d1_1, d0)) // updates, reference changes - assertSame(d0, state.value) + fun testUpdate() = runTest { + val state = MutableStateFlow(0) + state.update { it + 2 } + assertEquals(2, state.value) + state.update { it + 3 } + assertEquals(5, state.value) } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/jvm/src/Dispatchers.kt b/kotlinx-coroutines-core/jvm/src/Dispatchers.kt index 25c0fbe9b0..d82598eab4 100644 --- a/kotlinx-coroutines-core/jvm/src/Dispatchers.kt +++ b/kotlinx-coroutines-core/jvm/src/Dispatchers.kt @@ -107,9 +107,10 @@ public actual object Dispatchers { * * ### Implementation note * - * This dispatcher shares threads with a [Default][Dispatchers.Default] dispatcher, so using - * `withContext(Dispatchers.IO) { ... }` does not lead to an actual switching to another thread — - * typically execution continues in the same thread. + * This dispatcher shares 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. * As a result of thread sharing, more than 64 (default parallelism) threads can be created (but not used) * during operations over IO dispatcher. */ diff --git a/kotlinx-coroutines-core/jvm/src/Executors.kt b/kotlinx-coroutines-core/jvm/src/Executors.kt index 394304f231..7ea3cc6874 100644 --- a/kotlinx-coroutines-core/jvm/src/Executors.kt +++ b/kotlinx-coroutines-core/jvm/src/Executors.kt @@ -4,6 +4,7 @@ package kotlinx.coroutines +import kotlinx.coroutines.flow.* import kotlinx.coroutines.internal.* import java.io.* import java.util.concurrent.* @@ -39,6 +40,22 @@ public abstract class ExecutorCoroutineDispatcher: CoroutineDispatcher(), Closea /** * Converts an instance of [ExecutorService] to an implementation of [ExecutorCoroutineDispatcher]. * + * ## Interaction with [delay] and time-based coroutines. + * + * If the given [ExecutorService] is an instance of [ScheduledExecutorService], then all time-related + * coroutine operations such as [delay], [withTimeout] and time-based [Flow] operators will be scheduled + * on this executor using [schedule][ScheduledExecutorService.schedule] method. If the corresponding + * coroutine is cancelled, [ScheduledFuture.cancel] will be invoked on the corresponding future. + * + * If the given [ExecutorService] is an instance of [ScheduledThreadPoolExecutor], then prior to any scheduling, + * remove on cancel policy will be set via [ScheduledThreadPoolExecutor.setRemoveOnCancelPolicy] in order + * to reduce the memory pressure of cancelled coroutines. + * + * If the executor service is neither of this types, the separate internal thread will be used to + * _track_ the delay and time-related executions, but the coroutine itself will still be executed + * on top of the given executor. + * + * ## Rejected execution * If the underlying executor throws [RejectedExecutionException] on * attempt to submit a continuation task (it happens when [closing][ExecutorCoroutineDispatcher.close] the * resulting dispatcher, on underlying executor [shutdown][ExecutorService.shutdown], or when it uses limited queues), @@ -52,6 +69,23 @@ public fun ExecutorService.asCoroutineDispatcher(): ExecutorCoroutineDispatcher /** * Converts an instance of [Executor] to an implementation of [CoroutineDispatcher]. * + * ## Interaction with [delay] and time-based coroutines. + * + * If the given [Executor] is an instance of [ScheduledExecutorService], then all time-related + * coroutine operations such as [delay], [withTimeout] and time-based [Flow] operators will be scheduled + * on this executor using [schedule][ScheduledExecutorService.schedule] method. If the corresponding + * coroutine is cancelled, [ScheduledFuture.cancel] will be invoked on the corresponding future. + * + * If the given [Executor] is an instance of [ScheduledThreadPoolExecutor], then prior to any scheduling, + * remove on cancel policy will be set via [ScheduledThreadPoolExecutor.setRemoveOnCancelPolicy] in order + * to reduce the memory pressure of cancelled coroutines. + * + * If the executor is neither of this types, the separate internal thread will be used to + * _track_ the delay and time-related executions, but the coroutine itself will still be executed + * on top of the given executor. + * + * ## Rejected execution + * * If the underlying executor throws [RejectedExecutionException] on * attempt to submit a continuation task (it happens when [closing][ExecutorCoroutineDispatcher.close] the * resulting dispatcher, on underlying executor [shutdown][ExecutorService.shutdown], or when it uses limited queues), @@ -75,18 +109,15 @@ private class DispatcherExecutor(@JvmField val dispatcher: CoroutineDispatcher) override fun toString(): String = dispatcher.toString() } -private class ExecutorCoroutineDispatcherImpl(override val executor: Executor) : ExecutorCoroutineDispatcherBase() { - init { - initFutureCancellation() - } -} +internal class ExecutorCoroutineDispatcherImpl(override val executor: Executor) : ExecutorCoroutineDispatcher(), Delay { -internal abstract class ExecutorCoroutineDispatcherBase : ExecutorCoroutineDispatcher(), Delay { - - private var removesFutureOnCancellation: Boolean = false - - internal fun initFutureCancellation() { - removesFutureOnCancellation = removeFutureOnCancel(executor) + /* + * Attempts to reflectively (to be Java 6 compatible) invoke + * ScheduledThreadPoolExecutor.setRemoveOnCancelPolicy in order to cleanup + * internal scheduler queue on cancellation. + */ + init { + removeFutureOnCancel(executor) } override fun dispatch(context: CoroutineContext, block: Runnable) { @@ -99,17 +130,12 @@ internal abstract class ExecutorCoroutineDispatcherBase : ExecutorCoroutineDispa } } - /* - * removesFutureOnCancellation is required to avoid memory leak. - * On Java 7+ we reflectively invoke ScheduledThreadPoolExecutor.setRemoveOnCancelPolicy(true) and we're fine. - * On Java 6 we're scheduling time-based coroutines to our own thread safe heap which supports cancellation. - */ override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation) { - val future = if (removesFutureOnCancellation) { - scheduleBlock(ResumeUndispatchedRunnable(this, continuation), continuation.context, timeMillis) - } else { - null - } + val future = (executor as? ScheduledExecutorService)?.scheduleBlock( + ResumeUndispatchedRunnable(this, continuation), + continuation.context, + timeMillis + ) // If everything went fine and the scheduling attempt was not rejected -- use it if (future != null) { continuation.cancelFutureOnCancellation(future) @@ -120,20 +146,16 @@ internal abstract class ExecutorCoroutineDispatcherBase : ExecutorCoroutineDispa } override fun invokeOnTimeout(timeMillis: Long, block: Runnable, context: CoroutineContext): DisposableHandle { - val future = if (removesFutureOnCancellation) { - scheduleBlock(block, context, timeMillis) - } else { - null - } + val future = (executor as? ScheduledExecutorService)?.scheduleBlock(block, context, timeMillis) return when { future != null -> DisposableFutureHandle(future) else -> DefaultExecutor.invokeOnTimeout(timeMillis, block, context) } } - private fun scheduleBlock(block: Runnable, context: CoroutineContext, timeMillis: Long): ScheduledFuture<*>? { + private fun ScheduledExecutorService.scheduleBlock(block: Runnable, context: CoroutineContext, timeMillis: Long): ScheduledFuture<*>? { return try { - (executor as? ScheduledExecutorService)?.schedule(block, timeMillis, TimeUnit.MILLISECONDS) + schedule(block, timeMillis, TimeUnit.MILLISECONDS) } catch (e: RejectedExecutionException) { cancelJobOnRejection(context, e) null @@ -149,7 +171,7 @@ internal abstract class ExecutorCoroutineDispatcherBase : ExecutorCoroutineDispa } override fun toString(): String = executor.toString() - override fun equals(other: Any?): Boolean = other is ExecutorCoroutineDispatcherBase && other.executor === executor + override fun equals(other: Any?): Boolean = other is ExecutorCoroutineDispatcherImpl && other.executor === executor override fun hashCode(): Int = System.identityHashCode(executor) } diff --git a/kotlinx-coroutines-core/jvm/src/Future.kt b/kotlinx-coroutines-core/jvm/src/Future.kt index 948ef6065c..b27a970845 100644 --- a/kotlinx-coroutines-core/jvm/src/Future.kt +++ b/kotlinx-coroutines-core/jvm/src/Future.kt @@ -13,20 +13,20 @@ import java.util.concurrent.* * Cancels a specified [future] when this job is cancelled. * This is a shortcut for the following code with slightly more efficient implementation (one fewer object created). * ``` - * invokeOnCompletion { future.cancel(false) } + * invokeOnCompletion { if (it != null) future.cancel(false) } * ``` * * @suppress **This an internal API and should not be used from general code.** */ @InternalCoroutinesApi public fun Job.cancelFutureOnCompletion(future: Future<*>): DisposableHandle = - invokeOnCompletion(handler = CancelFutureOnCompletion(future)) // TODO make it work only on cancellation as well? + invokeOnCompletion(handler = CancelFutureOnCompletion(future)) /** * Cancels a specified [future] when this job is cancelled. * This is a shortcut for the following code with slightly more efficient implementation (one fewer object created). * ``` - * invokeOnCancellation { future.cancel(false) } + * invokeOnCancellation { if (it != null) future.cancel(false) } * ``` */ public fun CancellableContinuation<*>.cancelFutureOnCancellation(future: Future<*>): Unit = @@ -38,7 +38,7 @@ private class CancelFutureOnCompletion( override fun invoke(cause: Throwable?) { // Don't interrupt when cancelling future on completion, because no one is going to reset this // interruption flag and it will cause spurious failures elsewhere - future.cancel(false) + if (cause != null) future.cancel(false) } } @@ -46,7 +46,7 @@ private class CancelFutureOnCancel(private val future: Future<*>) : CancelHandle override fun invoke(cause: Throwable?) { // Don't interrupt when cancelling future on completion, because no one is going to reset this // interruption flag and it will cause spurious failures elsewhere - future.cancel(false) + if (cause != null) future.cancel(false) } override fun toString() = "CancelFutureOnCancel[$future]" } diff --git a/kotlinx-coroutines-core/jvm/src/ThreadPoolDispatcher.kt b/kotlinx-coroutines-core/jvm/src/ThreadPoolDispatcher.kt index 44a79d42ed..99e3b46cce 100644 --- a/kotlinx-coroutines-core/jvm/src/ThreadPoolDispatcher.kt +++ b/kotlinx-coroutines-core/jvm/src/ThreadPoolDispatcher.kt @@ -59,40 +59,11 @@ public fun newSingleThreadContext(name: String): ExecutorCoroutineDispatcher = @ObsoleteCoroutinesApi public fun newFixedThreadPoolContext(nThreads: Int, name: String): ExecutorCoroutineDispatcher { require(nThreads >= 1) { "Expected at least one thread, but $nThreads specified" } - return ThreadPoolDispatcher(nThreads, name) -} - -internal class PoolThread( - @JvmField val dispatcher: ThreadPoolDispatcher, // for debugging & tests - target: Runnable, name: String -) : Thread(target, name) { - init { isDaemon = true } -} - -/** - * Dispatches coroutine execution to a thread pool of a fixed size. Instances of this dispatcher are - * created with [newSingleThreadContext] and [newFixedThreadPoolContext]. - */ -internal class ThreadPoolDispatcher internal constructor( - private val nThreads: Int, - private val name: String -) : ExecutorCoroutineDispatcherBase() { - private val threadNo = AtomicInteger() - - override val executor: Executor = Executors.newScheduledThreadPool(nThreads) { target -> - PoolThread(this, target, if (nThreads == 1) name else name + "-" + threadNo.incrementAndGet()) - } - - init { - initFutureCancellation() + val threadNo = AtomicInteger() + val executor = Executors.newScheduledThreadPool(nThreads) { runnable -> + val t = Thread(runnable, if (nThreads == 1) name else name + "-" + threadNo.incrementAndGet()) + t.isDaemon = true + t } - - /** - * Closes this dispatcher -- shuts down all threads in this pool and releases resources. - */ - public override fun close() { - (executor as ExecutorService).shutdown() - } - - override fun toString(): String = "ThreadPoolDispatcher[$nThreads, $name]" + return executor.asCoroutineDispatcher() } diff --git a/kotlinx-coroutines-core/jvm/src/channels/Actor.kt b/kotlinx-coroutines-core/jvm/src/channels/Actor.kt index 5153cb47f1..96cda7b1af 100644 --- a/kotlinx-coroutines-core/jvm/src/channels/Actor.kt +++ b/kotlinx-coroutines-core/jvm/src/channels/Actor.kt @@ -163,6 +163,7 @@ private class LazyActorCoroutine( return super.send(element) } + @Suppress("DEPRECATION_ERROR") override fun offer(element: E): Boolean { start() return super.offer(element) diff --git a/kotlinx-coroutines-core/jvm/src/debug/internal/DebugProbes.kt b/kotlinx-coroutines-core/jvm/src/debug/internal/DebugProbes.kt new file mode 100644 index 0000000000..8dc5b7c23d --- /dev/null +++ b/kotlinx-coroutines-core/jvm/src/debug/internal/DebugProbes.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +@file:Suppress("unused") + +package kotlinx.coroutines.debug.internal + +import kotlin.coroutines.* + +/* + * This class is used by ByteBuddy from kotlinx-coroutines-debug as kotlin.coroutines.jvm.internal.DebugProbesKt replacement. + * In theory, it should belong to kotlinx-coroutines-debug, but placing it here significantly simplifies the + * Android AS debugger that does on-load DEX transformation + */ + +// Stubs which are injected as coroutine probes. Require direct match of signatures +internal fun probeCoroutineResumed(frame: Continuation<*>) = DebugProbesImpl.probeCoroutineResumed(frame) + +internal fun probeCoroutineSuspended(frame: Continuation<*>) = DebugProbesImpl.probeCoroutineSuspended(frame) +internal fun probeCoroutineCreated(completion: Continuation): Continuation = + DebugProbesImpl.probeCoroutineCreated(completion) diff --git a/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt b/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt index 3102fdfbb9..2d447413b8 100644 --- a/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt +++ b/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt @@ -67,7 +67,10 @@ public fun MainCoroutineDispatcher.isMissing(): Boolean = this is MissingMainCor @Suppress("MayBeConstant") private val SUPPORT_MISSING = true -@Suppress("ConstantConditionIf") +@Suppress( + "ConstantConditionIf", + "IMPLICIT_NOTHING_TYPE_ARGUMENT_AGAINST_NOT_NOTHING_EXPECTED_TYPE" // KT-47626 +) private fun createMissingDispatcher(cause: Throwable? = null, errorHint: String? = null) = if (SUPPORT_MISSING) MissingMainCoroutineDispatcher(cause, errorHint) else cause?.let { throw it } ?: throwMissingMainDispatcherException() diff --git a/kotlinx-coroutines-core/jvm/src/internal/StackTraceRecovery.kt b/kotlinx-coroutines-core/jvm/src/internal/StackTraceRecovery.kt index 48e8790cd1..174c57b762 100644 --- a/kotlinx-coroutines-core/jvm/src/internal/StackTraceRecovery.kt +++ b/kotlinx-coroutines-core/jvm/src/internal/StackTraceRecovery.kt @@ -29,7 +29,7 @@ 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 = tryCopyException(exception) ?: return exception + val copy = tryCopyAndVerify(exception) ?: return exception return copy.sanitizeStackTrace() } @@ -66,9 +66,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 = tryCopyException(cause) ?: return exception - // Verify that the new exception has the same message as the original one (bail out if not, see #1631) - if (newException.message != cause.message) return exception + val newException = tryCopyAndVerify(cause) ?: return exception // Update stacktrace val stacktrace = createStackTrace(continuation) if (stacktrace.isEmpty()) return exception @@ -80,6 +78,14 @@ 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 diff --git a/kotlinx-coroutines-core/jvm/test/ExecutorAsCoroutineDispatcherDelayTest.kt b/kotlinx-coroutines-core/jvm/test/ExecutorAsCoroutineDispatcherDelayTest.kt new file mode 100644 index 0000000000..dbe9cb3741 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/ExecutorAsCoroutineDispatcherDelayTest.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines + +import org.junit.Test +import java.lang.Runnable +import java.util.concurrent.* +import kotlin.test.* + +class ExecutorAsCoroutineDispatcherDelayTest : TestBase() { + + private var callsToSchedule = 0 + + private inner class STPE : ScheduledThreadPoolExecutor(1) { + override fun schedule(command: Runnable, delay: Long, unit: TimeUnit): ScheduledFuture<*> { + if (delay != 0L) ++callsToSchedule + return super.schedule(command, delay, unit) + } + } + + private inner class SES : ScheduledExecutorService by STPE() + + @Test + fun testScheduledThreadPool() = runTest { + val executor = STPE() + withContext(executor.asCoroutineDispatcher()) { + delay(100) + } + executor.shutdown() + assertEquals(1, callsToSchedule) + } + + @Test + fun testScheduledExecutorService() = runTest { + val executor = SES() + withContext(executor.asCoroutineDispatcher()) { + delay(100) + } + executor.shutdown() + assertEquals(1, callsToSchedule) + } +} diff --git a/kotlinx-coroutines-core/jvm/test/FailFastOnStartTest.kt b/kotlinx-coroutines-core/jvm/test/FailFastOnStartTest.kt index 15cb83ceed..8a7878c9a6 100644 --- a/kotlinx-coroutines-core/jvm/test/FailFastOnStartTest.kt +++ b/kotlinx-coroutines-core/jvm/test/FailFastOnStartTest.kt @@ -70,8 +70,18 @@ class FailFastOnStartTest : TestBase() { val actor = actor(Dispatchers.Main, start = CoroutineStart.LAZY) { fail() } actor.send(1) } - + private fun mainException(e: Throwable): Boolean { return e is IllegalStateException && e.message?.contains("Module with the Main dispatcher is missing") ?: false } + + @Test + fun testProduceNonChild() = runTest(expected = ::mainException) { + produce(Job() + Dispatchers.Main) { fail() } + } + + @Test + fun testAsyncNonChild() = runTest(expected = ::mainException) { + async(Job() + Dispatchers.Main) { fail() } + } } diff --git a/kotlinx-coroutines-core/jvm/test/FailingCoroutinesMachineryTest.kt b/kotlinx-coroutines-core/jvm/test/FailingCoroutinesMachineryTest.kt index c9f722a5b8..04b0ba547d 100644 --- a/kotlinx-coroutines-core/jvm/test/FailingCoroutinesMachineryTest.kt +++ b/kotlinx-coroutines-core/jvm/test/FailingCoroutinesMachineryTest.kt @@ -33,7 +33,7 @@ class FailingCoroutinesMachineryTest( private var caught: Throwable? = null private val latch = CountDownLatch(1) - private var exceptionHandler = CoroutineExceptionHandler { _, t -> caught = t;latch.countDown() } + private var exceptionHandler = CoroutineExceptionHandler { _, t -> caught = t; latch.countDown() } private val lazyOuterDispatcher = lazy { newFixedThreadPoolContext(1, "") } private object FailingUpdate : ThreadContextElement { @@ -115,14 +115,20 @@ class FailingCoroutinesMachineryTest( @Test fun testElement() = runTest { - launch(NonCancellable + dispatcher.value + exceptionHandler + element) {} + // Top-level throwing dispatcher may rethrow an exception right here + runCatching { + launch(NonCancellable + dispatcher.value + exceptionHandler + element) {} + } checkException() } @Test fun testNestedElement() = runTest { - launch(NonCancellable + dispatcher.value + exceptionHandler) { - launch(element) { } + // Top-level throwing dispatcher may rethrow an exception right here + runCatching { + launch(NonCancellable + dispatcher.value + exceptionHandler) { + launch(element) { } + } } checkException() } diff --git a/kotlinx-coroutines-core/jvm/test/ReusableContinuationStressTest.kt b/kotlinx-coroutines-core/jvm/test/ReusableContinuationStressTest.kt new file mode 100644 index 0000000000..a256815db3 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/ReusableContinuationStressTest.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines + +import kotlinx.coroutines.flow.* +import org.junit.* + +class ReusableContinuationStressTest : TestBase() { + + private val iterations = 1000 * stressTestMultiplierSqrt + + @Test // Originally reported by @denis-bezrukov in #2736 + fun testDebounceWithStateFlow() = runBlocking { + withContext(Dispatchers.Default) { + repeat(iterations) { + launch { // <- load the dispatcher and OS scheduler + runStressTestOnce(1, 1) + } + } + } + } + + private suspend fun runStressTestOnce(delay: Int, debounce: Int) = coroutineScope { + val stateFlow = MutableStateFlow(0) + val emitter = launch { + repeat(1000) { i -> + stateFlow.emit(i) + delay(delay.toLong()) + } + } + var last = 0 + stateFlow.debounce(debounce.toLong()).take(100).collect { i -> + if (i - last > 100) { + last = i + } + } + emitter.cancel() + } +} diff --git a/kotlinx-coroutines-core/jvm/test/channels/ChannelsJvmTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ChannelsJvmTest.kt index 2e4ba9ac0e..8512aebcc0 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ChannelsJvmTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/ChannelsJvmTest.kt @@ -25,7 +25,6 @@ class ChannelsJvmTest : TestBase() { assertEquals(45, runBlocking { sum.await() }) } - // Uncomment lines when migrated to 1.5, these are bugs in inline classes codegen @Test fun testTrySendBlockingClosedChannel() { run { @@ -33,7 +32,7 @@ class ChannelsJvmTest : TestBase() { channel.trySendBlocking(Unit) .onSuccess { expectUnreached() } .onFailure { assertTrue(it is ClosedSendChannelException) } -// .also { assertTrue { it.isClosed } } + .also { assertTrue { it.isClosed } } } run { @@ -41,7 +40,7 @@ class ChannelsJvmTest : TestBase() { channel.trySendBlocking(Unit) .onSuccess { expectUnreached() } .onFailure { assertTrue(it is TestException) } -// .also { assertTrue { it.isClosed } } + .also { assertTrue { it.isClosed } } } run { @@ -49,8 +48,7 @@ class ChannelsJvmTest : TestBase() { channel.trySendBlocking(Unit) .onSuccess { expectUnreached() } .onFailure { assertTrue(it is TestCancellationException) } -// .also { assertTrue { it.isClosed } } + .also { assertTrue { it.isClosed } } } } - } diff --git a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryCustomExceptionsTest.kt b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryCustomExceptionsTest.kt index 70336659e8..dba738a8d3 100644 --- a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryCustomExceptionsTest.kt +++ b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryCustomExceptionsTest.kt @@ -5,6 +5,7 @@ package kotlinx.coroutines.exceptions import kotlinx.coroutines.* +import kotlinx.coroutines.channels.* import org.junit.Test import kotlin.test.* @@ -71,4 +72,56 @@ class StackTraceRecoveryCustomExceptionsTest : TestBase() { assertEquals("custom", cause.message) } } + + class WrongMessageException(token: String) : RuntimeException("Token $token") + + @Test + fun testWrongMessageException() = runTest { + val result = runCatching { + coroutineScope { + throw WrongMessageException("OK") + } + } + val ex = result.exceptionOrNull() ?: error("Expected to fail") + assertTrue(ex is WrongMessageException) + assertEquals("Token OK", ex.message) + } + + @Test + fun testWrongMessageExceptionInChannel() = runTest { + val result = produce(SupervisorJob() + Dispatchers.Unconfined) { + throw WrongMessageException("OK") + } + val ex = runCatching { + @Suppress("ControlFlowWithEmptyBody") + for (unit in result) { + // Iterator has a special code path + } + }.exceptionOrNull() ?: error("Expected to fail") + assertTrue(ex is WrongMessageException) + assertEquals("Token OK", ex.message) + } + + class CopyableWithCustomMessage( + message: String?, + cause: Throwable? = null + ) : RuntimeException(message, cause), + CopyableThrowable { + + override fun createCopy(): CopyableWithCustomMessage { + return CopyableWithCustomMessage("Recovered: [$message]", cause) + } + } + + @Test + fun testCustomCopyableMessage() = runTest { + val result = runCatching { + coroutineScope { + throw CopyableWithCustomMessage("OK") + } + } + val ex = result.exceptionOrNull() ?: error("Expected to fail") + assertTrue(ex is CopyableWithCustomMessage) + assertEquals("Recovered: [OK]", ex.message) + } } diff --git a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryTest.kt b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryTest.kt index 8dc106bc22..0a8b6530e2 100644 --- a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryTest.kt +++ b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryTest.kt @@ -261,18 +261,4 @@ class StackTraceRecoveryTest : TestBase() { } yield() // nop to make sure it is not a tail call } - - @Test - fun testWrongMessageException() = runTest { - val result = runCatching { - coroutineScope { - throw WrongMessageException("OK") - } - } - val ex = result.exceptionOrNull() ?: error("Expected to fail") - assertTrue(ex is WrongMessageException) - assertEquals("Token OK", ex.message) - } - - public class WrongMessageException(token: String) : RuntimeException("Token $token") } diff --git a/kotlinx-coroutines-core/jvm/test/flow/StateFlowUpdateStressTest.kt b/kotlinx-coroutines-core/jvm/test/flow/StateFlowUpdateStressTest.kt new file mode 100644 index 0000000000..660ed0aacd --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/flow/StateFlowUpdateStressTest.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.flow + +import kotlinx.coroutines.* +import org.junit.* +import kotlin.test.* +import kotlin.test.Test + +class StateFlowUpdateStressTest : TestBase() { + private val iterations = 1_000_000 * stressTestMultiplier + + @get:Rule + public val executor = ExecutorRule(2) + + @Test + fun testUpdate() = doTest { update { it + 1 } } + + @Test + fun testUpdateAndGet() = doTest { updateAndGet { it + 1 } } + + @Test + fun testGetAndUpdate() = doTest { getAndUpdate { it + 1 } } + + private fun doTest(increment: MutableStateFlow.() -> Unit) = runTest { + val flow = MutableStateFlow(0) + val j1 = launch(Dispatchers.Default) { + repeat(iterations / 2) { + flow.increment() + } + } + + val j2 = launch(Dispatchers.Default) { + repeat(iterations / 2) { + flow.increment() + } + } + + joinAll(j1, j2) + assertEquals(iterations, flow.value) + } +} diff --git a/kotlinx-coroutines-core/jvm/test/knit/ClosedAfterGuideTestExecutor.kt b/kotlinx-coroutines-core/jvm/test/knit/ClosedAfterGuideTestExecutor.kt new file mode 100644 index 0000000000..30fbfee264 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/knit/ClosedAfterGuideTestExecutor.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines // Trick to make guide tests use these declarations with executors that can be closed on our side implicitly + +import java.util.concurrent.* +import java.util.concurrent.atomic.* +import kotlin.coroutines.* + +internal fun newSingleThreadContext(name: String): ExecutorCoroutineDispatcher = ClosedAfterGuideTestDispatcher(1, name) + +internal fun newFixedThreadPoolContext(nThreads: Int, name: String): ExecutorCoroutineDispatcher = + ClosedAfterGuideTestDispatcher(nThreads, name) + +internal class PoolThread( + @JvmField val dispatcher: ExecutorCoroutineDispatcher, // for debugging & tests + target: Runnable, name: String +) : Thread(target, name) { + init { + isDaemon = true + } +} + +private class ClosedAfterGuideTestDispatcher( + private val nThreads: Int, + private val name: String +) : ExecutorCoroutineDispatcher() { + private val threadNo = AtomicInteger() + + override val executor: Executor = + Executors.newScheduledThreadPool(nThreads, object : ThreadFactory { + override fun newThread(target: java.lang.Runnable): Thread { + return PoolThread( + this@ClosedAfterGuideTestDispatcher, + target, + if (nThreads == 1) name else name + "-" + threadNo.incrementAndGet() + ) + } + }) + + override fun dispatch(context: CoroutineContext, block: Runnable) { + executor.execute(wrapTask(block)) + } + + override fun close() { + (executor as ExecutorService).shutdown() + } + + override fun toString(): String = "ThreadPoolDispatcher[$nThreads, $name]" +} diff --git a/kotlinx-coroutines-core/jvm/test/knit/TestUtil.kt b/kotlinx-coroutines-core/jvm/test/knit/TestUtil.kt index 7eda9043db..2e61ec6bce 100644 --- a/kotlinx-coroutines-core/jvm/test/knit/TestUtil.kt +++ b/kotlinx-coroutines-core/jvm/test/knit/TestUtil.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.knit @@ -11,8 +11,6 @@ import kotlinx.knit.test.* import java.util.concurrent.* import kotlin.test.* -fun wrapTask(block: Runnable) = kotlinx.coroutines.wrapTask(block) - // helper function to dump exception to stdout for ease of debugging failed tests private inline fun outputException(name: String, block: () -> T): T = try { block() } @@ -176,4 +174,4 @@ private inline fun List.verify(verification: () -> Unit) { } throw t } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/jvm/test/scheduling/BlockingCoroutineDispatcherTest.kt b/kotlinx-coroutines-core/jvm/test/scheduling/BlockingCoroutineDispatcherTest.kt index f31752c8b5..fe09440f59 100644 --- a/kotlinx-coroutines-core/jvm/test/scheduling/BlockingCoroutineDispatcherTest.kt +++ b/kotlinx-coroutines-core/jvm/test/scheduling/BlockingCoroutineDispatcherTest.kt @@ -101,7 +101,7 @@ class BlockingCoroutineDispatcherTest : SchedulerTestBase() { firstBarrier.await() secondBarrier.await() blockingTasks.joinAll() - checkPoolThreadsCreated(21..22) + checkPoolThreadsCreated(21 /* blocking tasks + 1 for CPU */..20 + CORES_COUNT) } @Test @@ -122,7 +122,7 @@ class BlockingCoroutineDispatcherTest : SchedulerTestBase() { barrier.await() blockingTasks.joinAll() // There may be race when multiple CPU threads are trying to lazily created one more - checkPoolThreadsCreated(104..120) + checkPoolThreadsCreated(101..100 + CORES_COUNT) } @Test diff --git a/kotlinx-coroutines-core/jvm/test/scheduling/SchedulerTestBase.kt b/kotlinx-coroutines-core/jvm/test/scheduling/SchedulerTestBase.kt index 72d7c90882..dd969bdd37 100644 --- a/kotlinx-coroutines-core/jvm/test/scheduling/SchedulerTestBase.kt +++ b/kotlinx-coroutines-core/jvm/test/scheduling/SchedulerTestBase.kt @@ -39,14 +39,6 @@ abstract class SchedulerTestBase : TestBase() { ) } - /** - * Asserts that any number of pool worker threads in [range] exists at the time of method invocation - */ - fun checkPoolThreadsExist(range: IntRange) { - val threads = Thread.getAllStackTraces().keys.asSequence().filter { it is CoroutineScheduler.Worker }.count() - assertTrue(threads in range, "Expected threads in $range interval, but has $threads") - } - private fun maxSequenceNumber(): Int? { return Thread.getAllStackTraces().keys.asSequence().filter { it is CoroutineScheduler.Worker } .map { sequenceNumber(it.name) }.maxOrNull() diff --git a/kotlinx-coroutines-core/native/src/Debug.kt b/kotlinx-coroutines-core/native/src/Debug.kt index a0a8d272f8..f17c2ed7fc 100644 --- a/kotlinx-coroutines-core/native/src/Debug.kt +++ b/kotlinx-coroutines-core/native/src/Debug.kt @@ -5,14 +5,12 @@ package kotlinx.coroutines import kotlin.math.* +import kotlin.native.* internal actual val DEBUG: Boolean = false -internal actual val Any.hexAddress: String get() = abs(id().let { if (it == Int.MIN_VALUE) 0 else it }).toString(16) +internal actual val Any.hexAddress: String get() = identityHashCode().toUInt().toString(16) internal actual val Any.classSimpleName: String get() = this::class.simpleName ?: "Unknown" -@SymbolName("Kotlin_Any_hashCode") -public external fun Any.id(): Int // Note: can return negative value on K/N - internal actual inline fun assert(value: () -> Boolean) {} diff --git a/kotlinx-coroutines-debug/README.md b/kotlinx-coroutines-debug/README.md index 77ecd67f6c..f7b8602236 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.5.0.jar`. +You can run your application with an additional argument: `-javaagent:kotlinx-coroutines-debug-1.5.1.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-debug/build.gradle b/kotlinx-coroutines-debug/build.gradle index b2e3f2cf53..43d94d1841 100644 --- a/kotlinx-coroutines-debug/build.gradle +++ b/kotlinx-coroutines-debug/build.gradle @@ -31,16 +31,6 @@ dependencies { api "net.java.dev.jna:jna-platform:$jna_version" } -// TODO: JVM IR generates different stacktrace so temporary disable stacktrace tests -if (rootProject.ext.jvm_ir_enabled) { - tasks.named('test', Test) { - filter { -// excludeTest('kotlinx.coroutines.debug.CoroutinesDumpTest', 'testCreationStackTrace') - excludeTestsMatching('kotlinx.coroutines.debug.DebugProbesTest') - } - } -} - java { /* This is needed to be able to run JUnit5 tests. Otherwise, Gradle complains that it can't find the JVM1.6-compatible version of the `junit-jupiter-api` artifact. */ diff --git a/kotlinx-coroutines-debug/src/DebugProbes.kt b/kotlinx-coroutines-debug/src/DebugProbes.kt index 373864adb8..ed346d8136 100644 --- a/kotlinx-coroutines-debug/src/DebugProbes.kt +++ b/kotlinx-coroutines-debug/src/DebugProbes.kt @@ -143,10 +143,3 @@ public object DebugProbes { */ public fun dumpCoroutines(out: PrintStream = System.out): Unit = DebugProbesImpl.dumpCoroutines(out) } - -// Stubs which are injected as coroutine probes. Require direct match of signatures -internal fun probeCoroutineResumed(frame: Continuation<*>) = DebugProbesImpl.probeCoroutineResumed(frame) - -internal fun probeCoroutineSuspended(frame: Continuation<*>) = DebugProbesImpl.probeCoroutineSuspended(frame) -internal fun probeCoroutineCreated(completion: Continuation): Continuation = - DebugProbesImpl.probeCoroutineCreated(completion) diff --git a/kotlinx-coroutines-debug/src/internal/Attach.kt b/kotlinx-coroutines-debug/src/internal/Attach.kt index f38447f72a..f1cc96e6d3 100644 --- a/kotlinx-coroutines-debug/src/internal/Attach.kt +++ b/kotlinx-coroutines-debug/src/internal/Attach.kt @@ -20,7 +20,7 @@ internal class ByteBuddyDynamicAttach : Function1 { private fun attach() { ByteBuddyAgent.install(ByteBuddyAgent.AttachmentProvider.ForEmulatedAttachment.INSTANCE) val cl = Class.forName("kotlin.coroutines.jvm.internal.DebugProbesKt") - val cl2 = Class.forName("kotlinx.coroutines.debug.DebugProbesKt") + val cl2 = Class.forName("kotlinx.coroutines.debug.internal.DebugProbesKt") ByteBuddy() .redefine(cl2) diff --git a/kotlinx-coroutines-test/README.md b/kotlinx-coroutines-test/README.md index 30027fdb76..622e81d50b 100644 --- a/kotlinx-coroutines-test/README.md +++ b/kotlinx-coroutines-test/README.md @@ -9,7 +9,7 @@ This package provides testing utilities for effectively testing coroutines. Add `kotlinx-coroutines-test` to your project test dependencies: ``` dependencies { - testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.5.0' + testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.5.1' } ``` @@ -445,7 +445,7 @@ If you have any suggestions for improvements to this experimental API please sha -[setMain]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/kotlinx.coroutines.-dispatchers/set-main.html +[setMain]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/set-main.html [runBlockingTest]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/run-blocking-test.html [UncompletedCoroutinesError]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-uncompleted-coroutines-error/index.html [DelayController]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-delay-controller/index.html diff --git a/kotlinx-coroutines-test/src/internal/MainTestDispatcher.kt b/kotlinx-coroutines-test/src/internal/MainTestDispatcher.kt index 9953756f70..c85d27ea87 100644 --- a/kotlinx-coroutines-test/src/internal/MainTestDispatcher.kt +++ b/kotlinx-coroutines-test/src/internal/MainTestDispatcher.kt @@ -35,7 +35,6 @@ internal class TestMainDispatcher(private val mainFactory: MainDispatcherFactory delegate.dispatch(context, block) } - @ExperimentalCoroutinesApi override fun isDispatchNeeded(context: CoroutineContext): Boolean = delegate.isDispatchNeeded(context) override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation) { @@ -50,11 +49,11 @@ internal class TestMainDispatcher(private val mainFactory: MainDispatcherFactory return delay.invokeOnTimeout(timeMillis, block, context) } - public fun setDispatcher(dispatcher: CoroutineDispatcher) { + fun setDispatcher(dispatcher: CoroutineDispatcher) { _delegate = dispatcher } - public fun resetDispatcher() { + fun resetDispatcher() { _delegate = null } } diff --git a/reactive/kotlinx-coroutines-jdk9/build.gradle.kts b/reactive/kotlinx-coroutines-jdk9/build.gradle.kts index 38c6735cc4..be5eb421f1 100644 --- a/reactive/kotlinx-coroutines-jdk9/build.gradle.kts +++ b/reactive/kotlinx-coroutines-jdk9/build.gradle.kts @@ -3,8 +3,7 @@ */ dependencies { - compile(project(":kotlinx-coroutines-reactive")) - compile("org.reactivestreams:reactive-streams-flow-adapters:${version("reactive_streams")}") + implementation(project(":kotlinx-coroutines-reactive")) } tasks { diff --git a/reactive/kotlinx-coroutines-reactive/README.md b/reactive/kotlinx-coroutines-reactive/README.md index b6466b2da7..ec59d3d9fd 100644 --- a/reactive/kotlinx-coroutines-reactive/README.md +++ b/reactive/kotlinx-coroutines-reactive/README.md @@ -43,13 +43,13 @@ Suspending extension functions and suspending iteration: [kotlinx.coroutines.reactive.publish]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactive/kotlinx.coroutines.reactive/publish.html -[Publisher.asFlow]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactive/kotlinx.coroutines.reactive/org.reactivestreams.-publisher/as-flow.html -[Flow.asPublisher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactive/kotlinx.coroutines.reactive/kotlinx.coroutines.flow.-flow/as-publisher.html -[org.reactivestreams.Publisher.awaitFirst]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactive/kotlinx.coroutines.reactive/org.reactivestreams.-publisher/await-first.html -[org.reactivestreams.Publisher.awaitFirstOrDefault]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactive/kotlinx.coroutines.reactive/org.reactivestreams.-publisher/await-first-or-default.html -[org.reactivestreams.Publisher.awaitFirstOrElse]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactive/kotlinx.coroutines.reactive/org.reactivestreams.-publisher/await-first-or-else.html -[org.reactivestreams.Publisher.awaitFirstOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactive/kotlinx.coroutines.reactive/org.reactivestreams.-publisher/await-first-or-null.html -[org.reactivestreams.Publisher.awaitSingle]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactive/kotlinx.coroutines.reactive/org.reactivestreams.-publisher/await-single.html +[Publisher.asFlow]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactive/kotlinx.coroutines.reactive/as-flow.html +[Flow.asPublisher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactive/kotlinx.coroutines.reactive/as-publisher.html +[org.reactivestreams.Publisher.awaitFirst]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactive/kotlinx.coroutines.reactive/await-first.html +[org.reactivestreams.Publisher.awaitFirstOrDefault]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactive/kotlinx.coroutines.reactive/await-first-or-default.html +[org.reactivestreams.Publisher.awaitFirstOrElse]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactive/kotlinx.coroutines.reactive/await-first-or-else.html +[org.reactivestreams.Publisher.awaitFirstOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactive/kotlinx.coroutines.reactive/await-first-or-null.html +[org.reactivestreams.Publisher.awaitSingle]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactive/kotlinx.coroutines.reactive/await-single.html diff --git a/reactive/kotlinx-coroutines-reactive/src/Await.kt b/reactive/kotlinx-coroutines-reactive/src/Await.kt index 067f5e8031..fef1205a8a 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Await.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Await.kt @@ -100,6 +100,8 @@ public suspend fun Publisher.awaitSingle(): T = awaitOne(Mode.SINGLE) * * @throws NoSuchElementException if the publisher does not emit any value * @throws IllegalArgumentException if the publisher emits more than one value + * + * @suppress */ @Deprecated( message = "Deprecated without a replacement due to its name incorrectly conveying the behavior. " + @@ -127,6 +129,7 @@ public suspend fun Publisher.awaitSingleOrDefault(default: T): T = awaitO * meaning. * * @throws IllegalArgumentException if the publisher emits more than one value + * @suppress */ @Deprecated( message = "Deprecated without a replacement due to its name incorrectly conveying the behavior. " + @@ -156,6 +159,7 @@ public suspend fun Publisher.awaitSingleOrNull(): T? = awaitOne(Mode.SING * meaning. * * @throws IllegalArgumentException if the publisher emits more than one value + * @suppress */ @Deprecated( message = "Deprecated without a replacement due to its name incorrectly conveying the behavior. " + @@ -301,4 +305,4 @@ private fun gotSignalInTerminalStateException(context: CoroutineContext, signalN */ private fun moreThanOneValueProvidedException(context: CoroutineContext, mode: Mode) = handleCoroutineException(context, - IllegalStateException("Only a single value was requested in '$mode', but the publisher provided more")) \ No newline at end of file + IllegalStateException("Only a single value was requested in '$mode', but the publisher provided more")) diff --git a/reactive/kotlinx-coroutines-reactive/src/Migration.kt b/reactive/kotlinx-coroutines-reactive/src/Migration.kt index b949f22665..41927e67ec 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Migration.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Migration.kt @@ -12,6 +12,7 @@ import kotlinx.coroutines.flow.* import org.reactivestreams.* // Binary compatibility with Spring 5.2 RC +/** @suppress */ @Deprecated( message = "Replaced in favor of ReactiveFlow extension, please import kotlinx.coroutines.reactive.* instead of kotlinx.coroutines.reactive.FlowKt", level = DeprecationLevel.HIDDEN @@ -20,6 +21,7 @@ import org.reactivestreams.* public fun Publisher.asFlowDeprecated(): Flow = asFlow() // Binary compatibility with Spring 5.2 RC +/** @suppress */ @Deprecated( message = "Replaced in favor of ReactiveFlow extension, please import kotlinx.coroutines.reactive.* instead of kotlinx.coroutines.reactive.FlowKt", level = DeprecationLevel.HIDDEN @@ -27,6 +29,7 @@ public fun Publisher.asFlowDeprecated(): Flow = asFlow() @JvmName("asPublisher") public fun Flow.asPublisherDeprecated(): Publisher = asPublisher() +/** @suppress */ @FlowPreview @Deprecated( message = "batchSize parameter is deprecated, use .buffer() instead to control the backpressure", diff --git a/reactive/kotlinx-coroutines-reactive/src/Publish.kt b/reactive/kotlinx-coroutines-reactive/src/Publish.kt index 7ebe269436..4928a7439e 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Publish.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Publish.kt @@ -61,6 +61,7 @@ private const val CLOSED = -1L // closed, but have not signalled onCompleted/ private const val SIGNALLED = -2L // already signalled subscriber onCompleted/onError private val DEFAULT_HANDLER: (Throwable, CoroutineContext) -> Unit = { t, ctx -> if (t !is CancellationException) handleCoroutineException(ctx, t) } +/** @suppress */ @Suppress("CONFLICTING_JVM_DECLARATIONS", "RETURN_TYPE_MISMATCH_ON_INHERITANCE") @InternalCoroutinesApi public class PublisherCoroutine( diff --git a/reactive/kotlinx-coroutines-reactive/test/IterableFlowTckTest.kt b/reactive/kotlinx-coroutines-reactive/test/IterableFlowTckTest.kt index 906b2579d7..cf935f97dc 100644 --- a/reactive/kotlinx-coroutines-reactive/test/IterableFlowTckTest.kt +++ b/reactive/kotlinx-coroutines-reactive/test/IterableFlowTckTest.kt @@ -7,18 +7,12 @@ package kotlinx.coroutines.reactive import kotlinx.coroutines.flow.* -import org.junit.* import org.junit.Ignore import org.junit.Test import org.reactivestreams.* import org.reactivestreams.tck.* - -import org.reactivestreams.Subscription -import org.reactivestreams.Subscriber -import java.util.ArrayList import java.util.concurrent.* -import java.util.concurrent.CountDownLatch -import java.util.concurrent.ForkJoinPool.commonPool +import java.util.concurrent.ForkJoinPool.* import kotlin.test.* class IterableFlowTckTest : PublisherVerification(TestEnvironment()) { @@ -97,7 +91,7 @@ class IterableFlowTckTest : PublisherVerification(TestEnvironment()) { override fun onSubscribe(s: Subscription) { this.s = s - for (i in 0 until n) { + for (i in 0..n) { commonPool().execute { s.request(1) } } } @@ -115,7 +109,7 @@ class IterableFlowTckTest : PublisherVerification(TestEnvironment()) { } }) - latch.await(50, TimeUnit.SECONDS) + latch.await() assertEquals(array.toList(), collected) } diff --git a/reactive/kotlinx-coroutines-reactor/README.md b/reactive/kotlinx-coroutines-reactor/README.md index 7028310621..69157afc55 100644 --- a/reactive/kotlinx-coroutines-reactor/README.md +++ b/reactive/kotlinx-coroutines-reactor/README.md @@ -27,7 +27,6 @@ Conversion functions: | -------- | --------------- | [Job.asMono][kotlinx.coroutines.Job.asMono] | Converts a job to a hot Mono | [Deferred.asMono][kotlinx.coroutines.Deferred.asMono] | Converts a deferred value to a hot Mono -| [ReceiveChannel.asFlux][kotlinx.coroutines.channels.ReceiveChannel.asFlux] | Converts a streaming channel to a hot Flux | [Scheduler.asCoroutineDispatcher][reactor.core.scheduler.Scheduler.asCoroutineDispatcher] | Converts a scheduler to a [CoroutineDispatcher] @@ -46,12 +45,11 @@ Conversion functions: [mono]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactor/kotlinx.coroutines.reactor/mono.html [flux]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactor/kotlinx.coroutines.reactor/flux.html -[Flow.asFlux]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactor/kotlinx.coroutines.reactor/kotlinx.coroutines.flow.-flow/as-flux.html +[Flow.asFlux]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactor/kotlinx.coroutines.reactor/as-flux.html [ReactorContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactor/kotlinx.coroutines.reactor/-reactor-context/index.html -[kotlinx.coroutines.Job.asMono]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactor/kotlinx.coroutines.reactor/kotlinx.coroutines.-job/as-mono.html -[kotlinx.coroutines.Deferred.asMono]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactor/kotlinx.coroutines.reactor/kotlinx.coroutines.-deferred/as-mono.html -[kotlinx.coroutines.channels.ReceiveChannel.asFlux]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactor/kotlinx.coroutines.reactor/kotlinx.coroutines.channels.-receive-channel/as-flux.html -[reactor.core.scheduler.Scheduler.asCoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactor/kotlinx.coroutines.reactor/reactor.core.scheduler.-scheduler/as-coroutine-dispatcher.html +[kotlinx.coroutines.Job.asMono]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactor/kotlinx.coroutines.reactor/as-mono.html +[kotlinx.coroutines.Deferred.asMono]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactor/kotlinx.coroutines.reactor/as-mono.html +[reactor.core.scheduler.Scheduler.asCoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-reactor/kotlinx.coroutines.reactor/as-coroutine-dispatcher.html diff --git a/reactive/kotlinx-coroutines-reactor/src/Convert.kt b/reactive/kotlinx-coroutines-reactor/src/Convert.kt index 73cc336d0d..3063d1dda3 100644 --- a/reactive/kotlinx-coroutines-reactor/src/Convert.kt +++ b/reactive/kotlinx-coroutines-reactor/src/Convert.kt @@ -42,6 +42,7 @@ public fun Deferred.asMono(context: CoroutineContext): Mono = mono(co * Every subscriber receives values from this channel in a **fan-out** fashion. If the are multiple subscribers, * they'll receive values in a round-robin way. * @param context -- the coroutine context from which the resulting flux is going to be signalled + * @suppress */ @Deprecated(message = "Deprecated in the favour of consumeAsFlow()", level = DeprecationLevel.ERROR, diff --git a/reactive/kotlinx-coroutines-reactor/src/Flux.kt b/reactive/kotlinx-coroutines-reactor/src/Flux.kt index 63168a443b..1e408d83e5 100644 --- a/reactive/kotlinx-coroutines-reactor/src/Flux.kt +++ b/reactive/kotlinx-coroutines-reactor/src/Flux.kt @@ -83,6 +83,9 @@ private fun Subscriber?.reject(t: Throwable) { onError(t) } +/** + * @suppress + */ @Deprecated( message = "CoroutineScope.flux is deprecated in favour of top-level flux", level = DeprecationLevel.HIDDEN, diff --git a/reactive/kotlinx-coroutines-reactor/src/Migration.kt b/reactive/kotlinx-coroutines-reactor/src/Migration.kt index d5d84f5c2a..8da0db2d59 100644 --- a/reactive/kotlinx-coroutines-reactor/src/Migration.kt +++ b/reactive/kotlinx-coroutines-reactor/src/Migration.kt @@ -9,6 +9,7 @@ package kotlinx.coroutines.reactor import kotlinx.coroutines.flow.* import reactor.core.publisher.* +/** @suppress **/ @Deprecated( message = "Replaced in favor of ReactiveFlow extension, please import kotlinx.coroutines.reactor.* instead of kotlinx.coroutines.reactor.FlowKt", level = DeprecationLevel.HIDDEN diff --git a/reactive/kotlinx-coroutines-reactor/src/Mono.kt b/reactive/kotlinx-coroutines-reactor/src/Mono.kt index 67c1baa02d..e86d51c614 100644 --- a/reactive/kotlinx-coroutines-reactor/src/Mono.kt +++ b/reactive/kotlinx-coroutines-reactor/src/Mono.kt @@ -125,6 +125,9 @@ private class MonoCoroutine( override fun isDisposed(): Boolean = disposed } +/** + * @suppress + */ @Deprecated( message = "CoroutineScope.mono is deprecated in favour of top-level mono", level = DeprecationLevel.HIDDEN, @@ -148,6 +151,8 @@ public fun CoroutineScope.mono( * ``` * It looks like more than one value could be returned from `findById` and [awaitFirst] discards the extra elements, * when in fact, at most a single value can be present. + * + * @suppress */ @Deprecated( message = "Mono produces at most one value, so the semantics of dropping the remaining elements are not useful. " + @@ -170,6 +175,8 @@ public suspend fun Mono.awaitFirst(): T = awaitSingle() * ``` * It looks like more than one value could be returned from `findById` and [awaitFirstOrDefault] discards the extra * elements, when in fact, at most a single value can be present. + * + * @suppress */ @Deprecated( message = "Mono produces at most one value, so the semantics of dropping the remaining elements are not useful. " + @@ -192,6 +199,8 @@ public suspend fun Mono.awaitFirstOrDefault(default: T): T = awaitSingleO * ``` * It looks like more than one value could be returned from `findById` and [awaitFirstOrNull] discards the extra * elements, when in fact, at most a single value can be present. + * + * @suppress */ @Deprecated( message = "Mono produces at most one value, so the semantics of dropping the remaining elements are not useful. " + @@ -214,6 +223,8 @@ public suspend fun Mono.awaitFirstOrNull(): T? = awaitSingleOrNull() * ``` * It looks like more than one value could be returned from `findById` and [awaitFirstOrElse] discards the extra * elements, when in fact, at most a single value can be present. + * + * @suppress */ @Deprecated( message = "Mono produces at most one value, so the semantics of dropping the remaining elements are not useful. " + @@ -236,6 +247,8 @@ public suspend fun Mono.awaitFirstOrElse(defaultValue: () -> T): T = awai * ``` * It looks like more than one value could be returned from `findById` and [awaitLast] discards the initial elements, * when in fact, at most a single value can be present. + * + * @suppress */ @Deprecated( message = "Mono produces at most one value, so the last element is the same as the first. " + @@ -243,4 +256,4 @@ public suspend fun Mono.awaitFirstOrElse(defaultValue: () -> T): T = awai level = DeprecationLevel.WARNING, replaceWith = ReplaceWith("this.awaitSingle()") ) // Warning since 1.5, error in 1.6 -public suspend fun Mono.awaitLast(): T = awaitSingle() \ No newline at end of file +public suspend fun Mono.awaitLast(): T = awaitSingle() diff --git a/reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt b/reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt index a8e247fc95..d9228409db 100644 --- a/reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt +++ b/reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt @@ -68,6 +68,7 @@ public fun ContextView.asCoroutineContext(): ReactorContext = ReactorContext(thi /** * Wraps the given [Context] into [ReactorContext], so it can be added to the coroutine's context * and later used via `coroutineContext[ReactorContext]`. + * @suppress */ @Deprecated("The more general version for ContextView should be used instead", level = DeprecationLevel.HIDDEN) public fun Context.asCoroutineContext(): ReactorContext = readOnly().asCoroutineContext() // `readOnly()` is zero-cost. @@ -76,4 +77,4 @@ public fun Context.asCoroutineContext(): ReactorContext = readOnly().asCoroutine * Updates the Reactor context in this [CoroutineContext], adding (or possibly replacing) some values. */ internal fun CoroutineContext.extendReactorContext(extensions: ContextView): CoroutineContext = - (this[ReactorContext]?.context?.putAll(extensions) ?: extensions).asCoroutineContext() \ No newline at end of file + (this[ReactorContext]?.context?.putAll(extensions) ?: extensions).asCoroutineContext() diff --git a/reactive/kotlinx-coroutines-rx2/README.md b/reactive/kotlinx-coroutines-rx2/README.md index 1ae2c8a04c..d93f569ac9 100644 --- a/reactive/kotlinx-coroutines-rx2/README.md +++ b/reactive/kotlinx-coroutines-rx2/README.md @@ -25,9 +25,8 @@ Suspending extension functions and suspending iteration: | **Name** | **Description** | -------- | --------------- | [CompletableSource.await][io.reactivex.CompletableSource.await] | Awaits for completion of the completable value -| [MaybeSource.await][io.reactivex.MaybeSource.await] | Awaits for the value of the maybe and returns it or null -| [MaybeSource.awaitOrDefault][io.reactivex.MaybeSource.awaitOrDefault] | Awaits for the value of the maybe and returns it or default -| [MaybeSource.openSubscription][io.reactivex.MaybeSource.openSubscription] | Subscribes to maybe and returns [ReceiveChannel] +| [MaybeSource.awaitSingle][io.reactivex.MaybeSource.awaitSingle] | Awaits for the value of the maybe and returns it or throws an exception +| [MaybeSource.awaitSingleOrNull][io.reactivex.MaybeSource.awaitSingleOrNull] | Awaits for the value of the maybe and returns it or null | [SingleSource.await][io.reactivex.SingleSource.await] | Awaits for completion of the single value and returns it | [ObservableSource.awaitFirst][io.reactivex.ObservableSource.awaitFirst] | Awaits for the first value from the given observable | [ObservableSource.awaitFirstOrDefault][io.reactivex.ObservableSource.awaitFirstOrDefault] | Awaits for the first value from the given observable or default @@ -57,7 +56,6 @@ Conversion functions: [ProducerScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-producer-scope/index.html -[ReceiveChannel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/index.html @@ -71,22 +69,21 @@ Conversion functions: [rxSingle]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/rx-single.html [rxObservable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/rx-observable.html [rxFlowable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/rx-flowable.html -[Flow.asFlowable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/kotlinx.coroutines.flow.-flow/as-flowable.html -[Flow.asObservable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/kotlinx.coroutines.flow.-flow/as-observable.html -[ObservableSource.asFlow]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/io.reactivex.-observable-source/as-flow.html -[io.reactivex.CompletableSource.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/io.reactivex.-completable-source/await.html -[io.reactivex.MaybeSource.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/io.reactivex.-maybe-source/await.html -[io.reactivex.MaybeSource.awaitOrDefault]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/io.reactivex.-maybe-source/await-or-default.html -[io.reactivex.MaybeSource.openSubscription]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/io.reactivex.-maybe-source/open-subscription.html -[io.reactivex.SingleSource.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/io.reactivex.-single-source/await.html -[io.reactivex.ObservableSource.awaitFirst]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/io.reactivex.-observable-source/await-first.html -[io.reactivex.ObservableSource.awaitFirstOrDefault]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/io.reactivex.-observable-source/await-first-or-default.html -[io.reactivex.ObservableSource.awaitFirstOrElse]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/io.reactivex.-observable-source/await-first-or-else.html -[io.reactivex.ObservableSource.awaitFirstOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/io.reactivex.-observable-source/await-first-or-null.html -[io.reactivex.ObservableSource.awaitSingle]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/io.reactivex.-observable-source/await-single.html -[kotlinx.coroutines.Job.asCompletable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/kotlinx.coroutines.-job/as-completable.html -[kotlinx.coroutines.Deferred.asSingle]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/kotlinx.coroutines.-deferred/as-single.html -[io.reactivex.Scheduler.asCoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/io.reactivex.-scheduler/as-coroutine-dispatcher.html +[Flow.asFlowable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/as-flowable.html +[Flow.asObservable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/as-observable.html +[ObservableSource.asFlow]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/as-flow.html +[io.reactivex.CompletableSource.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/await.html +[io.reactivex.MaybeSource.awaitSingle]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/await-single.html +[io.reactivex.MaybeSource.awaitSingleOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/await-single-or-null.html +[io.reactivex.SingleSource.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/await.html +[io.reactivex.ObservableSource.awaitFirst]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/await-first.html +[io.reactivex.ObservableSource.awaitFirstOrDefault]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/await-first-or-default.html +[io.reactivex.ObservableSource.awaitFirstOrElse]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/await-first-or-else.html +[io.reactivex.ObservableSource.awaitFirstOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/await-first-or-null.html +[io.reactivex.ObservableSource.awaitSingle]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/await-single.html +[kotlinx.coroutines.Job.asCompletable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/as-completable.html +[kotlinx.coroutines.Deferred.asSingle]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/as-single.html +[io.reactivex.Scheduler.asCoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/as-coroutine-dispatcher.html diff --git a/reactive/kotlinx-coroutines-rx2/build.gradle b/reactive/kotlinx-coroutines-rx2/build.gradle index 01c226a990..b6fd93274c 100644 --- a/reactive/kotlinx-coroutines-rx2/build.gradle +++ b/reactive/kotlinx-coroutines-rx2/build.gradle @@ -1,6 +1,7 @@ /* * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ +import org.jetbrains.dokka.gradle.DokkaTaskPartial dependencies { api project(':kotlinx-coroutines-reactive') @@ -9,10 +10,12 @@ dependencies { api "io.reactivex.rxjava2:rxjava:$rxjava2_version" } -tasks.withType(dokka.getClass()) { - externalDocumentationLink { - url = new URL('http://reactivex.io/RxJava/2.x/javadoc/') - packageListUrl = projectDir.toPath().resolve("package.list").toUri().toURL() +tasks.withType(DokkaTaskPartial.class) { + dokkaSourceSets.configureEach { + externalDocumentationLink { + url.set(new URL('http://reactivex.io/RxJava/2.x/javadoc/')) + packageListUrl.set(projectDir.toPath().resolve("package.list").toUri().toURL()) + } } } diff --git a/reactive/kotlinx-coroutines-rx2/src/RxAwait.kt b/reactive/kotlinx-coroutines-rx2/src/RxAwait.kt index d7b8ee26f6..0e0b47ebe8 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxAwait.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxAwait.kt @@ -76,6 +76,7 @@ public suspend fun MaybeSource.awaitSingle(): T = awaitSingleOrNull() ?: * * Deprecated in favor of [awaitSingleOrNull] in order to reflect that `null` can be returned to denote the absence of * a value, as opposed to throwing in such case. + * @suppress */ @Deprecated( message = "Deprecated in favor of awaitSingleOrNull()", @@ -97,6 +98,7 @@ public suspend fun MaybeSource.await(): T? = awaitSingleOrNull() * * Deprecated in favor of [awaitSingleOrNull] for naming consistency (see the deprecation of [MaybeSource.await] for * details). + * @suppress */ @Deprecated( message = "Deprecated in favor of awaitSingleOrNull()", diff --git a/reactive/kotlinx-coroutines-rx2/src/RxChannel.kt b/reactive/kotlinx-coroutines-rx2/src/RxChannel.kt index 51a5d54e1c..bb093b0793 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxChannel.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxChannel.kt @@ -18,6 +18,7 @@ import kotlinx.coroutines.reactive.* * * This API is deprecated in the favour of [Flow]. * [MaybeSource] doesn't have a corresponding [Flow] adapter, so it should be transformed to [Observable] first. + * @suppress */ @Deprecated(message = "Deprecated in the favour of Flow", level = DeprecationLevel.ERROR) // Will be hidden in 1.5 public fun MaybeSource.openSubscription(): ReceiveChannel { @@ -32,6 +33,7 @@ public fun MaybeSource.openSubscription(): ReceiveChannel { * * This API is deprecated in the favour of [Flow]. * [ObservableSource] doesn't have a corresponding [Flow] adapter, so it should be transformed to [Observable] first. + * @suppress */ @Deprecated(message = "Deprecated in the favour of Flow", level = DeprecationLevel.ERROR) // Will be hidden in 1.5 public fun ObservableSource.openSubscription(): ReceiveChannel { diff --git a/reactive/kotlinx-coroutines-rx2/src/RxCompletable.kt b/reactive/kotlinx-coroutines-rx2/src/RxCompletable.kt index 3f6c27a693..3f9153822b 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxCompletable.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxCompletable.kt @@ -60,6 +60,9 @@ private class RxCompletableCoroutine( } } +/** + * @suppress + */ @Deprecated( message = "CoroutineScope.rxCompletable is deprecated in favour of top-level rxCompletable", level = DeprecationLevel.HIDDEN, diff --git a/reactive/kotlinx-coroutines-rx2/src/RxConvert.kt b/reactive/kotlinx-coroutines-rx2/src/RxConvert.kt index 2aeb994de1..497c922ca5 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxConvert.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxConvert.kt @@ -149,6 +149,7 @@ public fun ReceiveChannel.asObservable(context: CoroutineContext): send(t) } +/** @suppress **/ @Suppress("UNUSED") // KT-42513 @JvmOverloads // binary compatibility @JvmName("from") @@ -156,6 +157,7 @@ public fun ReceiveChannel.asObservable(context: CoroutineContext): public fun Flow._asFlowable(context: CoroutineContext = EmptyCoroutineContext): Flowable = asFlowable(context) +/** @suppress **/ @Suppress("UNUSED") // KT-42513 @JvmOverloads // binary compatibility @JvmName("from") diff --git a/reactive/kotlinx-coroutines-rx2/src/RxFlowable.kt b/reactive/kotlinx-coroutines-rx2/src/RxFlowable.kt index f3ae65aadf..c856bb4e18 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxFlowable.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxFlowable.kt @@ -40,6 +40,7 @@ public fun rxFlowable( return Flowable.fromPublisher(publishInternal(GlobalScope, context, RX_HANDLER, block)) } +/** @suppress */ @Deprecated( message = "CoroutineScope.rxFlowable is deprecated in favour of top-level rxFlowable", level = DeprecationLevel.HIDDEN, diff --git a/reactive/kotlinx-coroutines-rx2/src/RxMaybe.kt b/reactive/kotlinx-coroutines-rx2/src/RxMaybe.kt index aa531c6ecf..ab713123d9 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxMaybe.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxMaybe.kt @@ -61,6 +61,7 @@ private class RxMaybeCoroutine( } } +/** @suppress */ @Deprecated( message = "CoroutineScope.rxMaybe is deprecated in favour of top-level rxMaybe", level = DeprecationLevel.HIDDEN, diff --git a/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt b/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt index 7300b484c9..5f409815af 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt @@ -191,6 +191,7 @@ private class RxObservableCoroutine( } } +/** @suppress */ @Deprecated( message = "CoroutineScope.rxObservable is deprecated in favour of top-level rxObservable", level = DeprecationLevel.HIDDEN, diff --git a/reactive/kotlinx-coroutines-rx2/src/RxSingle.kt b/reactive/kotlinx-coroutines-rx2/src/RxSingle.kt index c7ad606eb6..27842a21ae 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxSingle.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxSingle.kt @@ -60,6 +60,7 @@ private class RxSingleCoroutine( } } +/** @suppress */ @Deprecated( message = "CoroutineScope.rxSingle is deprecated in favour of top-level rxSingle", level = DeprecationLevel.HIDDEN, diff --git a/reactive/kotlinx-coroutines-rx3/README.md b/reactive/kotlinx-coroutines-rx3/README.md index f9d3c5a86d..1530558ce2 100644 --- a/reactive/kotlinx-coroutines-rx3/README.md +++ b/reactive/kotlinx-coroutines-rx3/README.md @@ -25,8 +25,8 @@ Suspending extension functions and suspending iteration: | **Name** | **Description** | -------- | --------------- | [CompletableSource.await][io.reactivex.rxjava3.core.CompletableSource.await] | Awaits for completion of the completable value -| [MaybeSource.await][io.reactivex.rxjava3.core.MaybeSource.await] | Awaits for the value of the maybe and returns it or null -| [MaybeSource.awaitOrDefault][io.reactivex.rxjava3.core.MaybeSource.awaitOrDefault] | Awaits for the value of the maybe and returns it or default +| [MaybeSource.awaitSingle][io.reactivex.rxjava3.core.MaybeSource.awaitSingle] | Awaits for the value of the maybe and returns it or throws an exception +| [MaybeSource.awaitSingleOrNull][io.reactivex.rxjava3.core.MaybeSource.awaitSingleOrNull] | Awaits for the value of the maybe and returns it or null | [SingleSource.await][io.reactivex.rxjava3.core.SingleSource.await] | Awaits for completion of the single value and returns it | [ObservableSource.awaitFirst][io.reactivex.rxjava3.core.ObservableSource.awaitFirst] | Awaits for the first value from the given observable | [ObservableSource.awaitFirstOrDefault][io.reactivex.rxjava3.core.ObservableSource.awaitFirstOrDefault] | Awaits for the first value from the given observable or default @@ -69,21 +69,21 @@ Conversion functions: [rxSingle]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/rx-single.html [rxObservable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/rx-observable.html [rxFlowable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/rx-flowable.html -[Flow.asFlowable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/kotlinx.coroutines.flow.-flow/as-flowable.html -[Flow.asObservable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/kotlinx.coroutines.flow.-flow/as-observable.html -[ObservableSource.asFlow]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/io.reactivex.rxjava3.core.-observable-source/as-flow.html -[io.reactivex.rxjava3.core.CompletableSource.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/io.reactivex.rxjava3.core.-completable-source/await.html -[io.reactivex.rxjava3.core.MaybeSource.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/io.reactivex.rxjava3.core.-maybe-source/await.html -[io.reactivex.rxjava3.core.MaybeSource.awaitOrDefault]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/io.reactivex.rxjava3.core.-maybe-source/await-or-default.html -[io.reactivex.rxjava3.core.SingleSource.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/io.reactivex.rxjava3.core.-single-source/await.html -[io.reactivex.rxjava3.core.ObservableSource.awaitFirst]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/io.reactivex.rxjava3.core.-observable-source/await-first.html -[io.reactivex.rxjava3.core.ObservableSource.awaitFirstOrDefault]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/io.reactivex.rxjava3.core.-observable-source/await-first-or-default.html -[io.reactivex.rxjava3.core.ObservableSource.awaitFirstOrElse]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/io.reactivex.rxjava3.core.-observable-source/await-first-or-else.html -[io.reactivex.rxjava3.core.ObservableSource.awaitFirstOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/io.reactivex.rxjava3.core.-observable-source/await-first-or-null.html -[io.reactivex.rxjava3.core.ObservableSource.awaitSingle]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/io.reactivex.rxjava3.core.-observable-source/await-single.html -[kotlinx.coroutines.Job.asCompletable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/kotlinx.coroutines.-job/as-completable.html -[kotlinx.coroutines.Deferred.asSingle]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/kotlinx.coroutines.-deferred/as-single.html -[io.reactivex.rxjava3.core.Scheduler.asCoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/io.reactivex.rxjava3.core.-scheduler/as-coroutine-dispatcher.html +[Flow.asFlowable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/as-flowable.html +[Flow.asObservable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/as-observable.html +[ObservableSource.asFlow]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/as-flow.html +[io.reactivex.rxjava3.core.CompletableSource.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/await.html +[io.reactivex.rxjava3.core.MaybeSource.awaitSingle]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/await-single.html +[io.reactivex.rxjava3.core.MaybeSource.awaitSingleOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/await-single-or-null.html +[io.reactivex.rxjava3.core.SingleSource.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/await.html +[io.reactivex.rxjava3.core.ObservableSource.awaitFirst]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/await-first.html +[io.reactivex.rxjava3.core.ObservableSource.awaitFirstOrDefault]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/await-first-or-default.html +[io.reactivex.rxjava3.core.ObservableSource.awaitFirstOrElse]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/await-first-or-else.html +[io.reactivex.rxjava3.core.ObservableSource.awaitFirstOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/await-first-or-null.html +[io.reactivex.rxjava3.core.ObservableSource.awaitSingle]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/await-single.html +[kotlinx.coroutines.Job.asCompletable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/as-completable.html +[kotlinx.coroutines.Deferred.asSingle]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/as-single.html +[io.reactivex.rxjava3.core.Scheduler.asCoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx3/kotlinx.coroutines.rx3/as-coroutine-dispatcher.html diff --git a/reactive/kotlinx-coroutines-rx3/build.gradle b/reactive/kotlinx-coroutines-rx3/build.gradle index 1760270d76..15ef66da18 100644 --- a/reactive/kotlinx-coroutines-rx3/build.gradle +++ b/reactive/kotlinx-coroutines-rx3/build.gradle @@ -1,6 +1,8 @@ /* * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ +import org.jetbrains.dokka.gradle.DokkaTaskPartial + targetCompatibility = JavaVersion.VERSION_1_8 dependencies { @@ -18,10 +20,12 @@ compileKotlin { kotlinOptions.jvmTarget = "1.8" } -tasks.withType(dokka.getClass()) { - externalDocumentationLink { - url = new URL('http://reactivex.io/RxJava/3.x/javadoc/') - packageListUrl = projectDir.toPath().resolve("package.list").toUri().toURL() +tasks.withType(DokkaTaskPartial.class) { + dokkaSourceSets.configureEach { + externalDocumentationLink { + url.set(new URL('http://reactivex.io/RxJava/3.x/javadoc/')) + packageListUrl.set(projectDir.toPath().resolve("package.list").toUri().toURL()) + } } } diff --git a/reactive/kotlinx-coroutines-rx3/src/RxAwait.kt b/reactive/kotlinx-coroutines-rx3/src/RxAwait.kt index a4435b88e2..2a14cf7c6c 100644 --- a/reactive/kotlinx-coroutines-rx3/src/RxAwait.kt +++ b/reactive/kotlinx-coroutines-rx3/src/RxAwait.kt @@ -76,6 +76,8 @@ public suspend fun MaybeSource.awaitSingle(): T = awaitSingleOrNull() ?: * * Deprecated in favor of [awaitSingleOrNull] in order to reflect that `null` can be returned to denote the absence of * a value, as opposed to throwing in such case. + * + * @suppress */ @Deprecated( message = "Deprecated in favor of awaitSingleOrNull()", @@ -97,6 +99,8 @@ public suspend fun MaybeSource.await(): T? = awaitSingleOrNull() * * Deprecated in favor of [awaitSingleOrNull] for naming consistency (see the deprecation of [MaybeSource.await] for * details). + * + * @suppress */ @Deprecated( message = "Deprecated in favor of awaitSingleOrNull()", diff --git a/reactive/kotlinx-coroutines-rx3/src/RxConvert.kt b/reactive/kotlinx-coroutines-rx3/src/RxConvert.kt index dfe4c055f8..b4693a55e7 100644 --- a/reactive/kotlinx-coroutines-rx3/src/RxConvert.kt +++ b/reactive/kotlinx-coroutines-rx3/src/RxConvert.kt @@ -139,6 +139,7 @@ public fun Flow.asObservable(context: CoroutineContext = EmptyCorout public fun Flow.asFlowable(context: CoroutineContext = EmptyCoroutineContext): Flowable = Flowable.fromPublisher(asPublisher(context)) +/** @suppress */ @Suppress("UNUSED") // KT-42513 @JvmOverloads // binary compatibility @JvmName("from") @@ -146,6 +147,7 @@ public fun Flow.asFlowable(context: CoroutineContext = EmptyCoroutin public fun Flow._asFlowable(context: CoroutineContext = EmptyCoroutineContext): Flowable = asFlowable(context) +/** @suppress */ @Suppress("UNUSED") // KT-42513 @JvmOverloads // binary compatibility @JvmName("from") diff --git a/settings.gradle b/settings.gradle index 37fa6757cf..44effa7c20 100644 --- a/settings.gradle +++ b/settings.gradle @@ -10,6 +10,11 @@ pluginManagement { id "net.ltgt.apt" version "0.21" id "me.champeau.gradle.jmh" version "0.5.2" } + + repositories { + maven { url "https://maven.pkg.jetbrains.space/kotlin/p/dokka/dev/" } + gradlePluginPortal() + } } rootProject.name = 'kotlinx.coroutines' @@ -31,7 +36,6 @@ include "kotlinx-coroutines-core" module('kotlinx-coroutines-test') module('kotlinx-coroutines-debug') -module('stdlib-stubs') module('kotlinx-coroutines-bom') @@ -52,10 +56,8 @@ if (JavaVersion.current().isJava11Compatible()) { } module('ui/kotlinx-coroutines-swing') -module('js/js-stub') if (!build_snapshot_train) { module('js/example-frontend-js') - include('site') } module('integration-testing') diff --git a/site/README.md b/site/README.md deleted file mode 100644 index 7ffb4103fc..0000000000 --- a/site/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Reference documentation site - -This module builds references documentation. - -## Building docs - -* Install [Docker](https://www.docker.com/) -* In the project root directory run `./gradlew site` -* The resulting HTML pages are generated in `site/build/dist` -* For continuous testing of the documentation run `./gradlew serve` and navigate - to the URL that is printed on the screen - * Update the docs via `./gradlew copyDocs` while `serve` is running - -For release use [`deploy.sh`](deploy.sh) that performs clean build of the site and pushes the results -into `gh-pages` branch of the project. \ No newline at end of file diff --git a/site/build.gradle.kts b/site/build.gradle.kts deleted file mode 100644 index 003ae76262..0000000000 --- a/site/build.gradle.kts +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -import groovy.lang.* - -/* - * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -val buildDocsDir = "$buildDir/docs" -val jekyllDockerImage = "jekyll/jekyll:${version("jekyll")}" - -val copyDocs by tasks.registering(Copy::class) { - val dokkaTasks = rootProject.getTasksByName("dokka", true) - - from(dokkaTasks.map { "${it.project.buildDir}/dokka" }) { - include("**/*.md") - include("**/package-list") - } - from("docs") - into(buildDocsDir) - filter { it.replace("/index.md\"", "/index.html\"") } - - dependsOn(dokkaTasks) -} - -val copyExampleFrontendJs by tasks.registering(Copy::class) { - val srcBuildDir = project(":example-frontend-js").buildDir - from("$srcBuildDir/dist") - into("$buildDocsDir/example-frontend-js") - - dependsOn(":example-frontend-js:browserDistribution") -} - -tasks.register("site") { - description = "Generate github pages" - - inputs.files(fileTree(buildDocsDir)) - outputs.dir("$buildDir/dist") - workingDir = file(buildDocsDir) - - commandLine( - "docker", "run", "--rm", "--volume=$buildDir:/srv/jekyll", - "-t", jekyllDockerImage, - "/bin/bash", "-c", "cd docs; jekyll build" - ) - - dependsOn(copyDocs) - dependsOn(copyExampleFrontendJs) -} - -// A useful task for local debugging -- serves a site locally -tasks.register("serve") { - commandLine( - "docker", "run", "--rm", "--volume=$buildDir:/srv/jekyll", - "-p", "8080:8080", - "-t", jekyllDockerImage, - "/bin/bash", "-c", "cd docs; jekyll serve --host 0.0.0.0 --port 8080" - ) - - dependsOn(copyDocs) - dependsOn(copyExampleFrontendJs) -} - -tasks.register("clean") { - delete(buildDir) -} - diff --git a/site/deploy.sh b/site/deploy.sh index fd510aae0b..a04e49258d 100755 --- a/site/deploy.sh +++ b/site/deploy.sh @@ -10,8 +10,8 @@ set -e # Directories SITE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" ROOT_DIR="$SITE_DIR/.." -BUILD_DIR="$SITE_DIR/build" -DIST_DIR="$BUILD_DIR/dist" +BUILD_DIR="$ROOT_DIR/build" +DIST_DIR="$BUILD_DIR/dokka/htmlMultiModule" PAGES_DIR="$BUILD_DIR/pages" # Init options @@ -28,7 +28,7 @@ else fi # Makes sure that site is built -"$ROOT_DIR/gradlew" $GRADLE_OPT site +"$ROOT_DIR/gradlew" $GRADLE_OPT dokkaHtmlMultiModule # Cleanup dist directory (and ignore errors) rm -rf "$PAGES_DIR" || true @@ -46,28 +46,8 @@ cd "$PAGES_DIR" # Remove non-.html files git rm `find . -type f -not -name '*.html' -not -name '.git'` > /dev/null -# Replace "experimental" .html files with links to the corresponding non-experimental version -# or remove them if there is no corresponding non-experimental file -echo "Redirecting experimental pages" -git_add=() -git_rm=() -`find . -type f -name '*.html'` | while read file -do - match=nothing_is_found - if [[ $file =~ \.experimental ]] ; then - match="${file//\.experimental/}" - fi - if [[ -f "$DIST_DIR/$match" ]] ; then - # redirect to non-experimental version - echo "" > "$file" - git_add+=("$file") - else - # redirect not found -- remove the file - git_rm+=("$file") - fi -done -git add "${git_add[@]}" -git rm "${git_rm[@]}" > /dev/null +# Remove all the old documentation +git rm -r * > /dev/null # Copy new documentation from dist cp -r "$DIST_DIR"/* "$PAGES_DIR" diff --git a/site/docs/_config.yml b/site/docs/_config.yml deleted file mode 100644 index d7617e1ba1..0000000000 --- a/site/docs/_config.yml +++ /dev/null @@ -1,14 +0,0 @@ -# Jekyll configuration file -title: kotlinx.coroutines -description: Library support for kotlin coroutines -baseurl: "/kotlinx.coroutines" -url: "https://kotlin.github.io" - -# Dirs -source: . -destination: ../dist - -# Build settings -markdown: kramdown -include: - - package-list diff --git a/site/docs/_includes/footer.html b/site/docs/_includes/footer.html deleted file mode 100644 index b115703c01..0000000000 --- a/site/docs/_includes/footer.html +++ /dev/null @@ -1,3 +0,0 @@ -
- -
diff --git a/site/docs/_includes/head.html b/site/docs/_includes/head.html deleted file mode 100644 index ca3227d624..0000000000 --- a/site/docs/_includes/head.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - {% if page.title %}{{ page.title | escape }}{% else %}{{ site.title | escape }}{% endif %} - - - - - - - diff --git a/site/docs/_includes/header.html b/site/docs/_includes/header.html deleted file mode 100644 index b250a17723..0000000000 --- a/site/docs/_includes/header.html +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/site/docs/_layouts/api.html b/site/docs/_layouts/api.html deleted file mode 100644 index 0f93a6b213..0000000000 --- a/site/docs/_layouts/api.html +++ /dev/null @@ -1,14 +0,0 @@ - - - {% include head.html %} - - {% include header.html %} -
-
- {{ content }} -
-
- {% include footer.html %} - - - diff --git a/site/docs/_sass/_api.scss b/site/docs/_sass/_api.scss deleted file mode 100644 index ae41149586..0000000000 --- a/site/docs/_sass/_api.scss +++ /dev/null @@ -1,225 +0,0 @@ - -// ----------------- Bits and pieces from kotlinlang.org reference ----------------- - -body { - -webkit-font-smoothing: antialiased; - font-smoothing: antialiased; - text-rendering: optimizeLegibility; -} - -a { - text-decoration: underline; -} - -$vertical-rhythm-unit: 15px !global; - -h1 { - margin-top: $vertical-rhythm-unit * 2; - margin-bottom: $vertical-rhythm-unit; - font-size: 30px; - line-height: 33px; - - &:first-of-type { - margin-top: 0; - margin-bottom: $vertical-rhythm-unit * 2; - } - - @media print { - page-break-before: always; - page-break-after: avoid; - } - - &%_section-title { - padding-top: 140px; - margin-bottom: 45px; - font-size: 55px; - line-height: 65px; - font-weight: bold; - } -} - -h2 { - margin-top: $vertical-rhythm-unit * 2; - margin-bottom: $vertical-rhythm-unit; - font-size: 24px; - line-height: 27px; - - &:first-of-type { - margin-top: 0; - } - - @media print { - page-break-after: avoid; - } -} - -h3 { - margin-top: $vertical-rhythm-unit * 2; - margin-bottom: $vertical-rhythm-unit; - font-size: 19px; - line-height: 22px; - - @media print { - page-break-after: avoid; - } -} - -h4 { - margin-top: $vertical-rhythm-unit * 2; - margin-bottom: $vertical-rhythm-unit; - font-size: 16px; - line-height: 20px; - font-weight: bold; - - @media print { - page-break-after: avoid; - } -} - -h5 { - margin-top: $vertical-rhythm-unit * 2; - margin-bottom: $vertical-rhythm-unit; - font-size: 16px; - line-height: 20px; - font-weight: normal; - - @media print { - page-break-after: avoid; - } -} - -caption {margin: 0;} - -$vertical-rhythm-unit: 15px !global; - -// tables - -table { - margin-bottom: $vertical-rhythm-unit*2; - line-height: inherit; - font-size: inherit; - border: 1px solid #dcdcdc; - - // Remove most spacing between table cells - border-collapse: collapse; - border-spacing: 0; - - &.zebra { - tbody tr:nth-child(odd) { - background-color: #f5f5f5; - } - } - - &.wide { - min-width: 100%; - } - - // Table header - thead { - background-color: #F7F7F7; - border-bottom-width: 2px; - } - - // Table footer - tfoot { - color: #ccc; - - tr {border-bottom: none;} - } - - // Row - tr { - border-bottom: 1px solid #dcdcdc; - } - - // Header cell - th { - padding-top: 10px; - padding-bottom: 6px; - text-align: left; - font-weight: bold; - } - - // Cell - th, - td { - padding: 6px 10px; - vertical-align: top; - - &:first-child { - padding-left: 12px; - } - - &:last-child { - padding-right: 12px; - } - } - - // ??? - p:last-child, - pre:last-child { - margin-bottom: 0; - } -} - -.api-docs-breadcrumbs { - margin-bottom: 25px; -} - -// code - -$font-family-mono: 'Liberation Mono', Consolas, Menlo, Courier, monospace !global; -$code-background: #efefef; - -pre { - background-color: $code-background; - overflow: auto; -} - -code { - font-family: $font-family-mono; - font-style: normal; - background-color: $code-background; -} - -code :target { - background-color: #FFFFCC; -} - -// kotlin syntax highlight - -.signature { - background-color: $code-background; - padding: 4px; -} - -.keyword { - color: #0000C0; -} - -.summarizedTypeName { - background-color: lightcyan; - font-style: italic; -} - -.parameterName { - font-weight: bold; -} - -// MPP projects - -.tags { - display: flex; -} - -.tags__tag { - color: white; - font-weight: bold; - text-transform: uppercase; - background: #a7a7a7; - padding: 0 7px; - font-size: 10px; - border-radius: 9px; - line-height: 18px; - margin-right: 5px; -} \ No newline at end of file diff --git a/site/docs/_sass/_base.scss b/site/docs/_sass/_base.scss deleted file mode 100644 index b8d70d526b..0000000000 --- a/site/docs/_sass/_base.scss +++ /dev/null @@ -1,97 +0,0 @@ -// Bits and pieces from Minima Jekyll Layout -// The MIT License (MIT) Copyright (c) 2016 Parker Moor - -// Reset some basic elements -body, h1, h2, h3, h4, h5, h6, -p, blockquote, pre, hr, -dl, dd, ol, ul, figure { - margin: 0; - padding: 0; -} - -// Basic styling -body { - font: $base-font-weight #{$base-font-size}/#{$base-line-height} $base-font-family; - color: $text-color; - background-color: $background-color; - -webkit-text-size-adjust: 100%; - -webkit-font-feature-settings: "kern" 1; - -moz-font-feature-settings: "kern" 1; - -o-font-feature-settings: "kern" 1; - font-feature-settings: "kern" 1; - font-kerning: normal; -} - -// Set `margin-bottom` to maintain vertical rhythm -h1, h2, h3, h4, h5, h6, -p, blockquote, pre, -ul, ol, dl, figure, -%vertical-rhythm { - margin-bottom: $spacing-unit / 2; -} - -// Lists -ul, ol { - margin-left: $spacing-unit; -} - -li { - > ul, - > ol { - margin-bottom: 0; - } -} - -// Links -a { - color: $brand-color; - text-decoration: none; - - &:visited { - color: darken($brand-color, 15%); - } - - &:hover { - color: $text-color; - text-decoration: underline; - } -} - -// Blockquotes -blockquote { - color: $grey-color; - border-left: 4px solid $grey-color-light; - padding-left: $spacing-unit / 2; - font-size: 18px; - letter-spacing: -1px; - font-style: italic; - - > :last-child { - margin-bottom: 0; - } -} - -// Wrapper -.wrapper { - max-width: -webkit-calc(#{$content-width} - (#{$spacing-unit} * 2)); - max-width: calc(#{$content-width} - (#{$spacing-unit} * 2)); - margin-right: auto; - margin-left: auto; - padding-right: $spacing-unit; - padding-left: $spacing-unit; - @extend %clearfix; - - @include media-query($on-laptop) { - max-width: -webkit-calc(#{$content-width} - (#{$spacing-unit})); - max-width: calc(#{$content-width} - (#{$spacing-unit})); - padding-right: $spacing-unit / 2; - padding-left: $spacing-unit / 2; - } -} - -// Clearfix -%clearfix:after { - content: ""; - display: table; - clear: both; -} diff --git a/site/docs/_sass/_layout.scss b/site/docs/_sass/_layout.scss deleted file mode 100644 index d85d0592ef..0000000000 --- a/site/docs/_sass/_layout.scss +++ /dev/null @@ -1,37 +0,0 @@ -// Bits and pieces from Minima Jekyll Layout -// The MIT License (MIT) Copyright (c) 2016 Parker Moor - -// Site header -.site-header { - border-top: 5px solid $grey-color-dark; - border-bottom: 1px solid $grey-color-light; - min-height: 56px; - - // Positioning context for the mobile navigation icon - position: relative; -} - -.site-title { - font-size: 26px; - font-weight: 300; - line-height: 56px; - letter-spacing: -1px; - margin-bottom: 0; - float: left; - - &, - &:visited { - color: $grey-color-dark; - } -} -// Site footer -.site-footer { - border-top: 1px solid $grey-color-light; - padding: $spacing-unit 0; -} - -// Page content -.page-content { - padding: $spacing-unit 0; -} - diff --git a/site/docs/_sass/_minima.scss b/site/docs/_sass/_minima.scss deleted file mode 100644 index 86079f8529..0000000000 --- a/site/docs/_sass/_minima.scss +++ /dev/null @@ -1,35 +0,0 @@ -// Bits and pieces from Minima Jekyll Layout -// The MIT License (MIT) Copyright (c) 2016 Parker Moor - -// Define defaults for each variable. - -$base-font-family: "Helvetica Neue", Helvetica, Arial, sans-serif !default; -$base-font-size: 16px !default; -$base-font-weight: 400 !default; -$small-font-size: $base-font-size * 0.875 !default; -$base-line-height: 1.5 !default; - -$spacing-unit: 30px !default; - -$text-color: #111 !default; -$background-color: #fdfdfd !default; -$brand-color: #2a7ae2 !default; - -$grey-color: #828282 !default; -$grey-color-light: lighten($grey-color, 40%) !default; -$grey-color-dark: darken($grey-color, 25%) !default; - -// Width of the content area -$content-width: 800px !default; - -$on-palm: 600px !default; -$on-laptop: 800px !default; - -@mixin media-query($device) { - @media screen and (max-width: $device) { - @content; - } -} - -// Import partials. -@import "base", "layout"; diff --git a/site/docs/assets/js/api.js b/site/docs/assets/js/api.js deleted file mode 100644 index 08f41fa087..0000000000 --- a/site/docs/assets/js/api.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - - -function addTag(rowElement, tag) { - var tags = $(rowElement).find('.tags'); - if (tags.length == 0) { - tags = $('
'); - $(rowElement).find('td:first').append(tags); - } - tags.append('
' + tag + '
') -} - -$(document).ready(function () { - $('[data-platform]').each(function (ind, element) { - var platform = element.getAttribute('data-platform'); - addTag(element, platform) - }); -}); diff --git a/site/docs/assets/main.scss b/site/docs/assets/main.scss deleted file mode 100644 index 1be8487fb4..0000000000 --- a/site/docs/assets/main.scss +++ /dev/null @@ -1,36 +0,0 @@ ---- -# Only the main Sass file needs front matter (the dashes are enough) ---- -@charset "utf-8"; - -// Sans Serif -@import url('//fonts.googleapis.com/css?family=Open+Sans:300,300italic,400italic,700italic,400,700'); - -// Our variables -$base-font-family: "Open Sans", Helvetica, Arial, sans-serif; -$base-font-size: 14px; -$base-font-weight: 400; -$small-font-size: $base-font-size * 0.875; -$base-line-height: 20px; - -$spacing-unit: 30px; - -$text-color: #111; -$background-color: #fdfdfd; -$brand-color: #2a7ae2; - -$grey-color: #828282; -$grey-color-light: lighten($grey-color, 40%); -$grey-color-dark: darken($grey-color, 25%); - -// Width of the content area -$content-width: 800px; - -$on-palm: 600px; -$on-laptop: 800px; - -// Import partials from the `minima` theme. -@import "minima"; - -// Import api reference styles -@import "api"; diff --git a/site/docs/index.md b/site/docs/index.md deleted file mode 100644 index 92eb09483e..0000000000 --- a/site/docs/index.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: kotlinx-coroutines -layout: api ---- - -# kotlinx.coroutines reference documentation - -Library support for Kotlin coroutines. This reference is a companion to -[Guide to kotlinx.coroutines by example](https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md). - -## Modules - -| Name | Description | -| ---------------------------------------------------------- | ------------------------------------------------ | -| [kotlinx-coroutines-core](kotlinx-coroutines-core) | Core primitives to work with coroutines | -| [kotlinx-coroutines-debug](kotlinx-coroutines-debug) | Debugging utilities for coroutines | -| [kotlinx-coroutines-test](kotlinx-coroutines-test) | Test primitives for coroutines, `Main` dispatcher injection | -| [kotlinx-coroutines-reactive](kotlinx-coroutines-reactive) | Utilities for [Reactive Streams](https://www.reactive-streams.org) | -| [kotlinx-coroutines-reactor](kotlinx-coroutines-reactor) | Utilities for [Reactor](https://projectreactor.io) | -| [kotlinx-coroutines-rx2](kotlinx-coroutines-rx2) | Utilities for [RxJava 2.x](https://github.com/ReactiveX/RxJava) | -| [kotlinx-coroutines-rx3](kotlinx-coroutines-rx3) | Utilities for [RxJava 3.x](https://github.com/ReactiveX/RxJava) | -| [kotlinx-coroutines-android](kotlinx-coroutines-android) | `Main` dispatcher for Android applications | -| [kotlinx-coroutines-javafx](kotlinx-coroutines-javafx) | `JavaFx` dispatcher for JavaFX UI applications | -| [kotlinx-coroutines-swing](kotlinx-coroutines-swing) | `Swing` dispatcher for Swing UI applications | -| [kotlinx-coroutines-jdk8](kotlinx-coroutines-jdk8) | Integration with JDK8 `CompletableFuture` (Android API level 24) | -| [kotlinx-coroutines-guava](kotlinx-coroutines-guava) | Integration with Guava [ListenableFuture](https://github.com/google/guava/wiki/ListenableFutureExplained) | -| [kotlinx-coroutines-slf4j](kotlinx-coroutines-slf4j) | Integration with SLF4J [MDC](https://logback.qos.ch/manual/mdc.html) | -| [kotlinx-coroutines-play-services](kotlinx-coroutines-play-services) | Integration with Google Play Services [Tasks API](https://developers.google.com/android/guides/tasks) | - -## Examples - -* [example-frontend-js](example-frontend-js/index.html) -- frontend application written in Kotlin/JS -that uses coroutines to implement animations in imperative style. diff --git a/stdlib-stubs/README.md b/stdlib-stubs/README.md deleted file mode 100644 index f47bccc8da..0000000000 --- a/stdlib-stubs/README.md +++ /dev/null @@ -1 +0,0 @@ -This is a workaround for Dokka to generate proper references for Kotlin 1.3 API. \ No newline at end of file diff --git a/stdlib-stubs/build.gradle.kts b/stdlib-stubs/build.gradle.kts deleted file mode 100644 index 201ac43cb0..0000000000 --- a/stdlib-stubs/build.gradle.kts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - -tasks.named("compileKotlin") { - kotlinOptions { - freeCompilerArgs += "-Xallow-kotlin-package" - } -} diff --git a/stdlib-stubs/src/Continuation.kt b/stdlib-stubs/src/Continuation.kt deleted file mode 100644 index 66d672afea..0000000000 --- a/stdlib-stubs/src/Continuation.kt +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ -package kotlin.coroutines - -// DOKKA STUB -public interface Continuation { - public val context: CoroutineContext - public fun resumeWith(result: Result) -} diff --git a/stdlib-stubs/src/ContinuationInterceptor.kt b/stdlib-stubs/src/ContinuationInterceptor.kt deleted file mode 100644 index 5c96e82dff..0000000000 --- a/stdlib-stubs/src/ContinuationInterceptor.kt +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ -package kotlin.coroutines - -// DOKKA STUB -public interface ContinuationInterceptor : CoroutineContext.Element { - public companion object Key : CoroutineContext.Key - public fun interceptContinuation(continuation: Continuation): Continuation - public fun releaseInterceptedContinuation(continuation: Continuation<*>): Continuation<*> { - return continuation - } - public override operator fun get(key: CoroutineContext.Key): E? = TODO() - public override fun minusKey(key: CoroutineContext.Key<*>): CoroutineContext = TODO() -} diff --git a/stdlib-stubs/src/CoroutineContext.kt b/stdlib-stubs/src/CoroutineContext.kt deleted file mode 100644 index 6861198da5..0000000000 --- a/stdlib-stubs/src/CoroutineContext.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ -package kotlin.coroutines - -// DOKKA STUB -public interface CoroutineContext { - public operator fun get(key: Key): E? - public fun fold(initial: R, operation: (R, Element) -> R): R - public operator fun plus(context: CoroutineContext): CoroutineContext = TODO() - public fun minusKey(key: Key<*>): CoroutineContext - public interface Key - public interface Element : CoroutineContext { - public val key: Key<*> - - public override operator fun get(key: Key): E? = - @Suppress("UNCHECKED_CAST") - if (this.key == key) this as E else null - - public override fun fold(initial: R, operation: (R, Element) -> R): R = - operation(initial, this) - - public override fun minusKey(key: Key<*>): CoroutineContext = - if (this.key == key) EmptyCoroutineContext else this - } -} - -public object EmptyCoroutineContext : CoroutineContext { - private const val serialVersionUID: Long = 0 - private fun readResolve(): Any = EmptyCoroutineContext - - public override fun get(key: CoroutineContext.Key): E? = null - public override fun fold(initial: R, operation: (R, CoroutineContext.Element) -> R): R = initial - public override fun plus(context: CoroutineContext): CoroutineContext = context - public override fun minusKey(key: CoroutineContext.Key<*>): CoroutineContext = this - public override fun hashCode(): Int = 0 - public override fun toString(): String = "EmptyCoroutineContext" -} diff --git a/stdlib-stubs/src/Result.kt b/stdlib-stubs/src/Result.kt deleted file mode 100644 index d873ac8902..0000000000 --- a/stdlib-stubs/src/Result.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlin - -public interface Result { - public val value: T - public val isSuccess: Boolean - public val isFailure: Boolean - public fun exceptionOrNull(): Throwable? - public fun getOrNull(): T? - public fun getOrThrow(): T -} diff --git a/ui/coroutines-guide-ui.md b/ui/coroutines-guide-ui.md index bc974f6c11..408a43d1e1 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.5.0" +implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.1" ``` You can clone [kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) project from GitHub onto your @@ -310,7 +310,7 @@ processing the previous one. The [actor] coroutine builder accepts an optional controls the implementation of the channel that this actor is using for its mailbox. The description of all the available choices is given in documentation of the [`Channel()`][Channel] factory function. -Let us change the code to use a conflated channel by passing [Channel.CONFLATED] capacity value. The +Let us change the code to use a conflated channel by passing [Channel.CONFLATED][Channel.Factory.CONFLATED] capacity value. The change is only to the line that creates an actor: ```kotlin @@ -611,14 +611,14 @@ After delay [launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html [delay]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/delay.html [Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html -[Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/cancel.html +[Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/cancel.html [CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html [MainScope()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-main-scope.html [withContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-context.html [Dispatchers.Default]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html [CoroutineStart]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-start/index.html [async]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html -[CoroutineStart.UNDISPATCHED]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-start/-u-n-d-i-s-p-a-t-c-h-e-d.html +[CoroutineStart.UNDISPATCHED]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-start/-u-n-d-i-s-p-a-t-c-h-e-d/index.html @@ -626,12 +626,12 @@ After delay [SendChannel.trySend]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/try-send.html [SendChannel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/index.html [Channel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-channel/index.html -[Channel.CONFLATED]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-channel/-c-o-n-f-l-a-t-e-d.html +[Channel.Factory.CONFLATED]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-channel/-factory/-c-o-n-f-l-a-t-e-d.html -[kotlinx.coroutines.Dispatchers.JavaFx]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-javafx/kotlinx.coroutines.javafx/kotlinx.coroutines.-dispatchers/-java-fx.html +[kotlinx.coroutines.Dispatchers.JavaFx]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-javafx/kotlinx.coroutines.javafx/-java-fx.html diff --git a/ui/kotlinx-coroutines-android/android-unit-tests/build.gradle.kts b/ui/kotlinx-coroutines-android/android-unit-tests/build.gradle.kts index 18adf4bd1a..2fd0b81479 100644 --- a/ui/kotlinx-coroutines-android/android-unit-tests/build.gradle.kts +++ b/ui/kotlinx-coroutines-android/android-unit-tests/build.gradle.kts @@ -3,6 +3,7 @@ */ dependencies { + kotlinCompilerPluginClasspathMain(project(":kotlinx-coroutines-core")) testImplementation("com.google.android:android:${version("android")}") testImplementation("org.robolectric:robolectric:${version("robolectric")}") testImplementation(project(":kotlinx-coroutines-test")) diff --git a/ui/kotlinx-coroutines-android/build.gradle.kts b/ui/kotlinx-coroutines-android/build.gradle.kts index 2af2d4f3a9..9cec1dc9f0 100644 --- a/ui/kotlinx-coroutines-android/build.gradle.kts +++ b/ui/kotlinx-coroutines-android/build.gradle.kts @@ -18,7 +18,7 @@ dependencies { testImplementation("org.robolectric:robolectric:${version("robolectric")}") testImplementation("org.smali:baksmali:${version("baksmali")}") - "r8"("com.android.tools.build:builder:7.0.0-alpha14") + "r8"("com.android.tools.build:builder:7.1.0-alpha01") } val optimizedDexDir = File(buildDir, "dex-optim/") @@ -41,24 +41,65 @@ val runR8NoOptim by tasks.registering(RunR8::class) { dependsOn("jar") } -// TODO: Disable the test until we have published version of R8 that supports Kotlin 1.5.0 metadata - -//tasks.test { -// // Ensure the R8-processed dex is built and supply its path as a property to the test. -// dependsOn(runR8) -// dependsOn(runR8NoOptim) -// -// inputs.files(optimizedDexFile, unOptimizedDexFile) -// -// systemProperty("dexPath", optimizedDexFile.absolutePath) -// systemProperty("noOptimDexPath", unOptimizedDexFile.absolutePath) -// -// // Output custom metric with the size of the optimized dex -// doLast { -// println("##teamcity[buildStatisticValue key='optimizedDexSize' value='${optimizedDexFile.length()}']") -// } -//} +tasks.test { + // Ensure the R8-processed dex is built and supply its path as a property to the test. + dependsOn(runR8) + dependsOn(runR8NoOptim) + + inputs.files(optimizedDexFile, unOptimizedDexFile) + + systemProperty("dexPath", optimizedDexFile.absolutePath) + systemProperty("noOptimDexPath", unOptimizedDexFile.absolutePath) + + // Output custom metric with the size of the optimized dex + doLast { + println("##teamcity[buildStatisticValue key='optimizedDexSize' value='${optimizedDexFile.length()}']") + } +} externalDocumentationLink( url = "https://developer.android.com/reference/" ) +/* + * Task used by our ui/android tests to test minification results and keep track of size of the binary. + */ +open class RunR8 : JavaExec() { + + @OutputDirectory + lateinit var outputDex: File + + @InputFile + lateinit var inputConfig: File + + @InputFile + val inputConfigCommon: File = File("testdata/r8-test-common.pro") + + @InputFiles + val jarFile: File = project.tasks.named("jar").get().archivePath + + init { + classpath = project.configurations["r8"] + main = "com.android.tools.r8.R8" + } + + override fun exec() { + // Resolve classpath only during execution + val arguments = mutableListOf( + "--release", + "--no-desugaring", + "--min-api", "26", + "--output", outputDex.absolutePath, + "--pg-conf", inputConfig.absolutePath + ) + arguments.addAll(project.configurations["runtimeClasspath"].files.map { it.absolutePath }) + arguments.add(jarFile.absolutePath) + + args = arguments + + project.delete(outputDex) + outputDex.mkdirs() + + super.exec() + } +} + diff --git a/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt b/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt index d693e2bc25..ca8dd0d0ca 100644 --- a/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt +++ b/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt @@ -7,8 +7,8 @@ package kotlinx.coroutines.android import android.os.* -import androidx.annotation.* import android.view.* +import androidx.annotation.* import kotlinx.coroutines.* import kotlinx.coroutines.internal.* import java.lang.reflect.* @@ -54,15 +54,22 @@ internal class AndroidDispatcherFactory : MainDispatcherFactory { override fun createDispatcher(allFactories: List) = HandlerContext(Looper.getMainLooper().asHandler(async = true)) - override fun hintOnError(): String? = "For tests Dispatchers.setMain from kotlinx-coroutines-test module can be used" + override fun hintOnError(): String = "For tests Dispatchers.setMain from kotlinx-coroutines-test module can be used" override val loadPriority: Int get() = Int.MAX_VALUE / 2 } /** - * Represents an arbitrary [Handler] as a implementation of [CoroutineDispatcher] + * Represents an arbitrary [Handler] as an implementation of [CoroutineDispatcher] * with an optional [name] for nicer debugging + * + * ## Rejected execution + * + * If the underlying handler is closed and its message-scheduling methods start to return `false` on + * an attempt to submit a continuation task to the resulting dispatcher, + * then the [Job] of the affected task is [cancelled][Job.cancel] and the task is submitted to the + * [Dispatchers.IO], so that the affected coroutine can cleanup its resources and promptly complete. */ @JvmName("from") // this is for a nice Java API, see issue #255 @JvmOverloads @@ -113,7 +120,7 @@ internal class HandlerContext private constructor( * @param handler a handler. * @param name an optional name for debugging. */ - public constructor( + constructor( handler: Handler, name: String? = null ) : this(handler, name, false) @@ -129,24 +136,33 @@ internal class HandlerContext private constructor( } override fun dispatch(context: CoroutineContext, block: Runnable) { - handler.post(block) + if (!handler.post(block)) { + cancelOnRejection(context, block) + } } override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation) { val block = Runnable { with(continuation) { resumeUndispatched(Unit) } } - handler.postDelayed(block, timeMillis.coerceAtMost(MAX_DELAY)) - continuation.invokeOnCancellation { handler.removeCallbacks(block) } + if (handler.postDelayed(block, timeMillis.coerceAtMost(MAX_DELAY))) { + continuation.invokeOnCancellation { handler.removeCallbacks(block) } + } else { + cancelOnRejection(continuation.context, block) + } } override fun invokeOnTimeout(timeMillis: Long, block: Runnable, context: CoroutineContext): DisposableHandle { - handler.postDelayed(block, timeMillis.coerceAtMost(MAX_DELAY)) - return object : DisposableHandle { - override fun dispose() { - handler.removeCallbacks(block) - } + if (handler.postDelayed(block, timeMillis.coerceAtMost(MAX_DELAY))) { + return DisposableHandle { handler.removeCallbacks(block) } } + cancelOnRejection(context, block) + return NonDisposableHandle + } + + private fun cancelOnRejection(context: CoroutineContext, block: Runnable) { + context.cancel(CancellationException("The task was rejected, the handler underlying the dispatcher '${toString()}' was closed")) + Dispatchers.IO.dispatch(context, block) } override fun toString(): String = toStringInternalImpl() ?: run { diff --git a/ui/kotlinx-coroutines-android/test/DisabledHandlerTest.kt b/ui/kotlinx-coroutines-android/test/DisabledHandlerTest.kt new file mode 100644 index 0000000000..a1f0a03d4a --- /dev/null +++ b/ui/kotlinx-coroutines-android/test/DisabledHandlerTest.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.android + +import android.os.* +import kotlinx.coroutines.* +import org.junit.* +import org.junit.runner.* +import org.robolectric.* +import org.robolectric.annotation.* + +@RunWith(RobolectricTestRunner::class) +@Config(manifest = Config.NONE, sdk = [28]) +class DisabledHandlerTest : TestBase() { + + private var delegateToSuper = false + private val disabledDispatcher = object : Handler() { + override fun sendMessageAtTime(msg: Message?, uptimeMillis: Long): Boolean { + if (delegateToSuper) return super.sendMessageAtTime(msg, uptimeMillis) + return false + } + }.asCoroutineDispatcher() + + @Test + fun testRunBlocking() { + expect(1) + try { + runBlocking(disabledDispatcher) { + expectUnreached() + } + expectUnreached() + } catch (e: CancellationException) { + finish(2) + } + } + + @Test + fun testInvokeOnCancellation() = runTest { + val job = launch(disabledDispatcher, start = CoroutineStart.LAZY) { expectUnreached() } + job.invokeOnCompletion { if (it != null) expect(2) } + yield() + expect(1) + job.join() + finish(3) + } + + @Test + fun testWithTimeout() = runTest { + delegateToSuper = true + try { + withContext(disabledDispatcher) { + expect(1) + delegateToSuper = false + delay(Long.MAX_VALUE - 1) + expectUnreached() + } + expectUnreached() + } catch (e: CancellationException) { + finish(2) + } + } +} diff --git a/ui/kotlinx-coroutines-android/test/HandlerDispatcherTest.kt b/ui/kotlinx-coroutines-android/test/HandlerDispatcherTest.kt index 55decde61b..5128a74caf 100644 --- a/ui/kotlinx-coroutines-android/test/HandlerDispatcherTest.kt +++ b/ui/kotlinx-coroutines-android/test/HandlerDispatcherTest.kt @@ -29,7 +29,7 @@ class HandlerDispatcherTest : TestBase() { fun mainIsAsync() = runTest { ReflectionHelpers.setStaticField(Build.VERSION::class.java, "SDK_INT", 28) - val mainLooper = ShadowLooper.getShadowMainLooper() + val mainLooper = shadowOf(Looper.getMainLooper()) mainLooper.pause() val mainMessageQueue = shadowOf(Looper.getMainLooper().queue) @@ -48,7 +48,7 @@ class HandlerDispatcherTest : TestBase() { val main = Looper.getMainLooper().asHandler(async = true).asCoroutineDispatcher() - val mainLooper = ShadowLooper.getShadowMainLooper() + val mainLooper = shadowOf(Looper.getMainLooper()) mainLooper.pause() val mainMessageQueue = shadowOf(Looper.getMainLooper().queue) @@ -67,7 +67,7 @@ class HandlerDispatcherTest : TestBase() { val main = Looper.getMainLooper().asHandler(async = true).asCoroutineDispatcher() - val mainLooper = ShadowLooper.getShadowMainLooper() + val mainLooper = shadowOf(Looper.getMainLooper()) mainLooper.pause() val mainMessageQueue = shadowOf(Looper.getMainLooper().queue) @@ -86,7 +86,7 @@ class HandlerDispatcherTest : TestBase() { val main = Looper.getMainLooper().asHandler(async = true).asCoroutineDispatcher() - val mainLooper = ShadowLooper.getShadowMainLooper() + val mainLooper = shadowOf(Looper.getMainLooper()) mainLooper.pause() val mainMessageQueue = shadowOf(Looper.getMainLooper().queue) @@ -105,7 +105,7 @@ class HandlerDispatcherTest : TestBase() { val main = Looper.getMainLooper().asHandler(async = false).asCoroutineDispatcher() - val mainLooper = ShadowLooper.getShadowMainLooper() + val mainLooper = shadowOf(Looper.getMainLooper()) mainLooper.pause() val mainMessageQueue = shadowOf(Looper.getMainLooper().queue)