diff --git a/CHANGES.md b/CHANGES.md index bf1c2e90d0..bdd5d7a6f7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,25 @@ # Change log for kotlinx.coroutines +## Version 1.3.4 + +### Flow + +* Detect missing `awaitClose` calls in `callbackFlow` to make it less error-prone when used with callbacks (#1762, #1770). This change makes `callbackFlow` **different** from `channelFlow`. +* `ReceiveChannel.asFlow` extension is introduced (#1490). +* Enforce exception transparency invariant in `flow` builder (#1657). +* Proper `Dispatcher` support in `Flow` reactive integrations (#1765). +* Batch `Subscription.request` calls in `Flow` reactive integration (#766). +* `ObservableValue.asFlow` added to JavaFx integration module (#1695). +* `ObservableSource.asFlow` added to RxJava2 integration module (#1768). + +### Other changes + +* `kotlinx-coroutines-core` is optimized for R8, making it much smaller for Android usages (75 KB for `1.3.4` release). +* Performance of `Dispatchers.Default` is improved (#1704, #1706). +* Kotlin is updated to 1.3.70. +* `CoroutineDispatcher` and `ExecutorCoroutineDispatcher` experimental coroutine context keys are introduced (#1805). +* Performance of various `Channel` operations is improved (#1565). + ## Version 1.3.3 ### Flow diff --git a/README.md b/README.md index 313cd05501..ba10d6b19d 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,10 @@ [![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://api.bintray.com/packages/kotlin/kotlinx/kotlinx.coroutines/images/download.svg?version=1.3.3) ](https://bintray.com/kotlin/kotlinx/kotlinx.coroutines/1.3.3) +[![Download](https://api.bintray.com/packages/kotlin/kotlinx/kotlinx.coroutines/images/download.svg?version=1.3.4) ](https://bintray.com/kotlin/kotlinx/kotlinx.coroutines/1.3.4) Library support for Kotlin coroutines with [multiplatform](#multiplatform) support. -This is a companion version for Kotlin `1.3.61` release. +This is a companion version for Kotlin `1.3.70` release. ```kotlin suspend fun main() = coroutineScope { @@ -82,7 +82,7 @@ Add dependencies (you can also add other modules that you need): org.jetbrains.kotlinx kotlinx-coroutines-core - 1.3.3 + 1.3.4 ``` @@ -90,7 +90,7 @@ And make sure that you use the latest Kotlin version: ```xml - 1.3.61 + 1.3.70 ``` @@ -100,7 +100,7 @@ Add dependencies (you can also add other modules that you need): ```groovy dependencies { - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.4' } ``` @@ -108,7 +108,7 @@ And make sure that you use the latest Kotlin version: ```groovy buildscript { - ext.kotlin_version = '1.3.61' + ext.kotlin_version = '1.3.70' } ``` @@ -126,7 +126,7 @@ Add dependencies (you can also add other modules that you need): ```groovy dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.4") } ``` @@ -134,7 +134,7 @@ And make sure that you use the latest Kotlin version: ```groovy plugins { - kotlin("jvm") version "1.3.61" + kotlin("jvm") version "1.3.70" } ``` @@ -145,7 +145,7 @@ Make sure that you have either `jcenter()` or `mavenCentral()` in the list of re Core modules of `kotlinx.coroutines` are also available for [Kotlin/JS](#js) and [Kotlin/Native](#native). In common code that should get compiled for different platforms, add dependency to -[`kotlinx-coroutines-core-common`](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core-common/1.3.3/jar) +[`kotlinx-coroutines-core-common`](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core-common/1.3.4/jar) (follow the link to get the dependency declaration snippet). ### Android @@ -154,7 +154,7 @@ Add [`kotlinx-coroutines-android`](ui/kotlinx-coroutines-android) module as dependency when using `kotlinx.coroutines` on Android: ```groovy -implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3' +implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.4' ``` This gives you access to Android [Dispatchers.Main] @@ -164,16 +164,13 @@ threads are handled by Android runtime. #### R8 and ProGuard -For R8 no actions required, it will take obfuscation rules from the jar. - -For Proguard you need to add options from [coroutines.pro](kotlinx-coroutines-core/jvm/resources/META-INF/proguard/coroutines.pro) to your rules manually. - -R8 is a replacement for ProGuard in Android ecosystem, it is enabled by default since Android gradle plugin 3.4.0 (3.3.0-beta also had it enabled). +R8 and ProGuard rules are bundled into the [`kotlinx-coroutines-android`](ui/kotlinx-coroutines-android) module. +For more details see ["Optimization" section for Android](ui/kotlinx-coroutines-android/README.md#optimization). ### JS [Kotlin/JS](https://kotlinlang.org/docs/reference/js-overview.html) version of `kotlinx.coroutines` is published as -[`kotlinx-coroutines-core-js`](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core-js/1.3.3/jar) +[`kotlinx-coroutines-core-js`](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core-js/1.3.4/jar) (follow the link to get the dependency declaration snippet). You can also use [`kotlinx-coroutines-core`](https://www.npmjs.com/package/kotlinx-coroutines-core) package via NPM. @@ -181,7 +178,7 @@ You can also use [`kotlinx-coroutines-core`](https://www.npmjs.com/package/kotli ### Native [Kotlin/Native](https://kotlinlang.org/docs/reference/native-overview.html) version of `kotlinx.coroutines` is published as -[`kotlinx-coroutines-core-native`](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core-native/1.3.3/jar) +[`kotlinx-coroutines-core-native`](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core-native/1.3.4/jar) (follow the link to get the dependency declaration snippet). Only single-threaded code (JS-style) on Kotlin/Native is currently supported. @@ -203,8 +200,9 @@ to Gradle (in Preferences -> Build, Execution, Deployment -> Build Tools -> Grad ### Requirements -* JDK >= 1.8 referred to by the `JAVA_HOME` environment variable. JDK must include JavaFX. +* JDK >= 11 referred to by the `JAVA_HOME` environment variable. * JDK 1.6 referred to by the `JDK_16` environment variable. It is okay to have `JDK_16` pointing to `JAVA_HOME` for external contributions. +* JDK 1.8 referred to by the `JDK_18` environment variable. Only used by nightly stress-tests. It is okay to have `JDK_16` pointing to `JAVA_HOME` for external contributions. ## Contributions and releases @@ -217,6 +215,12 @@ The `develop` branch is pushed to `master` during release. * Full release procedure checklist is [here](RELEASE.md). * Steps for contributing new integration modules are explained [here](integration/README.md#Contributing). +* Use [Knit](https://github.com/Kotlin/kotlinx-knit/blob/master/README.md) for updates to documentation: + * In project root directory run `./gradlew knit`. + * Commit updated documents and examples together with other changes. +* Use [Binary Compatibility Validator](https://github.com/Kotlin/binary-compatibility-validator/blob/master/README.md) for updates to public API: + * In project root directory run `./gradlew apiDump`. + * Commit updated API index together with other changes. diff --git a/RELEASE.md b/RELEASE.md index 22140e68c7..efb361f1e5 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -53,7 +53,8 @@ To release new `` of `kotlinx-coroutines`: * Create a release named ``. * Cut & paste lines from [`CHANGES.md`](CHANGES.md) into description. -3. Build and publish documentation for web-site:
+3. Build and publish documentation for web-site + (make sure you have [Docker](https://www.docker.com/) installed first):
`site/deploy.sh push` 4. In [Bintray](https://bintray.com/kotlin/kotlinx/kotlinx.coroutines) admin interface: diff --git a/benchmarks/build.gradle b/benchmarks/build.gradle index 157eb88aa6..a192f2795e 100644 --- a/benchmarks/build.gradle +++ b/benchmarks/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ sourceCompatibility = 1.8 targetCompatibility = 1.8 @@ -74,4 +74,6 @@ dependencies { compile "org.openjdk.jmh:jmh-core:1.21" compile 'com.typesafe.akka:akka-actor_2.12:2.5.0' compile project(':kotlinx-coroutines-core') + // add jmh dependency on main + jmh sourceSets.main.runtimeClasspath } diff --git a/benchmarks/scripts/generate_plots_flow_flatten_merge.py b/benchmarks/scripts/generate_plots_flow_flatten_merge.py new file mode 100644 index 0000000000..c7f5ebb88b --- /dev/null +++ b/benchmarks/scripts/generate_plots_flow_flatten_merge.py @@ -0,0 +1,75 @@ +# To run this script run the command 'python3 scripts/generate_plots_flow_flatten_merge.py' in the /benchmarks folder + + +import pandas as pd +import sys +import locale +import matplotlib.pyplot as plt +from matplotlib.ticker import FormatStrFormatter + +input_file = "build/reports/jmh/results.csv" +output_file = "out/flow-flatten-merge.svg" +# Please change the value of this variable according to the FlowFlattenMergeBenchmarkKt.ELEMENTS +elements = 100000 +benchmark_name = "benchmarks.flow.FlowFlattenMergeBenchmark.flattenMerge" +csv_columns = ["Benchmark", "Score", "Unit", "Param: concurrency", "Param: flowsNumberStrategy"] +rename_columns = {"Benchmark": "benchmark", "Score" : "score", "Unit" : "unit", + "Param: concurrency" : "concurrency", "Param: flowsNumberStrategy" : "flows"} + +markers = ['.', 'v', '^', '1', '2', '8', 'p', 'P', 'x', 'D', 'd', 's'] +colours = ['red', 'gold', 'sienna', 'olivedrab', 'lightseagreen', 'navy', 'blue', 'm', 'crimson', 'yellow', 'orangered', 'slateblue', 'aqua', 'black', 'silver'] + +def next_colour(): + i = 0 + while True: + yield colours[i % len(colours)] + i += 1 + +def next_marker(): + i = 0 + while True: + yield markers[i % len(markers)] + i += 1 + +def draw(data, plt): + plt.xscale('log', basex=2) + plt.gca().xaxis.set_major_formatter(FormatStrFormatter('%0.f')) + plt.grid(linewidth='0.5', color='lightgray') + if data.unit.unique()[0] != "ops/s": + print("Unexpected time unit: " + data.unit.unique()[0]) + sys.exit(1) + plt.ylabel("elements / ms") + plt.xlabel('concurrency') + plt.xticks(data.concurrency.unique()) + + colour_gen = next_colour() + marker_gen = next_marker() + for flows in data.flows.unique(): + gen_colour = next(colour_gen) + gen_marker = next(marker_gen) + res = data[(data.flows == flows)] +# plt.plot(res.concurrency, res.score*elements/1000, label="flows={}".format(flows), color=gen_colour, marker=gen_marker) + plt.errorbar(x=res.concurrency, y=res.score*elements/1000, yerr=res.score_error*elements/1000, solid_capstyle='projecting', + label="flows={}".format(flows), capsize=4, color=gen_colour, linewidth=2.2) + +langlocale = locale.getdefaultlocale()[0] +locale.setlocale(locale.LC_ALL, langlocale) +dp = locale.localeconv()['decimal_point'] +if dp == ",": + csv_columns.append("Score Error (99,9%)") + rename_columns["Score Error (99,9%)"] = "score_error" +elif dp == ".": + csv_columns.append("Score Error (99.9%)") + rename_columns["Score Error (99.9%)"] = "score_error" +else: + print("Unexpected locale delimeter: " + dp) + sys.exit(1) +data = pd.read_csv(input_file, sep=",", decimal=dp) +data = data[csv_columns].rename(columns=rename_columns) +data = data[(data.benchmark == benchmark_name)] +plt.rcParams.update({'font.size': 15}) +plt.figure(figsize=(12.5, 10)) +draw(data, plt) +plt.legend(loc='upper center', borderpad=0, bbox_to_anchor=(0.5, 1.3), ncol=2, frameon=False, borderaxespad=2, prop={'size': 15}) +plt.tight_layout(pad=12, w_pad=2, h_pad=1) +plt.savefig(output_file, bbox_inches='tight') diff --git a/benchmarks/src/jmh/java/benchmarks/flow/scrabble/RxJava2PlaysScrabble.java b/benchmarks/src/jmh/java/benchmarks/flow/scrabble/RxJava2PlaysScrabble.java index 2a85d0dbdd..04f7210381 100644 --- a/benchmarks/src/jmh/java/benchmarks/flow/scrabble/RxJava2PlaysScrabble.java +++ b/benchmarks/src/jmh/java/benchmarks/flow/scrabble/RxJava2PlaysScrabble.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.flow.scrabble; @@ -160,4 +160,4 @@ public List>> play() throws Exception { .blockingGet() ; return finalList2 ; } -} \ No newline at end of file +} diff --git a/benchmarks/src/jmh/java/benchmarks/flow/scrabble/RxJava2PlaysScrabbleOpt.java b/benchmarks/src/jmh/java/benchmarks/flow/scrabble/RxJava2PlaysScrabbleOpt.java index 7a7cb1aa4e..71c7604d0c 100644 --- a/benchmarks/src/jmh/java/benchmarks/flow/scrabble/RxJava2PlaysScrabbleOpt.java +++ b/benchmarks/src/jmh/java/benchmarks/flow/scrabble/RxJava2PlaysScrabbleOpt.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.flow.scrabble; @@ -171,4 +171,4 @@ public List>> play() throws Exception { return finalList2 ; } -} \ No newline at end of file +} diff --git a/benchmarks/src/jmh/java/benchmarks/flow/scrabble/optimizations/FlowableCharSequence.java b/benchmarks/src/jmh/java/benchmarks/flow/scrabble/optimizations/FlowableCharSequence.java index a45dbdd2c5..5f93b4ee88 100644 --- a/benchmarks/src/jmh/java/benchmarks/flow/scrabble/optimizations/FlowableCharSequence.java +++ b/benchmarks/src/jmh/java/benchmarks/flow/scrabble/optimizations/FlowableCharSequence.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.flow.scrabble.optimizations; diff --git a/benchmarks/src/jmh/java/benchmarks/flow/scrabble/optimizations/FlowableSplit.java b/benchmarks/src/jmh/java/benchmarks/flow/scrabble/optimizations/FlowableSplit.java index 83c203e42f..af8696c802 100644 --- a/benchmarks/src/jmh/java/benchmarks/flow/scrabble/optimizations/FlowableSplit.java +++ b/benchmarks/src/jmh/java/benchmarks/flow/scrabble/optimizations/FlowableSplit.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.flow.scrabble.optimizations; diff --git a/benchmarks/src/jmh/java/benchmarks/flow/scrabble/optimizations/StringFlowable.java b/benchmarks/src/jmh/java/benchmarks/flow/scrabble/optimizations/StringFlowable.java index 3d36a0d8e7..cf6cc79b79 100644 --- a/benchmarks/src/jmh/java/benchmarks/flow/scrabble/optimizations/StringFlowable.java +++ b/benchmarks/src/jmh/java/benchmarks/flow/scrabble/optimizations/StringFlowable.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.flow.scrabble.optimizations; diff --git a/benchmarks/src/jmh/kotlin/benchmarks/ChannelProducerConsumerBenchmark.kt b/benchmarks/src/jmh/kotlin/benchmarks/ChannelProducerConsumerBenchmark.kt index 941e3d84ba..deeea77af9 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/ChannelProducerConsumerBenchmark.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/ChannelProducerConsumerBenchmark.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks @@ -147,4 +147,4 @@ private fun doWork(): Unit = Blackhole.consumeCPU(ThreadLocalRandom.current().ne private const val WORK_MIN = 50L private const val WORK_MAX = 100L -private const val APPROX_BATCH_SIZE = 100000 \ No newline at end of file +private const val APPROX_BATCH_SIZE = 100000 diff --git a/benchmarks/src/jmh/kotlin/benchmarks/ChannelSinkBenchmark.kt b/benchmarks/src/jmh/kotlin/benchmarks/ChannelSinkBenchmark.kt index 8b5e90aaf5..9c7f38a6f9 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/ChannelSinkBenchmark.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/ChannelSinkBenchmark.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks diff --git a/benchmarks/src/jmh/kotlin/benchmarks/ParametrizedDispatcherBase.kt b/benchmarks/src/jmh/kotlin/benchmarks/ParametrizedDispatcherBase.kt index fab052370e..b635d1ef2c 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/ParametrizedDispatcherBase.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/ParametrizedDispatcherBase.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks @@ -44,4 +44,4 @@ abstract class ParametrizedDispatcherBase : CoroutineScope { closeable?.close() } -} \ No newline at end of file +} diff --git a/benchmarks/src/jmh/kotlin/benchmarks/SemaphoreBenchmark.kt b/benchmarks/src/jmh/kotlin/benchmarks/SemaphoreBenchmark.kt index 0fc563a89e..5da5dc8920 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/SemaphoreBenchmark.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/SemaphoreBenchmark.kt @@ -1,5 +1,10 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package benchmarks +import benchmarks.common.* import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.scheduling.ExperimentalCoroutineDispatcher @@ -7,7 +12,6 @@ import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.withPermit import org.openjdk.jmh.annotations.* import java.util.concurrent.ForkJoinPool -import java.util.concurrent.ThreadLocalRandom import java.util.concurrent.TimeUnit @Warmup(iterations = 3, time = 500, timeUnit = TimeUnit.MICROSECONDS) @@ -50,9 +54,9 @@ open class SemaphoreBenchmark { jobs += GlobalScope.launch { repeat(n) { semaphore.withPermit { - doWork(WORK_INSIDE) + doGeomDistrWork(WORK_INSIDE) } - doWork(WORK_OUTSIDE) + doGeomDistrWork(WORK_OUTSIDE) } } } @@ -68,9 +72,9 @@ open class SemaphoreBenchmark { jobs += GlobalScope.launch { repeat(n) { semaphore.send(Unit) // acquire - doWork(WORK_INSIDE) + doGeomDistrWork(WORK_INSIDE) semaphore.receive() // release - doWork(WORK_OUTSIDE) + doGeomDistrWork(WORK_OUTSIDE) } } } @@ -83,15 +87,6 @@ enum class SemaphoreBenchDispatcherCreator(val create: (parallelism: Int) -> Cor EXPERIMENTAL({ parallelism -> ExperimentalCoroutineDispatcher(corePoolSize = parallelism, maxPoolSize = parallelism) }) } -private fun doWork(work: Int) { - // We use geometric distribution here - val p = 1.0 / work - val r = ThreadLocalRandom.current() - while (true) { - if (r.nextDouble() < p) break - } -} - private const val WORK_INSIDE = 80 private const val WORK_OUTSIDE = 40 -private const val BATCH_SIZE = 1000000 \ No newline at end of file +private const val BATCH_SIZE = 1000000 diff --git a/benchmarks/src/jmh/kotlin/benchmarks/akka/PingPongAkkaBenchmark.kt b/benchmarks/src/jmh/kotlin/benchmarks/akka/PingPongAkkaBenchmark.kt index 1a6e9d4036..ea9aeca94d 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/akka/PingPongAkkaBenchmark.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/akka/PingPongAkkaBenchmark.kt @@ -1,18 +1,15 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.akka -import akka.actor.ActorRef -import akka.actor.ActorSystem -import akka.actor.Props -import akka.actor.UntypedAbstractActor -import com.typesafe.config.ConfigFactory +import akka.actor.* +import com.typesafe.config.* import org.openjdk.jmh.annotations.* -import scala.concurrent.Await -import scala.concurrent.duration.Duration -import java.util.concurrent.CountDownLatch +import scala.concurrent.* +import scala.concurrent.duration.* +import java.util.concurrent.* const val N_MESSAGES = 100_000 @@ -117,4 +114,4 @@ open class PingPongAkkaBenchmark { } } } -} \ No newline at end of file +} diff --git a/benchmarks/src/jmh/kotlin/benchmarks/akka/StatefulActorAkkaBenchmark.kt b/benchmarks/src/jmh/kotlin/benchmarks/akka/StatefulActorAkkaBenchmark.kt index 4e3ad6ce4d..5cfb86dda0 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/akka/StatefulActorAkkaBenchmark.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/akka/StatefulActorAkkaBenchmark.kt @@ -1,19 +1,15 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.akka -import akka.actor.ActorRef -import akka.actor.ActorSystem -import akka.actor.Props -import akka.actor.UntypedAbstractActor -import com.typesafe.config.ConfigFactory +import akka.actor.* +import com.typesafe.config.* import org.openjdk.jmh.annotations.* -import scala.concurrent.Await -import scala.concurrent.duration.Duration -import java.util.concurrent.CountDownLatch -import java.util.concurrent.ThreadLocalRandom +import scala.concurrent.* +import scala.concurrent.duration.* +import java.util.concurrent.* const val ROUNDS = 10_000 const val STATE_SIZE = 1024 @@ -171,4 +167,4 @@ open class StatefulActorAkkaBenchmark { initLatch.countDown() } } -} \ No newline at end of file +} diff --git a/benchmarks/src/jmh/kotlin/benchmarks/flow/FlatMapMergeBenchmark.kt b/benchmarks/src/jmh/kotlin/benchmarks/flow/FlatMapMergeBenchmark.kt index f6690977a2..f3b2082ae1 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/flow/FlatMapMergeBenchmark.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/flow/FlatMapMergeBenchmark.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.flow @@ -44,4 +44,4 @@ open class FlatMapMergeBenchmark { } } -} \ No newline at end of file +} diff --git a/benchmarks/src/jmh/kotlin/benchmarks/flow/FlowFlattenMergeBenchmark.kt b/benchmarks/src/jmh/kotlin/benchmarks/flow/FlowFlattenMergeBenchmark.kt new file mode 100644 index 0000000000..3fff2697cc --- /dev/null +++ b/benchmarks/src/jmh/kotlin/benchmarks/flow/FlowFlattenMergeBenchmark.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package benchmarks.flow + +import benchmarks.common.* +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* +import org.openjdk.jmh.annotations.* +import java.util.concurrent.* + +/** + * Benchmark to measure performance of [kotlinx.coroutines.flow.FlowKt.flattenMerge]. + * In addition to that, it can be considered as a macro benchmark for the [kotlinx.coroutines.sync.Semaphore] + */ +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.SECONDS) +@State(Scope.Benchmark) +@Fork(1) +open class FlowFlattenMergeBenchmark { + @Param + private var flowsNumberStrategy: FlowsNumberStrategy = FlowsNumberStrategy.`10xConcurrency flows` + + @Param("1", "2", "4", "8") + private var concurrency: Int = 0 + + private lateinit var flow: Flow> + + @Setup + fun setup() { + val n = flowsNumberStrategy.get(concurrency) + val flowElementsToProcess = ELEMENTS / n + + flow = (1..n).asFlow().map { + flow { + repeat(flowElementsToProcess) { + doGeomDistrWork(WORK) + emit(it) + } + } + } + } + + @Benchmark + fun flattenMerge() = runBlocking(Dispatchers.Default) { + flow.flattenMerge(concurrency = concurrency).collect() + } +} + +enum class FlowsNumberStrategy(val get: (concurrency: Int) -> Int) { + `10xConcurrency flows`({ concurrency -> concurrency * 10 }), + `1xConcurrency flows`({ it }), + `100 flows`({ 100 }), + `500 flows`({ 500 }) +} + +// If you change this variable please be sure that you change variable elements in the generate_plots_flow_flatten_merge.py +// python script as well +private const val ELEMENTS = 100_000 +private const val WORK = 100 diff --git a/benchmarks/src/jmh/kotlin/benchmarks/flow/NumbersBenchmark.kt b/benchmarks/src/jmh/kotlin/benchmarks/flow/NumbersBenchmark.kt index e0bc2fcc48..0cb31056bb 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/flow/NumbersBenchmark.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/flow/NumbersBenchmark.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @@ -103,4 +103,4 @@ open class NumbersBenchmark { .filter { (it + 1) % 3 == 0L }.count() .blockingGet() } -} \ No newline at end of file +} diff --git a/benchmarks/src/jmh/kotlin/benchmarks/flow/SafeFlowBenchmark.kt b/benchmarks/src/jmh/kotlin/benchmarks/flow/SafeFlowBenchmark.kt index f8c459fd0d..258df9b03e 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/flow/SafeFlowBenchmark.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/flow/SafeFlowBenchmark.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.flow diff --git a/benchmarks/src/jmh/kotlin/benchmarks/flow/TakeBenchmark.kt b/benchmarks/src/jmh/kotlin/benchmarks/flow/TakeBenchmark.kt index 84afca2439..1c469a69b9 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/flow/TakeBenchmark.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/flow/TakeBenchmark.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.flow diff --git a/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/FlowPlaysScrabbleBase.kt b/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/FlowPlaysScrabbleBase.kt index b556053b5d..9e39b43b8b 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/FlowPlaysScrabbleBase.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/FlowPlaysScrabbleBase.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.flow.scrabble @@ -7,7 +7,6 @@ package benchmarks.flow.scrabble import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import org.openjdk.jmh.annotations.* -import java.lang.Long.* import java.lang.Long.max import java.util.* import java.util.concurrent.* diff --git a/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/FlowPlaysScrabbleOpt.kt b/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/FlowPlaysScrabbleOpt.kt index 921f390dce..62cc2e5c50 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/FlowPlaysScrabbleOpt.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/FlowPlaysScrabbleOpt.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. and contributors Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.flow.scrabble @@ -7,11 +7,9 @@ package benchmarks.flow.scrabble import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import org.openjdk.jmh.annotations.* -import java.lang.Long.max +import java.lang.Long.* import java.util.* import java.util.concurrent.* -import java.util.stream.* -import kotlin.math.* @Warmup(iterations = 7, time = 1, timeUnit = TimeUnit.SECONDS) @Measurement(iterations = 7, time = 1, timeUnit = TimeUnit.SECONDS) diff --git a/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/IterableSpliterator.kt b/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/IterableSpliterator.kt index 434ea1e19d..32c0d4c8fa 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/IterableSpliterator.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/IterableSpliterator.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.flow.scrabble diff --git a/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/ReactorPlaysScrabble.kt b/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/ReactorPlaysScrabble.kt index 9adc4f1f59..2283d6c3fb 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/ReactorPlaysScrabble.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/ReactorPlaysScrabble.kt @@ -1,14 +1,12 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.flow.scrabble -import org.openjdk.jmh.annotations.* import reactor.core.publisher.* import java.lang.Long.* import java.util.* -import java.util.concurrent.* import java.util.function.Function /*@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) diff --git a/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/SaneFlowPlaysScrabble.kt b/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/SaneFlowPlaysScrabble.kt index 597667c1c6..0a4f69672f 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/SaneFlowPlaysScrabble.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/SaneFlowPlaysScrabble.kt @@ -1,9 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -/* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.flow.scrabble diff --git a/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/SequencePlaysScrabble.kt b/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/SequencePlaysScrabble.kt index 5f4f4c2d1a..87d0e61232 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/SequencePlaysScrabble.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/SequencePlaysScrabble.kt @@ -1,11 +1,10 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.flow.scrabble import kotlinx.coroutines.* -import kotlinx.coroutines.flow.* import org.openjdk.jmh.annotations.* import java.lang.Long.* import java.util.* diff --git a/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/ShakespearePlaysScrabble.kt b/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/ShakespearePlaysScrabble.kt index 7eaa3f0a5d..7beb54cc3d 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/ShakespearePlaysScrabble.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/flow/scrabble/ShakespearePlaysScrabble.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.flow.scrabble diff --git a/benchmarks/src/jmh/kotlin/benchmarks/scheduler/DispatchersContextSwitchBenchmark.kt b/benchmarks/src/jmh/kotlin/benchmarks/scheduler/DispatchersContextSwitchBenchmark.kt index e7f806760e..3012b9178a 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/scheduler/DispatchersContextSwitchBenchmark.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/scheduler/DispatchersContextSwitchBenchmark.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.scheduler diff --git a/benchmarks/src/jmh/kotlin/benchmarks/scheduler/ForkJoinBenchmark.kt b/benchmarks/src/jmh/kotlin/benchmarks/scheduler/ForkJoinBenchmark.kt index 0c731c3ba8..724a5909b9 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/scheduler/ForkJoinBenchmark.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/scheduler/ForkJoinBenchmark.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.scheduler @@ -164,4 +164,4 @@ private fun compute(coefficients: LongArray, start: Int, end: Int): Double { } return result -} \ No newline at end of file +} diff --git a/benchmarks/src/jmh/kotlin/benchmarks/scheduler/LaunchBenchmark.kt b/benchmarks/src/jmh/kotlin/benchmarks/scheduler/LaunchBenchmark.kt index 8435ddc262..a0de6016c4 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/scheduler/LaunchBenchmark.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/scheduler/LaunchBenchmark.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.scheduler @@ -53,4 +53,4 @@ open class LaunchBenchmark : ParametrizedDispatcherBase() { stopBarrier.reset() } -} \ No newline at end of file +} diff --git a/benchmarks/src/jmh/kotlin/benchmarks/scheduler/StatefulAwaitsBenchmark.kt b/benchmarks/src/jmh/kotlin/benchmarks/scheduler/StatefulAwaitsBenchmark.kt index 93667c0c2f..f829573c87 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/scheduler/StatefulAwaitsBenchmark.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/scheduler/StatefulAwaitsBenchmark.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.scheduler @@ -117,4 +117,4 @@ open class StatefulAsyncBenchmark : ParametrizedDispatcherBase() { } sum } -} \ No newline at end of file +} diff --git a/benchmarks/src/jmh/kotlin/benchmarks/scheduler/actors/ConcurrentStatefulActorBenchmark.kt b/benchmarks/src/jmh/kotlin/benchmarks/scheduler/actors/ConcurrentStatefulActorBenchmark.kt index 6998577310..6ac97ad3e7 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/scheduler/actors/ConcurrentStatefulActorBenchmark.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/scheduler/actors/ConcurrentStatefulActorBenchmark.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.scheduler.actors diff --git a/benchmarks/src/jmh/kotlin/benchmarks/scheduler/actors/CycledActorsBenchmark.kt b/benchmarks/src/jmh/kotlin/benchmarks/scheduler/actors/CycledActorsBenchmark.kt index 67548f624e..71018abcbc 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/scheduler/actors/CycledActorsBenchmark.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/scheduler/actors/CycledActorsBenchmark.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.scheduler.actors diff --git a/benchmarks/src/jmh/kotlin/benchmarks/scheduler/actors/PingPongActorBenchmark.kt b/benchmarks/src/jmh/kotlin/benchmarks/scheduler/actors/PingPongActorBenchmark.kt index 2d547e2660..4c6ae7754c 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/scheduler/actors/PingPongActorBenchmark.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/scheduler/actors/PingPongActorBenchmark.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.scheduler.actors @@ -100,4 +100,4 @@ fun CoroutineScope.pongActorCoroutine(capacity: Int = 1) = else -> error("Cannot happen $message") } } - } \ No newline at end of file + } diff --git a/benchmarks/src/jmh/kotlin/benchmarks/scheduler/actors/PingPongWithBlockingContext.kt b/benchmarks/src/jmh/kotlin/benchmarks/scheduler/actors/PingPongWithBlockingContext.kt index 86a9440a58..dcbda090e8 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/scheduler/actors/PingPongWithBlockingContext.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/scheduler/actors/PingPongWithBlockingContext.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.scheduler.actors diff --git a/benchmarks/src/jmh/kotlin/benchmarks/scheduler/actors/StatefulActorBenchmark.kt b/benchmarks/src/jmh/kotlin/benchmarks/scheduler/actors/StatefulActorBenchmark.kt index fb342295a6..01691a2d77 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/scheduler/actors/StatefulActorBenchmark.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/scheduler/actors/StatefulActorBenchmark.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.scheduler.actors diff --git a/benchmarks/src/jmh/kotlin/benchmarks/tailcall/SimpleChannel.kt b/benchmarks/src/jmh/kotlin/benchmarks/tailcall/SimpleChannel.kt index ee1ef724cb..c217fcae91 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/tailcall/SimpleChannel.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/tailcall/SimpleChannel.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.tailcall @@ -95,4 +95,4 @@ class CancellableReusableChannel : SimpleChannel() { producer = it.intercepted() COROUTINE_SUSPENDED } -} \ No newline at end of file +} diff --git a/benchmarks/src/jmh/kotlin/benchmarks/tailcall/SimpleChannelBenchmark.kt b/benchmarks/src/jmh/kotlin/benchmarks/tailcall/SimpleChannelBenchmark.kt index 09ff7697f6..7bb962b3b8 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/tailcall/SimpleChannelBenchmark.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/tailcall/SimpleChannelBenchmark.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.tailcall diff --git a/benchmarks/src/main/kotlin/benchmarks/common/BenchmarkUtils.kt b/benchmarks/src/main/kotlin/benchmarks/common/BenchmarkUtils.kt new file mode 100644 index 0000000000..0057573bc6 --- /dev/null +++ b/benchmarks/src/main/kotlin/benchmarks/common/BenchmarkUtils.kt @@ -0,0 +1,17 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package benchmarks.common + +import java.util.concurrent.* + +fun doGeomDistrWork(work: Int) { + // We use geometric distribution here. We also checked on macbook pro 13" (2017) that the resulting work times + // are distributed geometrically, see https://github.com/Kotlin/kotlinx.coroutines/pull/1464#discussion_r355705325 + val p = 1.0 / work + val r = ThreadLocalRandom.current() + while (true) { + if (r.nextDouble() < p) break + } +} diff --git a/binary-compatibility-validator/README.md b/binary-compatibility-validator/README.md deleted file mode 100644 index e34d6877c5..0000000000 --- a/binary-compatibility-validator/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Public API binary compatibility validator - -This module allows to dump and compare public binary API to ensure binary compatibility with a previous version. -This tool is slightly adapted copy of [original Kotlin compatibility validator](https://github.com/JetBrains/kotlin/tree/master/libraries/tools/binary-compatibility-validator) by @ilya-g. - -To update public API dumps use: - -```bash -./gradlew :binary-compatibility-validator:test -Poverwrite.output=true -``` diff --git a/binary-compatibility-validator/build.gradle b/binary-compatibility-validator/build.gradle deleted file mode 100644 index c6eaffdfca..0000000000 --- a/binary-compatibility-validator/build.gradle +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -configurations { - testArtifacts - configureKotlinJvmPlatform(testArtifacts) -} - -dependencies { - compile 'org.ow2.asm:asm-debug-all:5.0.4' - compile 'com.google.code.gson:gson:2.6.2' - - testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version" - - testArtifacts project(':kotlinx-coroutines-core') - testArtifacts project(':kotlinx-coroutines-test') - testArtifacts project(':kotlinx-coroutines-debug') - - testArtifacts project(':kotlinx-coroutines-reactive') - testArtifacts project(':kotlinx-coroutines-reactor') - testArtifacts project(':kotlinx-coroutines-rx2') - - testArtifacts project(':kotlinx-coroutines-guava') - testArtifacts project(':kotlinx-coroutines-jdk8') - testArtifacts project(':kotlinx-coroutines-slf4j') - testArtifacts project(path: ':kotlinx-coroutines-play-services', configuration: 'default') - - testArtifacts project(':kotlinx-coroutines-android') - testArtifacts project(':kotlinx-coroutines-javafx') - testArtifacts project(':kotlinx-coroutines-swing') -} - -def testCasesDeclarationsDump = "${buildDir}/visibilities.json".toString() - -compileTestKotlin { - kotlinOptions { - freeCompilerArgs = ["-Xdump-declarations-to=$testCasesDeclarationsDump"] - } -} - -sourceSets { - test { - java { - srcDir "test/cases" - } - } -} - -test { - dependsOn cleanCompileTestKotlin - dependsOn configurations.testArtifacts - - systemProperty 'testCasesClassesDirs', sourceSets.test.output.classesDirs.asPath - systemProperty 'testCasesDeclarations', testCasesDeclarationsDump - systemProperty 'overwrite.output', project.properties['overwrite.output'] - jvmArgs '-ea' -} diff --git a/binary-compatibility-validator/resources/api.properties b/binary-compatibility-validator/resources/api.properties deleted file mode 100644 index e15ad21a02..0000000000 --- a/binary-compatibility-validator/resources/api.properties +++ /dev/null @@ -1,9 +0,0 @@ -# -# Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. -# - -module.roots=/ integration reactive ui -module.marker=build.gradle -module.ignore=stdlib-stubs benchmarks knit binary-compatibility-validator site publication-validator kotlinx-coroutines-bom - -packages.internal=kotlinx.coroutines.internal \ No newline at end of file diff --git a/binary-compatibility-validator/src/PublicApiDump.kt b/binary-compatibility-validator/src/PublicApiDump.kt deleted file mode 100644 index 343df34b87..0000000000 --- a/binary-compatibility-validator/src/PublicApiDump.kt +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.coroutines.tools - -import org.objectweb.asm.* -import org.objectweb.asm.tree.* -import java.io.* -import java.util.jar.* - -fun JarFile.classEntries() = entries().asSequence().filter { - !it.isDirectory && it.name.endsWith(".class") && !it.name.startsWith("META-INF/") -} - -fun getBinaryAPI(jar: JarFile, visibilityMap: Map): List = - getBinaryAPI(jar.classEntries().map { entry -> jar.getInputStream(entry) }, visibilityMap) - -fun getBinaryAPI( - classStreams: Sequence, - visibilityMap: Map -): List = - classStreams.map { - it.use { stream -> - val classNode = ClassNode() - ClassReader(stream).accept(classNode, ClassReader.SKIP_CODE) - classNode - } - }.map { - with(it) { - val classVisibility = visibilityMap[name] - val classAccess = AccessFlags(effectiveAccess and Opcodes.ACC_STATIC.inv()) - val supertypes = listOf(superName) - "java/lang/Object" + interfaces.sorted() - - val memberSignatures = ( - fields.map { - with(it) { - FieldBinarySignature( - name, - desc, - isPublishedApi(), - AccessFlags(access) - ) - } - } + - methods.map { - with(it) { - MethodBinarySignature( - name, - desc, - isPublishedApi(), - AccessFlags(access) - ) - } - } - ).filter { - it.isEffectivelyPublic(classAccess, classVisibility) - } - - ClassBinarySignature( - name, - superName, - outerClassName, - supertypes, - memberSignatures, - classAccess, - isEffectivelyPublic(classVisibility), - isFileOrMultipartFacade() || isDefaultImpls() - ) - } - }.asIterable().sortedBy { it.name } - - -fun List.filterOutNonPublic(nonPublicPackages: List = emptyList()): List { - val nonPublicPaths = nonPublicPackages.map { it.replace('.', '/') + '/' } - val classByName = associateBy { it.name } - - fun ClassBinarySignature.isInNonPublicPackage() = - nonPublicPaths.any { name.startsWith(it) } - - fun ClassBinarySignature.isPublicAndAccessible(): Boolean = - isEffectivelyPublic && - (outerName == null || classByName[outerName]?.let { outerClass -> - !(this.access.isProtected && outerClass.access.isFinal) - && outerClass.isPublicAndAccessible() - } ?: true) - - fun supertypes(superName: String) = generateSequence({ classByName[superName] }, { classByName[it.superName] }) - - fun ClassBinarySignature.flattenNonPublicBases(): ClassBinarySignature { - - val nonPublicSupertypes = supertypes(superName).takeWhile { !it.isPublicAndAccessible() }.toList() - if (nonPublicSupertypes.isEmpty()) - return this - - val inheritedStaticSignatures = - nonPublicSupertypes.flatMap { it.memberSignatures.filter { it.access.isStatic } } - - // not covered the case when there is public superclass after chain of private superclasses - return this.copy( - memberSignatures = memberSignatures + inheritedStaticSignatures, - supertypes = supertypes - superName - ) - } - - return filter { !it.isInNonPublicPackage() && it.isPublicAndAccessible() } - .map { it.flattenNonPublicBases() } - .filterNot { it.isNotUsedWhenEmpty && it.memberSignatures.isEmpty() } -} - -fun List.dump() = dump(to = System.out) - -fun List.dump(to: T): T = to.apply { - this@dump.forEach { - append(it.signature).appendln(" {") - it.memberSignatures.sortedWith(MEMBER_SORT_ORDER).forEach { append("\t").appendln(it.signature) } - appendln("}\n") - } -} - diff --git a/binary-compatibility-validator/src/asmUtils.kt b/binary-compatibility-validator/src/asmUtils.kt deleted file mode 100644 index b14cb8d5e6..0000000000 --- a/binary-compatibility-validator/src/asmUtils.kt +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.coroutines.tools - -import org.objectweb.asm.* -import org.objectweb.asm.tree.* - -val ACCESS_NAMES = mapOf( - Opcodes.ACC_PUBLIC to "public", - Opcodes.ACC_PROTECTED to "protected", - Opcodes.ACC_PRIVATE to "private", - Opcodes.ACC_STATIC to "static", - Opcodes.ACC_FINAL to "final", - Opcodes.ACC_ABSTRACT to "abstract", - Opcodes.ACC_SYNTHETIC to "synthetic", - Opcodes.ACC_INTERFACE to "interface", - Opcodes.ACC_ANNOTATION to "annotation") - -data class ClassBinarySignature( - val name: String, - val superName: String, - val outerName: String?, - val supertypes: List, - val memberSignatures: List, - val access: AccessFlags, - val isEffectivelyPublic: Boolean, - val isNotUsedWhenEmpty: Boolean) { - - val signature: String - get() = "${access.getModifierString()} class $name" + if (supertypes.isEmpty()) "" else " : ${supertypes.joinToString()}" - -} - - -interface MemberBinarySignature { - val name: String - val desc: String - val access: AccessFlags - val isPublishedApi: Boolean - - fun isEffectivelyPublic(classAccess: AccessFlags, classVisibility: ClassVisibility?) - = access.isPublic && !(access.isProtected && classAccess.isFinal) - && (findMemberVisibility(classVisibility)?.isPublic(isPublishedApi) ?: true) - - fun findMemberVisibility(classVisibility: ClassVisibility?) - = classVisibility?.members?.get(MemberSignature(name, desc)) - - val signature: String -} - -data class MethodBinarySignature( - override val name: String, - override val desc: String, - override val isPublishedApi: Boolean, - override val access: AccessFlags) : MemberBinarySignature { - override val signature: String - get() = "${access.getModifierString()} fun $name $desc" - - override fun isEffectivelyPublic(classAccess: AccessFlags, classVisibility: ClassVisibility?) - = super.isEffectivelyPublic(classAccess, classVisibility) - && !isAccessOrAnnotationsMethod() - - private fun isAccessOrAnnotationsMethod() = access.isSynthetic && (name.startsWith("access\$") || name.endsWith("\$annotations")) -} - -data class FieldBinarySignature( - override val name: String, - override val desc: String, - override val isPublishedApi: Boolean, - override val access: AccessFlags) : MemberBinarySignature { - override val signature: String - get() = "${access.getModifierString()} field $name $desc" - - override fun findMemberVisibility(classVisibility: ClassVisibility?): MemberVisibility? { - val fieldVisibility = super.findMemberVisibility(classVisibility) ?: return null - - // good case for 'satisfying': fieldVisibility.satisfying { it.isLateInit() }?.let { classVisibility?.findSetterForProperty(it) } - if (fieldVisibility.isLateInit()) { - classVisibility?.findSetterForProperty(fieldVisibility)?.let { return it } - } - return fieldVisibility - } -} - -val MemberBinarySignature.kind: Int get() = when (this) { - is FieldBinarySignature -> 1 - is MethodBinarySignature -> 2 - else -> error("Unsupported $this") -} - -val MEMBER_SORT_ORDER = compareBy( - { it.kind }, - { it.name }, - { it.desc } -) - - -data class AccessFlags(val access: Int) { - val isPublic: Boolean get() = isPublic(access) - val isProtected: Boolean get() = isProtected(access) - val isStatic: Boolean get() = isStatic(access) - val isFinal: Boolean get() = isFinal(access) - val isSynthetic: Boolean get() = isSynthetic(access) - - fun getModifiers(): List = ACCESS_NAMES.entries.mapNotNull { if (access and it.key != 0) it.value else null } - fun getModifierString(): String = getModifiers().joinToString(" ") -} - -fun isPublic(access: Int) = access and Opcodes.ACC_PUBLIC != 0 || access and Opcodes.ACC_PROTECTED != 0 -fun isProtected(access: Int) = access and Opcodes.ACC_PROTECTED != 0 -fun isStatic(access: Int) = access and Opcodes.ACC_STATIC != 0 -fun isFinal(access: Int) = access and Opcodes.ACC_FINAL != 0 -fun isSynthetic(access: Int) = access and Opcodes.ACC_SYNTHETIC != 0 - - -fun ClassNode.isEffectivelyPublic(classVisibility: ClassVisibility?) = - isPublic(access) - && !isLocal() - && !isWhenMappings() - && (classVisibility?.isPublic(isPublishedApi()) ?: true) - - -val ClassNode.innerClassNode: InnerClassNode? get() = innerClasses.singleOrNull { it.name == name } -fun ClassNode.isLocal() = innerClassNode?.run { innerName == null && outerName == null} ?: false -fun ClassNode.isInner() = innerClassNode != null -fun ClassNode.isWhenMappings() = isSynthetic(access) && name.endsWith("\$WhenMappings") - -val ClassNode.effectiveAccess: Int get() = innerClassNode?.access ?: access -val ClassNode.outerClassName: String? get() = innerClassNode?.outerName - - -const val publishedApiAnnotationName = "kotlin/PublishedApi" -fun ClassNode.isPublishedApi() = findAnnotation(publishedApiAnnotationName, includeInvisible = true) != null -fun MethodNode.isPublishedApi() = findAnnotation(publishedApiAnnotationName, includeInvisible = true) != null -fun FieldNode.isPublishedApi() = findAnnotation(publishedApiAnnotationName, includeInvisible = true) != null - - -private object KotlinClassKind { - const val FILE = 2 - const val SYNTHETIC_CLASS = 3 - const val MULTIPART_FACADE = 4 - - val FILE_OR_MULTIPART_FACADE_KINDS = listOf(FILE, MULTIPART_FACADE) -} - -fun ClassNode.isFileOrMultipartFacade() = kotlinClassKind.let { it != null && it in KotlinClassKind.FILE_OR_MULTIPART_FACADE_KINDS } -fun ClassNode.isDefaultImpls() = isInner() && name.endsWith("\$DefaultImpls") && kotlinClassKind == KotlinClassKind.SYNTHETIC_CLASS - - -val ClassNode.kotlinClassKind: Int? - get() = findAnnotation("kotlin/Metadata", false)?.get("k") as Int? - -fun ClassNode.findAnnotation(annotationName: String, includeInvisible: Boolean = false) = findAnnotation(annotationName, visibleAnnotations, invisibleAnnotations, includeInvisible) -fun MethodNode.findAnnotation(annotationName: String, includeInvisible: Boolean = false) = findAnnotation(annotationName, visibleAnnotations, invisibleAnnotations, includeInvisible) -fun FieldNode.findAnnotation(annotationName: String, includeInvisible: Boolean = false) = findAnnotation(annotationName, visibleAnnotations, invisibleAnnotations, includeInvisible) - -operator fun AnnotationNode.get(key: String): Any? = values.annotationValue(key) - -private fun List.annotationValue(key: String): Any? { - for (index in (0 .. size / 2 - 1)) { - if (this[index*2] == key) - return this[index*2 + 1] - } - return null -} - -private fun findAnnotation(annotationName: String, visibleAnnotations: List?, invisibleAnnotations: List?, includeInvisible: Boolean): AnnotationNode? = - visibleAnnotations?.firstOrNull { it.refersToName(annotationName) } ?: - if (includeInvisible) invisibleAnnotations?.firstOrNull { it.refersToName(annotationName) } else null - -fun AnnotationNode.refersToName(name: String) = desc.startsWith('L') && desc.endsWith(';') && desc.regionMatches(1, name, 0, name.length) \ No newline at end of file diff --git a/binary-compatibility-validator/src/kotlinVisibilities.kt b/binary-compatibility-validator/src/kotlinVisibilities.kt deleted file mode 100644 index 4322140c80..0000000000 --- a/binary-compatibility-validator/src/kotlinVisibilities.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.coroutines.tools - -import com.google.gson.internal.* -import com.google.gson.stream.* -import java.io.* - -data class ClassVisibility(val name: String, val visibility: String?, val members: Map) -data class MemberVisibility(val member: MemberSignature, val declaration: String?, val visibility: String?) -data class MemberSignature(val name: String, val desc: String) - -private fun isPublic(visibility: String?, isPublishedApi: Boolean) = visibility == null || visibility == "public" || visibility == "protected" || (isPublishedApi && visibility == "internal") -fun ClassVisibility.isPublic(isPublishedApi: Boolean) = isPublic(visibility, isPublishedApi) -fun MemberVisibility.isPublic(isPublishedApi: Boolean) = isPublic(visibility, isPublishedApi) - -fun MemberVisibility.isLateInit() = declaration != null && "lateinit var " in declaration - -private val varValPrefix = Regex("va[lr]\\s+") -fun ClassVisibility.findSetterForProperty(property: MemberVisibility): MemberVisibility? { - // ad-hoc solution: - val declaration = property.declaration ?: return null - val match = varValPrefix.find(declaration) ?: return null - val name = declaration.substring(match.range.endInclusive + 1).substringBefore(':') - val setterName = "" - return members.values.find { it.declaration?.contains(setterName) ?: false } -} - -fun readKotlinVisibilities(declarationFile: File): Map { - val result = mutableListOf() - declarationFile.bufferedReader().use { reader -> - val jsonReader = JsonReader(reader) - jsonReader.beginArray() - while (jsonReader.hasNext()) { - val classObject = Streams.parse(jsonReader).asJsonObject - result += with (classObject) { - val name = getAsJsonPrimitive("class").asString - val visibility = getAsJsonPrimitive("visibility")?.asString - val members = getAsJsonArray("members").map { it -> - with(it.asJsonObject) { - val name = getAsJsonPrimitive("name").asString - val desc = getAsJsonPrimitive("desc").asString - val declaration = getAsJsonPrimitive("declaration")?.asString - val visibility = getAsJsonPrimitive("visibility")?.asString - MemberVisibility(MemberSignature(name, desc), declaration, visibility) - } - } - ClassVisibility(name, visibility, members.associateByTo(hashMapOf()) { it.member }) - } - } - jsonReader.endArray() - } - - return result.associateByTo(hashMapOf()) { it.name } -} diff --git a/binary-compatibility-validator/test/CasesPublicAPITest.kt b/binary-compatibility-validator/test/CasesPublicAPITest.kt deleted file mode 100644 index f0212e70a9..0000000000 --- a/binary-compatibility-validator/test/CasesPublicAPITest.kt +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.coroutines.tools - -import org.junit.* -import org.junit.rules.* -import java.io.* - -class CasesPublicAPITest { - - companion object { - val visibilities by lazy { readKotlinVisibilities(File(System.getProperty("testCasesDeclarations")!!)) } - - val baseClassPaths: List = - System.getProperty("testCasesClassesDirs") - .let { requireNotNull(it) { "Specify testCasesClassesDirs with a system property" } } - .split(File.pathSeparator) - .map { File(it, "cases").canonicalFile } - val baseOutputPath = File("test/cases") - } - - @Rule - @JvmField - val testName = TestName() - - @Test - fun companions() { - snapshotAPIAndCompare(testName.methodName) - } - - @Test - fun inline() { - snapshotAPIAndCompare(testName.methodName) - } - - @Test - fun interfaces() { - snapshotAPIAndCompare(testName.methodName) - } - - @Test - fun internal() { - snapshotAPIAndCompare(testName.methodName) - } - - @Test - fun java() { - snapshotAPIAndCompare(testName.methodName) - } - - @Test - fun localClasses() { - snapshotAPIAndCompare(testName.methodName) - } - - @Test - fun nestedClasses() { - snapshotAPIAndCompare(testName.methodName) - } - - @Test - fun private() { - snapshotAPIAndCompare(testName.methodName) - } - - @Test - fun protected() { - snapshotAPIAndCompare(testName.methodName) - } - - @Test - fun public() { - snapshotAPIAndCompare(testName.methodName) - } - - @Test - fun special() { - snapshotAPIAndCompare(testName.methodName) - } - - @Test - fun whenMappings() { - snapshotAPIAndCompare(testName.methodName) - } - - - private fun snapshotAPIAndCompare(testClassRelativePath: String) { - val testClassPaths = baseClassPaths.map { it.resolve(testClassRelativePath) } - val testClasses = testClassPaths.flatMap { it.listFiles().orEmpty().asIterable() } - check(testClasses.isNotEmpty()) { "No class files are found in paths: $testClassPaths" } - val testClassStreams = testClasses.asSequence().filter { it.name.endsWith(".class") }.map { it.inputStream() } - val api = getBinaryAPI(testClassStreams, visibilities).filterOutNonPublic() - val target = baseOutputPath.resolve(testClassRelativePath).resolve(testName.methodName + ".txt") - api.dumpAndCompareWith(target) - } -} diff --git a/binary-compatibility-validator/test/PublicApiTest.kt b/binary-compatibility-validator/test/PublicApiTest.kt deleted file mode 100644 index fb4f55cc17..0000000000 --- a/binary-compatibility-validator/test/PublicApiTest.kt +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.coroutines.tools - -import org.junit.* -import org.junit.runner.* -import org.junit.runners.* -import java.io.* -import java.util.* -import java.util.jar.* -import kotlin.collections.ArrayList - -@RunWith(Parameterized::class) -class PublicApiTest( - private val rootDir: String, - private val moduleName: String -) { - companion object { - private val apiProps = ClassLoader.getSystemClassLoader() - .getResource("api.properties").openStream().use { Properties().apply { load(it) } } - private val nonPublicPackages = apiProps.getProperty("packages.internal")!!.split(" ") - - @Parameterized.Parameters(name = "{1}") - @JvmStatic - fun modules(): List> { - val moduleRoots = apiProps.getProperty("module.roots").split(" ") - val moduleMarker = apiProps.getProperty("module.marker")!! - val moduleIgnore = apiProps.getProperty("module.ignore")!!.split(" ").toSet() - val modules = ArrayList>() - for (rootDir in moduleRoots) { - File("../$rootDir").listFiles( FileFilter { it.isDirectory })?.forEach { dir -> - if (dir.name !in moduleIgnore && File(dir, moduleMarker).exists()) { - modules += arrayOf(rootDir, dir.name) - } - } - } - return modules - } - } - - @Test - fun testApi() { - val libsDir = File("../$rootDir/$moduleName/build/libs").absoluteFile.normalize() - val jarPath = getJarPath(libsDir) - val kotlinJvmMappingsFiles = listOf(libsDir.resolve("../visibilities.json")) - val visibilities = - kotlinJvmMappingsFiles - .map { readKotlinVisibilities(it) } - .reduce { m1, m2 -> m1 + m2 } - JarFile(jarPath).use { jarFile -> - val api = getBinaryAPI(jarFile, visibilities).filterOutNonPublic(nonPublicPackages) - api.dumpAndCompareWith(File("reference-public-api").resolve("$moduleName.txt")) - // check for atomicfu leaks - jarFile.checkForAtomicFu() - } - } - - private fun getJarPath(libsDir: File): File { - val regex = Regex("$moduleName-.+\\.jar") - var files = (libsDir.listFiles() ?: throw Exception("Cannot list files in $libsDir")) - .filter { it.name.let { - it matches regex - && !it.endsWith("-sources.jar") - && !it.endsWith("-javadoc.jar") - && !it.endsWith("-tests.jar")} - && !it.name.contains("-metadata-")} - if (files.size > 1) // maybe multiplatform? - files = files.filter { it.name.startsWith("$moduleName-jvm-") } - return files.singleOrNull() ?: - error("No single file matching $regex in $libsDir:\n${files.joinToString("\n")}") - } -} - -private val ATOMIC_FU_REF = "Lkotlinx/atomicfu/".toByteArray() - -private fun JarFile.checkForAtomicFu() { - val foundClasses = mutableListOf() - for (e in entries()) { - if (!e.name.endsWith(".class")) continue - val bytes = getInputStream(e).use { it.readBytes() } - loop@for (i in 0 until bytes.size - ATOMIC_FU_REF.size) { - for (j in 0 until ATOMIC_FU_REF.size) { - if (bytes[i + j] != ATOMIC_FU_REF[j]) continue@loop - } - foundClasses += e.name // report error at the end with all class names - break@loop - } - } - if (foundClasses.isNotEmpty()) { - error("Found references to atomicfu in jar file $name in the following class files: ${ - foundClasses.joinToString("") { "\n\t\t" + it } - }") - } -} diff --git a/binary-compatibility-validator/test/cases/companions/companions.kt b/binary-compatibility-validator/test/cases/companions/companions.kt deleted file mode 100644 index ef59c6febb..0000000000 --- a/binary-compatibility-validator/test/cases/companions/companions.kt +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.companions - - -object PublicClasses { - class PublicCompanion { - companion object - } - - class ProtectedCompanion { - protected companion object - } - - abstract class AbstractProtectedCompanion { - protected companion object - } - - class InternalCompanion { - internal companion object - } - - class PrivateCompanion { - private companion object - } -} - -object PublicInterfaces { - interface PublicCompanion { - companion object - } - - interface PrivateCompanion { - private companion object - } -} - - - -object InternalClasses { - internal class PublicCompanion { - companion object - } - - internal class ProtectedCompanion { - protected companion object - } - - internal abstract class AbstractProtectedCompanion { - protected companion object - } - - internal class InternalCompanion { - internal companion object - } - - internal class PrivateCompanion { - private companion object - } -} - -object InternalInterfaces { - internal interface PublicCompanion { - companion object - } - - internal interface PrivateCompanion { - private companion object - } -} - - -object PrivateClasses { - private class PublicCompanion { - companion object - } - - private class ProtectedCompanion { - protected companion object - } - - private abstract class AbstractProtectedCompanion { - protected companion object - } - - private class InternalCompanion { - internal companion object - } - - private class PrivateCompanion { - private companion object - } -} - -object PrivateInterfaces { - private interface PublicCompanion { - companion object - } - - private interface PrivateCompanion { - private companion object - } -} - diff --git a/binary-compatibility-validator/test/cases/companions/companions.txt b/binary-compatibility-validator/test/cases/companions/companions.txt deleted file mode 100644 index 691907ba0d..0000000000 --- a/binary-compatibility-validator/test/cases/companions/companions.txt +++ /dev/null @@ -1,66 +0,0 @@ -public final class cases/companions/InternalClasses { - public static final field INSTANCE Lcases/companions/InternalClasses; -} - -public final class cases/companions/InternalInterfaces { - public static final field INSTANCE Lcases/companions/InternalInterfaces; -} - -public final class cases/companions/PrivateClasses { - public static final field INSTANCE Lcases/companions/PrivateClasses; -} - -public final class cases/companions/PrivateInterfaces { - public static final field INSTANCE Lcases/companions/PrivateInterfaces; -} - -public final class cases/companions/PublicClasses { - public static final field INSTANCE Lcases/companions/PublicClasses; -} - -public abstract class cases/companions/PublicClasses$AbstractProtectedCompanion { - public static final field Companion Lcases/companions/PublicClasses$AbstractProtectedCompanion$Companion; - public fun ()V -} - -protected final class cases/companions/PublicClasses$AbstractProtectedCompanion$Companion { -} - -public final class cases/companions/PublicClasses$InternalCompanion { - public static final field Companion Lcases/companions/PublicClasses$InternalCompanion$Companion; - public fun ()V -} - -public final class cases/companions/PublicClasses$PrivateCompanion { - public static final field Companion Lcases/companions/PublicClasses$PrivateCompanion$Companion; - public fun ()V -} - -public final class cases/companions/PublicClasses$ProtectedCompanion { - public static final field Companion Lcases/companions/PublicClasses$ProtectedCompanion$Companion; - public fun ()V -} - -public final class cases/companions/PublicClasses$PublicCompanion { - public static final field Companion Lcases/companions/PublicClasses$PublicCompanion$Companion; - public fun ()V -} - -public final class cases/companions/PublicClasses$PublicCompanion$Companion { -} - -public final class cases/companions/PublicInterfaces { - public static final field INSTANCE Lcases/companions/PublicInterfaces; -} - -public abstract interface class cases/companions/PublicInterfaces$PrivateCompanion { - public static final field Companion Lcases/companions/PublicInterfaces$PrivateCompanion$Companion; -} - -public abstract interface class cases/companions/PublicInterfaces$PublicCompanion { - public static final field Companion Lcases/companions/PublicInterfaces$PublicCompanion$Companion; -} - -public final class cases/companions/PublicInterfaces$PublicCompanion$Companion { -} - diff --git a/binary-compatibility-validator/test/cases/inline/inline.txt b/binary-compatibility-validator/test/cases/inline/inline.txt deleted file mode 100644 index 4961dbcfd4..0000000000 --- a/binary-compatibility-validator/test/cases/inline/inline.txt +++ /dev/null @@ -1,9 +0,0 @@ -public final class cases/inline/InlineExposedKt { - public static final fun exposedForInline ()V -} - -public final class cases/inline/InternalClassExposed { - public fun ()V - public final fun funExposed ()V -} - diff --git a/binary-compatibility-validator/test/cases/inline/inlineExposed.kt b/binary-compatibility-validator/test/cases/inline/inlineExposed.kt deleted file mode 100644 index 2e017b3832..0000000000 --- a/binary-compatibility-validator/test/cases/inline/inlineExposed.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.inline - -@PublishedApi -internal fun exposedForInline() {} - -@PublishedApi -internal class InternalClassExposed - @PublishedApi - internal constructor() { - - @PublishedApi - internal fun funExposed() {} - - // TODO: Cover unsupported cases: requires correctly reflecting annotations from properties - /* - @PublishedApi - internal var propertyExposed: String? = null - - @JvmField - @PublishedApi - internal var fieldExposed: String? = null - */ - -} diff --git a/binary-compatibility-validator/test/cases/inline/inlineOnly.kt b/binary-compatibility-validator/test/cases/inline/inlineOnly.kt deleted file mode 100644 index 9c1f01eb0e..0000000000 --- a/binary-compatibility-validator/test/cases/inline/inlineOnly.kt +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.inline - -@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") -@kotlin.internal.InlineOnly -public inline fun inlineOnly(f: () -> Unit) = f() \ No newline at end of file diff --git a/binary-compatibility-validator/test/cases/interfaces/interfaceWithEmptyImpls.kt b/binary-compatibility-validator/test/cases/interfaces/interfaceWithEmptyImpls.kt deleted file mode 100644 index 96a1628c82..0000000000 --- a/binary-compatibility-validator/test/cases/interfaces/interfaceWithEmptyImpls.kt +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.interfaces - -public interface EmptyImpls { - @SinceKotlin("1.1") - val property: String -} diff --git a/binary-compatibility-validator/test/cases/interfaces/interfaceWithImpls.kt b/binary-compatibility-validator/test/cases/interfaces/interfaceWithImpls.kt deleted file mode 100644 index d2e4ad0c36..0000000000 --- a/binary-compatibility-validator/test/cases/interfaces/interfaceWithImpls.kt +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.interfaces - -public interface BaseWithImpl { - fun foo() = 42 -} - -public interface DerivedWithImpl : BaseWithImpl { - override fun foo(): Int { - return super.foo() + 1 - } -} - -public interface DerivedWithoutImpl : BaseWithImpl - diff --git a/binary-compatibility-validator/test/cases/interfaces/interfaces.txt b/binary-compatibility-validator/test/cases/interfaces/interfaces.txt deleted file mode 100644 index 4f37b42f3a..0000000000 --- a/binary-compatibility-validator/test/cases/interfaces/interfaces.txt +++ /dev/null @@ -1,27 +0,0 @@ -public abstract interface class cases/interfaces/BaseWithImpl { - public abstract fun foo ()I -} - -public final class cases/interfaces/BaseWithImpl$DefaultImpls { - public static fun foo (Lcases/interfaces/BaseWithImpl;)I -} - -public abstract interface class cases/interfaces/DerivedWithImpl : cases/interfaces/BaseWithImpl { - public abstract fun foo ()I -} - -public final class cases/interfaces/DerivedWithImpl$DefaultImpls { - public static fun foo (Lcases/interfaces/DerivedWithImpl;)I -} - -public abstract interface class cases/interfaces/DerivedWithoutImpl : cases/interfaces/BaseWithImpl { -} - -public final class cases/interfaces/DerivedWithoutImpl$DefaultImpls { - public static fun foo (Lcases/interfaces/DerivedWithoutImpl;)I -} - -public abstract interface class cases/interfaces/EmptyImpls { - public abstract fun getProperty ()Ljava/lang/String; -} - diff --git a/binary-compatibility-validator/test/cases/internal/internal.txt b/binary-compatibility-validator/test/cases/internal/internal.txt deleted file mode 100644 index 2ebfa3aff3..0000000000 --- a/binary-compatibility-validator/test/cases/internal/internal.txt +++ /dev/null @@ -1,3 +0,0 @@ -public final class cases/internal/PublicClass { -} - diff --git a/binary-compatibility-validator/test/cases/internal/internalClass.kt b/binary-compatibility-validator/test/cases/internal/internalClass.kt deleted file mode 100644 index 2410b65791..0000000000 --- a/binary-compatibility-validator/test/cases/internal/internalClass.kt +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.internal - -internal class InternalClass { - public val property = 1 - - public fun function() = property -} \ No newline at end of file diff --git a/binary-compatibility-validator/test/cases/internal/internalMultifile1.kt b/binary-compatibility-validator/test/cases/internal/internalMultifile1.kt deleted file mode 100644 index b4622b3afd..0000000000 --- a/binary-compatibility-validator/test/cases/internal/internalMultifile1.kt +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -@file:JvmName("MultifileKt") -@file:JvmMultifileClass -package cases.internal - -internal fun internalFun1() = internalVal diff --git a/binary-compatibility-validator/test/cases/internal/internalMultifile2.kt b/binary-compatibility-validator/test/cases/internal/internalMultifile2.kt deleted file mode 100644 index c375c01e16..0000000000 --- a/binary-compatibility-validator/test/cases/internal/internalMultifile2.kt +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -@file:JvmName("MultifileKt") -@file:JvmMultifileClass -package cases.internal - -internal val internalVal = "Internal" diff --git a/binary-compatibility-validator/test/cases/internal/internalPart.kt b/binary-compatibility-validator/test/cases/internal/internalPart.kt deleted file mode 100644 index b95ba6830e..0000000000 --- a/binary-compatibility-validator/test/cases/internal/internalPart.kt +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.internal - -internal fun internalFun() { - -} - -// TODO: var, val, const \ No newline at end of file diff --git a/binary-compatibility-validator/test/cases/internal/publicClassInternalMember.kt b/binary-compatibility-validator/test/cases/internal/publicClassInternalMember.kt deleted file mode 100644 index 11aac7c5cb..0000000000 --- a/binary-compatibility-validator/test/cases/internal/publicClassInternalMember.kt +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.internal - -public class PublicClass internal constructor() { - - internal val property = 1 - - internal fun function() = property - -} diff --git a/binary-compatibility-validator/test/cases/java/Facade.java b/binary-compatibility-validator/test/cases/java/Facade.java deleted file mode 100644 index d11c16cd7f..0000000000 --- a/binary-compatibility-validator/test/cases/java/Facade.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.java; - -class Part1 { - public static void publicMethod(int param) { } - - public static class Part2 extends Part1 { - public static void publicMethod(String param) { } - } -} - - -public class Facade extends Part1.Part2 { } diff --git a/binary-compatibility-validator/test/cases/java/java.txt b/binary-compatibility-validator/test/cases/java/java.txt deleted file mode 100644 index 75bfd3179f..0000000000 --- a/binary-compatibility-validator/test/cases/java/java.txt +++ /dev/null @@ -1,6 +0,0 @@ -public class cases/java/Facade { - public fun ()V - public static fun publicMethod (I)V - public static fun publicMethod (Ljava/lang/String;)V -} - diff --git a/binary-compatibility-validator/test/cases/localClasses/fromStdlib.kt b/binary-compatibility-validator/test/cases/localClasses/fromStdlib.kt deleted file mode 100644 index 29f6994ec3..0000000000 --- a/binary-compatibility-validator/test/cases/localClasses/fromStdlib.kt +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.localClasses - -private val COMPARER = compareBy { it.length } \ No newline at end of file diff --git a/binary-compatibility-validator/test/cases/localClasses/lambdas.kt b/binary-compatibility-validator/test/cases/localClasses/lambdas.kt deleted file mode 100644 index a99c4497ff..0000000000 --- a/binary-compatibility-validator/test/cases/localClasses/lambdas.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.localClasses - - -class L { - internal fun a(lambda: () -> Unit) = lambda() - - @Suppress("NOTHING_TO_INLINE") - internal inline fun inlineLambda() { - a { - println("OK") - } - } -} - -fun box() { - L().inlineLambda() -} - - -// TODO: inline lambda from stdlib \ No newline at end of file diff --git a/binary-compatibility-validator/test/cases/localClasses/localClasses.kt b/binary-compatibility-validator/test/cases/localClasses/localClasses.kt deleted file mode 100644 index 1af4b77df9..0000000000 --- a/binary-compatibility-validator/test/cases/localClasses/localClasses.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.localClasses - -class A { - fun a() : String { - class B() { - fun s() : String = "OK" - - inner class C {} - - } - return B().s() - } -} - - -class B { - fun a(p: String) : String { - class B() { - fun s() : String = p - } - return B().s() - } -} diff --git a/binary-compatibility-validator/test/cases/localClasses/localClasses.txt b/binary-compatibility-validator/test/cases/localClasses/localClasses.txt deleted file mode 100644 index da0d668ced..0000000000 --- a/binary-compatibility-validator/test/cases/localClasses/localClasses.txt +++ /dev/null @@ -1,18 +0,0 @@ -public final class cases/localClasses/A { - public fun ()V - public final fun a ()Ljava/lang/String; -} - -public final class cases/localClasses/B { - public fun ()V - public final fun a (Ljava/lang/String;)Ljava/lang/String; -} - -public final class cases/localClasses/L { - public fun ()V -} - -public final class cases/localClasses/LambdasKt { - public static final fun box ()V -} - diff --git a/binary-compatibility-validator/test/cases/nestedClasses/internalClass.kt b/binary-compatibility-validator/test/cases/nestedClasses/internalClass.kt deleted file mode 100644 index 7af2dda914..0000000000 --- a/binary-compatibility-validator/test/cases/nestedClasses/internalClass.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.nestedClasses - -internal class InternalClass { - public object ObjPublic - internal object ObjInternal - protected object ObjProtected - private object ObjPrivate - - public class NestedPublic - internal class NestedInternal - protected class NestedProtected - private class NestedPrivate - - public interface NestedPublicInterface - internal interface NestedInternalInterface - protected interface NestedProtectedInterface - private interface NestedPrivateInterface - - public inner class InnerPublic - internal inner class InnerInternal - protected inner class InnerProtected - private inner class InnerPrivate -} - diff --git a/binary-compatibility-validator/test/cases/nestedClasses/internalInterface.kt b/binary-compatibility-validator/test/cases/nestedClasses/internalInterface.kt deleted file mode 100644 index a0affc53a6..0000000000 --- a/binary-compatibility-validator/test/cases/nestedClasses/internalInterface.kt +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.nestedClasses - -internal interface InternalInterface { - public object ObjPublic - private object ObjPrivate - - public class NestedPublic - private class NestedPrivate - - public interface NestedPublicInterface - private interface NestedPrivateInterface - -} - diff --git a/binary-compatibility-validator/test/cases/nestedClasses/internalObject.kt b/binary-compatibility-validator/test/cases/nestedClasses/internalObject.kt deleted file mode 100644 index ca3fd8344b..0000000000 --- a/binary-compatibility-validator/test/cases/nestedClasses/internalObject.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.nestedClasses - -internal object InternalObject { - - public object ObjPublic - internal object ObjInternal - private object ObjPrivate - - public class NestedPublic - internal class NestedInternal - private class NestedPrivate - - public interface NestedPublicInterface - internal interface NestedInternalInterface - private interface NestedPrivateInterface -} - diff --git a/binary-compatibility-validator/test/cases/nestedClasses/nestedClasses.txt b/binary-compatibility-validator/test/cases/nestedClasses/nestedClasses.txt deleted file mode 100644 index 4ba38c780f..0000000000 --- a/binary-compatibility-validator/test/cases/nestedClasses/nestedClasses.txt +++ /dev/null @@ -1,86 +0,0 @@ -public abstract class cases/nestedClasses/PublicAbstractClass { - public fun ()V -} - -protected final class cases/nestedClasses/PublicAbstractClass$InnerProtected { - public fun (Lcases/nestedClasses/PublicAbstractClass;)V -} - -protected final class cases/nestedClasses/PublicAbstractClass$NestedProtected { - public fun ()V -} - -protected abstract interface class cases/nestedClasses/PublicAbstractClass$NestedProtectedInterface { -} - -protected final class cases/nestedClasses/PublicAbstractClass$ObjProtected { - public static final field INSTANCE Lcases/nestedClasses/PublicAbstractClass$ObjProtected; -} - -public final class cases/nestedClasses/PublicClass { - public fun ()V -} - -public final class cases/nestedClasses/PublicClass$InnerPublic { - public fun (Lcases/nestedClasses/PublicClass;)V -} - -public final class cases/nestedClasses/PublicClass$NestedPublic { - public fun ()V -} - -public abstract interface class cases/nestedClasses/PublicClass$NestedPublicInterface { -} - -public final class cases/nestedClasses/PublicClass$ObjPublic { - public static final field INSTANCE Lcases/nestedClasses/PublicClass$ObjPublic; -} - -public abstract interface class cases/nestedClasses/PublicInterface { -} - -public final class cases/nestedClasses/PublicInterface$NestedPublic { - public fun ()V -} - -public abstract interface class cases/nestedClasses/PublicInterface$NestedPublicInterface { -} - -public final class cases/nestedClasses/PublicInterface$ObjPublic { - public static final field INSTANCE Lcases/nestedClasses/PublicInterface$ObjPublic; -} - -public final class cases/nestedClasses/PublicObject { - public static final field INSTANCE Lcases/nestedClasses/PublicObject; -} - -public final class cases/nestedClasses/PublicObject$NestedPublic { - public fun ()V -} - -public abstract interface class cases/nestedClasses/PublicObject$NestedPublicInterface { -} - -public final class cases/nestedClasses/PublicObject$ObjPublic { - public static final field INSTANCE Lcases/nestedClasses/PublicObject$ObjPublic; -} - -public class cases/nestedClasses/PublicOpenClass { - public fun ()V -} - -protected final class cases/nestedClasses/PublicOpenClass$InnerProtected { - public fun (Lcases/nestedClasses/PublicOpenClass;)V -} - -protected final class cases/nestedClasses/PublicOpenClass$NestedProtected { - public fun ()V -} - -protected abstract interface class cases/nestedClasses/PublicOpenClass$NestedProtectedInterface { -} - -protected final class cases/nestedClasses/PublicOpenClass$ObjProtected { - public static final field INSTANCE Lcases/nestedClasses/PublicOpenClass$ObjProtected; -} - diff --git a/binary-compatibility-validator/test/cases/nestedClasses/privateClass.kt b/binary-compatibility-validator/test/cases/nestedClasses/privateClass.kt deleted file mode 100644 index 1e1fa8a8e3..0000000000 --- a/binary-compatibility-validator/test/cases/nestedClasses/privateClass.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.nestedClasses - -private class PrivateClass { - public object ObjPublic - internal object ObjInternal - protected object ObjProtected - private object ObjPrivate - - public class NestedPublic - internal class NestedInternal - protected class NestedProtected - private class NestedPrivate - - public interface NestedPublicInterface - internal interface NestedInternalInterface - protected interface NestedProtectedInterface - private interface NestedPrivateInterface - - public inner class InnerPublic - internal inner class InnerInternal - protected inner class InnerProtected - private inner class InnerPrivate -} - diff --git a/binary-compatibility-validator/test/cases/nestedClasses/privateInterface.kt b/binary-compatibility-validator/test/cases/nestedClasses/privateInterface.kt deleted file mode 100644 index 8c936a1fdb..0000000000 --- a/binary-compatibility-validator/test/cases/nestedClasses/privateInterface.kt +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.nestedClasses - -private interface PrivateInterface { - public object ObjPublic - private object ObjPrivate - - public class NestedPublic - private class NestedPrivate - - public interface NestedPublicInterface - private interface NestedPrivateInterface - -} - diff --git a/binary-compatibility-validator/test/cases/nestedClasses/privateObject.kt b/binary-compatibility-validator/test/cases/nestedClasses/privateObject.kt deleted file mode 100644 index 4251d25e78..0000000000 --- a/binary-compatibility-validator/test/cases/nestedClasses/privateObject.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.nestedClasses - -private object PrivateObject { - - public object ObjPublic - internal object ObjInternal - private object ObjPrivate - - public class NestedPublic - internal class NestedInternal - private class NestedPrivate - - public interface NestedPublicInterface - internal interface NestedInternalInterface - private interface NestedPrivateInterface -} - diff --git a/binary-compatibility-validator/test/cases/nestedClasses/publicAbstractClass.kt b/binary-compatibility-validator/test/cases/nestedClasses/publicAbstractClass.kt deleted file mode 100644 index 1da59a5b02..0000000000 --- a/binary-compatibility-validator/test/cases/nestedClasses/publicAbstractClass.kt +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.nestedClasses - -public abstract class PublicAbstractClass { - protected object ObjProtected - - protected class NestedProtected - - protected interface NestedProtectedInterface - - protected inner class InnerProtected -} - diff --git a/binary-compatibility-validator/test/cases/nestedClasses/publicClass.kt b/binary-compatibility-validator/test/cases/nestedClasses/publicClass.kt deleted file mode 100644 index 64f487d970..0000000000 --- a/binary-compatibility-validator/test/cases/nestedClasses/publicClass.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.nestedClasses - -public class PublicClass { - public object ObjPublic - internal object ObjInternal - protected object ObjProtected - private object ObjPrivate - - public class NestedPublic - internal class NestedInternal - protected class NestedProtected - private class NestedPrivate - - public interface NestedPublicInterface - internal interface NestedInternalInterface - protected interface NestedProtectedInterface - private interface NestedPrivateInterface - - public inner class InnerPublic - internal inner class InnerInternal - protected inner class InnerProtected - private inner class InnerPrivate -} - diff --git a/binary-compatibility-validator/test/cases/nestedClasses/publicInterface.kt b/binary-compatibility-validator/test/cases/nestedClasses/publicInterface.kt deleted file mode 100644 index 3ee24f549a..0000000000 --- a/binary-compatibility-validator/test/cases/nestedClasses/publicInterface.kt +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.nestedClasses - -public interface PublicInterface { - public object ObjPublic - private object ObjPrivate - - public class NestedPublic - private class NestedPrivate - - public interface NestedPublicInterface - private interface NestedPrivateInterface - -} - diff --git a/binary-compatibility-validator/test/cases/nestedClasses/publicObject.kt b/binary-compatibility-validator/test/cases/nestedClasses/publicObject.kt deleted file mode 100644 index 6ce2bcdd04..0000000000 --- a/binary-compatibility-validator/test/cases/nestedClasses/publicObject.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.nestedClasses - -public object PublicObject { - - public object ObjPublic - internal object ObjInternal - private object ObjPrivate - - public class NestedPublic - internal class NestedInternal - private class NestedPrivate - - public interface NestedPublicInterface - internal interface NestedInternalInterface - private interface NestedPrivateInterface -} - diff --git a/binary-compatibility-validator/test/cases/nestedClasses/publicOpenClass.kt b/binary-compatibility-validator/test/cases/nestedClasses/publicOpenClass.kt deleted file mode 100644 index 1c9edef504..0000000000 --- a/binary-compatibility-validator/test/cases/nestedClasses/publicOpenClass.kt +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.nestedClasses - -public open class PublicOpenClass { - protected object ObjProtected - - protected class NestedProtected - - protected interface NestedProtectedInterface - - protected inner class InnerProtected -} - diff --git a/binary-compatibility-validator/test/cases/private/private.txt b/binary-compatibility-validator/test/cases/private/private.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/binary-compatibility-validator/test/cases/private/privateClassMembers.kt b/binary-compatibility-validator/test/cases/private/privateClassMembers.kt deleted file mode 100644 index 92dd618372..0000000000 --- a/binary-compatibility-validator/test/cases/private/privateClassMembers.kt +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.private - -private open class PrivateClass public constructor() { - internal val internalVal = 1 - - protected fun protectedFun() = internalVal -} diff --git a/binary-compatibility-validator/test/cases/private/privateMultifile1.kt b/binary-compatibility-validator/test/cases/private/privateMultifile1.kt deleted file mode 100644 index 8f7535f869..0000000000 --- a/binary-compatibility-validator/test/cases/private/privateMultifile1.kt +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -@file:JvmName("MultifileKt") -@file:JvmMultifileClass -package cases.private - -private val privateVal: Any? = 1 -private var privateVar: Any? = 1 - - diff --git a/binary-compatibility-validator/test/cases/private/privateMultifile2.kt b/binary-compatibility-validator/test/cases/private/privateMultifile2.kt deleted file mode 100644 index ee0a08049c..0000000000 --- a/binary-compatibility-validator/test/cases/private/privateMultifile2.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -@file:JvmName("MultifileKt") -@file:JvmMultifileClass -package cases.private - - -// const -private const val privateConst: Int = 4 - -// fun -@Suppress("UNUSED_PARAMETER") -private fun privateFun(x: Any) {} - - -private class PrivateClassInMultifile { - internal fun accessUsage() { - privateFun(privateConst) - } - -} diff --git a/binary-compatibility-validator/test/cases/private/privatePart.kt b/binary-compatibility-validator/test/cases/private/privatePart.kt deleted file mode 100644 index 93d4ec14a0..0000000000 --- a/binary-compatibility-validator/test/cases/private/privatePart.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.private - -// properties -private val privateVal: Any? = 1 -private var privateVar: Any? = 1 - -// constants - -private const val privateConst: Int = 4 - -// fun - -@Suppress("UNUSED_PARAMETER") -private fun privateFun(a: Any?) = privateConst - -// access -private class PrivateClassInPart { - internal fun accessUsage() { - privateFun(privateVal) - privateFun(privateVar) - privateFun(privateConst) - } - -} \ No newline at end of file diff --git a/binary-compatibility-validator/test/cases/protected/protected.txt b/binary-compatibility-validator/test/cases/protected/protected.txt deleted file mode 100644 index 3c28d7e163..0000000000 --- a/binary-compatibility-validator/test/cases/protected/protected.txt +++ /dev/null @@ -1,19 +0,0 @@ -public abstract class cases/protected/PublicAbstractClass { - protected fun ()V - protected abstract fun getProtectedVal ()I - protected abstract fun getProtectedVar ()Ljava/lang/Object; - protected abstract fun protectedFun ()V - protected abstract fun setProtectedVar (Ljava/lang/Object;)V -} - -public final class cases/protected/PublicFinalClass { -} - -public class cases/protected/PublicOpenClass { - protected fun ()V - protected final fun getProtectedVal ()I - protected final fun getProtectedVar ()I - protected final fun protectedFun ()I - protected final fun setProtectedVar (I)V -} - diff --git a/binary-compatibility-validator/test/cases/protected/protectedInAbstract.kt b/binary-compatibility-validator/test/cases/protected/protectedInAbstract.kt deleted file mode 100644 index ab4e26fef6..0000000000 --- a/binary-compatibility-validator/test/cases/protected/protectedInAbstract.kt +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.protected - -public abstract class PublicAbstractClass protected constructor() { - protected abstract val protectedVal: Int - protected abstract var protectedVar: Any? - - protected abstract fun protectedFun() -} diff --git a/binary-compatibility-validator/test/cases/protected/protectedInFinal.kt b/binary-compatibility-validator/test/cases/protected/protectedInFinal.kt deleted file mode 100644 index 419e3f420c..0000000000 --- a/binary-compatibility-validator/test/cases/protected/protectedInFinal.kt +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.protected - -public class PublicFinalClass protected constructor() { - protected val protectedVal = 1 - protected var protectedVar = 2 - - protected fun protectedFun() = protectedVal -} diff --git a/binary-compatibility-validator/test/cases/protected/protectedInOpen.kt b/binary-compatibility-validator/test/cases/protected/protectedInOpen.kt deleted file mode 100644 index ee6868354f..0000000000 --- a/binary-compatibility-validator/test/cases/protected/protectedInOpen.kt +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.protected - -public open class PublicOpenClass protected constructor() { - protected val protectedVal = 1 - protected var protectedVar = 2 - - protected fun protectedFun() = protectedVal -} diff --git a/binary-compatibility-validator/test/cases/public/public.txt b/binary-compatibility-validator/test/cases/public/public.txt deleted file mode 100644 index c9dd5dd2b7..0000000000 --- a/binary-compatibility-validator/test/cases/public/public.txt +++ /dev/null @@ -1,9 +0,0 @@ -public final class cases/public/MultifileKt { - public static final fun getPublicVal ()Ljava/lang/String; - public static final fun publicFun1 ()Ljava/lang/String; -} - -public final class cases/public/PublicPartKt { - public static final fun publicFun ()V -} - diff --git a/binary-compatibility-validator/test/cases/public/publicMultifile1.kt b/binary-compatibility-validator/test/cases/public/publicMultifile1.kt deleted file mode 100644 index ccc6823c3e..0000000000 --- a/binary-compatibility-validator/test/cases/public/publicMultifile1.kt +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -@file:JvmName("MultifileKt") -@file:JvmMultifileClass -package cases.public - -public fun publicFun1() = publicVal diff --git a/binary-compatibility-validator/test/cases/public/publicMultifile2.kt b/binary-compatibility-validator/test/cases/public/publicMultifile2.kt deleted file mode 100644 index 174bd43d39..0000000000 --- a/binary-compatibility-validator/test/cases/public/publicMultifile2.kt +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -@file:JvmName("MultifileKt") -@file:JvmMultifileClass -package cases.public - -public val publicVal = "Public" diff --git a/binary-compatibility-validator/test/cases/public/publicPart.kt b/binary-compatibility-validator/test/cases/public/publicPart.kt deleted file mode 100644 index 3ad78362d1..0000000000 --- a/binary-compatibility-validator/test/cases/public/publicPart.kt +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.public - -public fun publicFun() { - -} \ No newline at end of file diff --git a/binary-compatibility-validator/test/cases/special/hidden.kt b/binary-compatibility-validator/test/cases/special/hidden.kt deleted file mode 100644 index cb389b8d0d..0000000000 --- a/binary-compatibility-validator/test/cases/special/hidden.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.special - -@Deprecated("For binary compatibility", level = DeprecationLevel.HIDDEN) -public class HiddenClass - @Deprecated("For binary compatibility", level = DeprecationLevel.HIDDEN) - public constructor() { - - @Deprecated("For binary compatibility", level = DeprecationLevel.HIDDEN) - val hiddenVal = 1 - - @Deprecated("For binary compatibility", level = DeprecationLevel.HIDDEN) - var hiddenVar = 2 - - @Deprecated("For binary compatibility", level = DeprecationLevel.HIDDEN) - fun hiddenFun() {} - - public var varWithHiddenAccessors: String = "" - @Deprecated("For binary compatibility", level = DeprecationLevel.HIDDEN) - get - @Deprecated("For binary compatibility", level = DeprecationLevel.HIDDEN) - set -} - -@Deprecated("For binary compatibility", level = DeprecationLevel.HIDDEN) -fun hiddenTopLevelFun() {} diff --git a/binary-compatibility-validator/test/cases/special/internalLateinitMember.kt b/binary-compatibility-validator/test/cases/special/internalLateinitMember.kt deleted file mode 100644 index e9819fa4e8..0000000000 --- a/binary-compatibility-validator/test/cases/special/internalLateinitMember.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.special - -public class ClassWithLateInitMembers internal constructor() { - - public lateinit var publicLateInitWithInternalSet: String - internal set - - internal lateinit var internalLateInit: String - -} \ No newline at end of file diff --git a/binary-compatibility-validator/test/cases/special/jvmField.kt b/binary-compatibility-validator/test/cases/special/jvmField.kt deleted file mode 100644 index 9a8c911711..0000000000 --- a/binary-compatibility-validator/test/cases/special/jvmField.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.special - -public open class JvmFieldsClass { - @JvmField - public var publicField = "x" - - @JvmField - internal var internalField = "y" - - @JvmField - protected var protectedField = "y" - - public companion object JvmFieldsCompanion { - @JvmField - public var publicСField = "x" - - @JvmField - internal var internalСField = "y" - - @JvmField - protected var protectedСField = "y" - - public const val publicConst = 1 - internal const val internalConst = 2 - protected const val protectedConst = 3 - private const val privateConst = 4 - } -} - -public object JvmFieldsObject { - @JvmField - public var publicField = "x" - - @JvmField - internal var internalField = "y" - - public const val publicConst = 1 - internal const val internalConst = 2 - private const val privateConst = 4 -} - - -@JvmField -public var publicField = "x" - -@JvmField -internal var internalField = "y" - -public const val publicConst = 1 -internal const val internalConst = 2 -private const val privateConst = 4 diff --git a/binary-compatibility-validator/test/cases/special/jvmNames.kt b/binary-compatibility-validator/test/cases/special/jvmNames.kt deleted file mode 100644 index e304de01fe..0000000000 --- a/binary-compatibility-validator/test/cases/special/jvmNames.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.special - -@JvmName("internalFun") -internal fun internalRenamedFun() {} - -internal var internalVar: Int = 1 - @JvmName("internalVarGetter") - get - @JvmName("internalVarSetter") - set - -@JvmName("publicFun") -public fun publicRenamedFun() {} - -public var publicVar: Int = 1 - @JvmName("publicVarGetter") - get - @JvmName("publicVarSetter") - set - - - diff --git a/binary-compatibility-validator/test/cases/special/jvmOverloads.kt b/binary-compatibility-validator/test/cases/special/jvmOverloads.kt deleted file mode 100644 index 8f238138a2..0000000000 --- a/binary-compatibility-validator/test/cases/special/jvmOverloads.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -@file:Suppress("UNUSED_PARAMETER") - -package cases.special - - -@JvmOverloads -public fun publicFunWithOverloads(a: Int = 0, b: String? = null) {} - -@JvmOverloads -internal fun internalFunWithOverloads(a: Int = 0, b: String? = null) {} - -public class ClassWithOverloads - @JvmOverloads - internal constructor(val a: Int = 0, val b: String? = null) { - - @JvmOverloads - internal fun internalFunWithOverloads(a: Int = 0, b: String? = null) {} - -} \ No newline at end of file diff --git a/binary-compatibility-validator/test/cases/special/special.txt b/binary-compatibility-validator/test/cases/special/special.txt deleted file mode 100644 index 40c074d245..0000000000 --- a/binary-compatibility-validator/test/cases/special/special.txt +++ /dev/null @@ -1,61 +0,0 @@ -public final class cases/special/ClassWithLateInitMembers { - public final fun getPublicLateInitWithInternalSet ()Ljava/lang/String; -} - -public final class cases/special/ClassWithOverloads { - public final fun getA ()I - public final fun getB ()Ljava/lang/String; -} - -public final class cases/special/HiddenClass { - public synthetic fun ()V - public final synthetic fun getHiddenVal ()I - public final synthetic fun getHiddenVar ()I - public final synthetic fun getVarWithHiddenAccessors ()Ljava/lang/String; - public final synthetic fun hiddenFun ()V - public final synthetic fun setHiddenVar (I)V - public final synthetic fun setVarWithHiddenAccessors (Ljava/lang/String;)V -} - -public final class cases/special/HiddenKt { - public static final synthetic fun hiddenTopLevelFun ()V -} - -public final class cases/special/JvmFieldKt { - public static final field publicConst I - public static field publicField Ljava/lang/String; -} - -public class cases/special/JvmFieldsClass { - public static final field JvmFieldsCompanion Lcases/special/JvmFieldsClass$JvmFieldsCompanion; - protected static final field protectedConst I - protected field protectedField Ljava/lang/String; - protected static field protectedСField Ljava/lang/String; - public static final field publicConst I - public field publicField Ljava/lang/String; - public static field publicСField Ljava/lang/String; - public fun ()V -} - -public final class cases/special/JvmFieldsClass$JvmFieldsCompanion { -} - -public final class cases/special/JvmFieldsObject { - public static final field INSTANCE Lcases/special/JvmFieldsObject; - public static final field publicConst I - public static field publicField Ljava/lang/String; -} - -public final class cases/special/JvmNamesKt { - public static final fun publicFun ()V - public static final fun publicVarGetter ()I - public static final fun publicVarSetter (I)V -} - -public final class cases/special/JvmOverloadsKt { - public static final fun publicFunWithOverloads ()V - public static final fun publicFunWithOverloads (I)V - public static final fun publicFunWithOverloads (ILjava/lang/String;)V - public static synthetic fun publicFunWithOverloads$default (ILjava/lang/String;ILjava/lang/Object;)V -} - diff --git a/binary-compatibility-validator/test/cases/whenMappings/enumWhen.kt b/binary-compatibility-validator/test/cases/whenMappings/enumWhen.kt deleted file mode 100644 index ff3a1d01b5..0000000000 --- a/binary-compatibility-validator/test/cases/whenMappings/enumWhen.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.whenMappings - -enum class SampleEnum { - A, - B, - C -} - -fun SampleEnum.deacronimize() = when (this) { - SampleEnum.A -> "Apple" - SampleEnum.B -> "Biscuit" - SampleEnum.C -> "Cinnamon" -} - - -inline fun SampleEnum.switch(thenA: () -> Unit, thenB: () -> Unit, thenC: () -> Unit) = when (this) { - SampleEnum.C -> thenC() - SampleEnum.B -> thenB() - SampleEnum.A -> thenA() -} diff --git a/binary-compatibility-validator/test/cases/whenMappings/sealedClassWhen.kt b/binary-compatibility-validator/test/cases/whenMappings/sealedClassWhen.kt deleted file mode 100644 index 5cd80b5d8d..0000000000 --- a/binary-compatibility-validator/test/cases/whenMappings/sealedClassWhen.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package cases.whenMappings - -sealed class SampleSealed { - class A : SampleSealed() - class B : SampleSealed() - class C : SampleSealed() -} - -fun SampleSealed.deacronimize() = when (this) { - is SampleSealed.A -> "Apple" - is SampleSealed.B -> "Biscuit" - is SampleSealed.C -> "Cinnamon" -} - - -inline fun SampleSealed.switch(thenA: () -> Unit, thenB: () -> Unit, thenC: () -> Unit) = when (this) { - is SampleSealed.C -> thenC() - is SampleSealed.B -> thenB() - is SampleSealed.A -> thenA() -} diff --git a/binary-compatibility-validator/test/cases/whenMappings/whenMappings.txt b/binary-compatibility-validator/test/cases/whenMappings/whenMappings.txt deleted file mode 100644 index 1975cf1364..0000000000 --- a/binary-compatibility-validator/test/cases/whenMappings/whenMappings.txt +++ /dev/null @@ -1,33 +0,0 @@ -public final class cases/whenMappings/EnumWhenKt { - public static final fun deacronimize (Lcases/whenMappings/SampleEnum;)Ljava/lang/String; - public static final fun switch (Lcases/whenMappings/SampleEnum;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;)V -} - -public final class cases/whenMappings/SampleEnum : java/lang/Enum { - public static final field A Lcases/whenMappings/SampleEnum; - public static final field B Lcases/whenMappings/SampleEnum; - public static final field C Lcases/whenMappings/SampleEnum; - public static fun valueOf (Ljava/lang/String;)Lcases/whenMappings/SampleEnum; - public static fun values ()[Lcases/whenMappings/SampleEnum; -} - -public abstract class cases/whenMappings/SampleSealed { -} - -public final class cases/whenMappings/SampleSealed$A : cases/whenMappings/SampleSealed { - public fun ()V -} - -public final class cases/whenMappings/SampleSealed$B : cases/whenMappings/SampleSealed { - public fun ()V -} - -public final class cases/whenMappings/SampleSealed$C : cases/whenMappings/SampleSealed { - public fun ()V -} - -public final class cases/whenMappings/SealedClassWhenKt { - public static final fun deacronimize (Lcases/whenMappings/SampleSealed;)Ljava/lang/String; - public static final fun switch (Lcases/whenMappings/SampleSealed;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;)V -} - diff --git a/binary-compatibility-validator/test/utils.kt b/binary-compatibility-validator/test/utils.kt deleted file mode 100644 index c7844108b1..0000000000 --- a/binary-compatibility-validator/test/utils.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.coroutines.tools - -import java.io.* -import kotlin.test.* - -private val OVERWRITE_EXPECTED_OUTPUT = - System.getProperty("overwrite.output")?.toBoolean() ?: false // use -Doverwrite.output=true - -fun List.dumpAndCompareWith(to: File) { - if (!to.exists()) { - to.parentFile?.mkdirs() - to.bufferedWriter().use { dump(to = it) } - fail("Expected data file did not exist. Generated: $to") - } else { - val actual = dump(to = StringBuilder()) - assertEqualsToFile(to, actual) - } -} - -private fun assertEqualsToFile(to: File, actual: CharSequence) { - val actualText = actual.trimTrailingWhitespacesAndAddNewlineAtEOF() - val expectedText = to.readText().trimTrailingWhitespacesAndAddNewlineAtEOF() - if (expectedText == actualText) return // Ok - // Difference - if (OVERWRITE_EXPECTED_OUTPUT) { - to.writeText(actualText) - println("Generated: $to") - return // make test pass when overwriting output - } - // Fail on difference - assertEquals( - expectedText, - actualText, - "Actual data differs from file content: ${to.name}\nTo overwrite the expected API rerun with -Doverwrite.output=true parameter\n" - ) -} - -private fun CharSequence.trimTrailingWhitespacesAndAddNewlineAtEOF(): String = - this.lineSequence().map { it.trimEnd() }.joinToString(separator = "\n").let { - if (it.endsWith("\n")) it else it + "\n" - } - - -private val UPPER_CASE_CHARS = Regex("[A-Z]+") diff --git a/build.gradle b/build.gradle index 8fd5441e5e..b6d0483afe 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ import org.jetbrains.kotlin.konan.target.HostManager @@ -8,8 +8,8 @@ apply from: rootProject.file("gradle/experimental.gradle") def rootModule = "kotlinx.coroutines" def coreModule = "kotlinx-coroutines-core" // Not applicable for Kotlin plugin -def sourceless = ['kotlinx.coroutines', 'site', 'kotlinx-coroutines-bom'] -def internal = ['kotlinx.coroutines', 'site', 'benchmarks', 'knit', 'js-stub', 'stdlib-stubs', 'binary-compatibility-validator'] +def sourceless = ['kotlinx.coroutines', 'site', 'kotlinx-coroutines-bom', 'publication-validator'] +def internal = ['kotlinx.coroutines', 'site', 'benchmarks', 'js-stub', 'stdlib-stubs', 'publication-validator'] // Not published def unpublished = internal + ['example-frontend-js', 'android-unit-tests'] @@ -41,7 +41,16 @@ buildscript { } } - if (build_snapshot_train || atomicfu_version.endsWith("-SNAPSHOT")) { + // Determine if any project dependency is using a snapshot version + ext.using_snapshot_version = build_snapshot_train + 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 + } + } + + if (using_snapshot_version) { repositories { mavenLocal() maven { url "https://oss.sonatype.org/content/repositories/snapshots" } @@ -51,7 +60,13 @@ buildscript { repositories { jcenter() maven { url "https://kotlin.bintray.com/kotlinx" } - maven { url "https://kotlin.bintray.com/kotlin-dev" } + maven { + url "https://kotlin.bintray.com/kotlin-dev" + credentials { + username = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : System.getenv('BINTRAY_USER') ?: "" + password = project.hasProperty('bintrayApiKey') ? project.property('bintrayApiKey') : System.getenv('BINTRAY_API_KEY') ?: "" + } + } maven { url "https://kotlin.bintray.com/kotlin-eap" } maven { url "https://jetbrains.bintray.com/kotlin-native-dependencies" } maven { url "https://plugins.gradle.org/m2/" } @@ -61,7 +76,10 @@ buildscript { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version" classpath "org.jetbrains.kotlinx:atomicfu-gradle-plugin:$atomicfu_version" + classpath "org.jetbrains.kotlinx:kotlinx-knit:$knit_version" classpath "com.moowork.gradle:gradle-node-plugin:$gradle_node_version" + classpath "org.openjfx:javafx-plugin:$javafx_plugin_version" + classpath "org.jetbrains.kotlinx:binary-compatibility-validator:$binary_compatibility_validator_version" // JMH plugins classpath "com.github.jengelman.gradle.plugins:shadow:5.1.0" @@ -95,7 +113,7 @@ allprojects { kotlin_version = rootProject.properties['kotlin_snapshot_version'] } - if (build_snapshot_train || atomicfu_version.endsWith("-SNAPSHOT")) { + if (using_snapshot_version) { repositories { mavenLocal() maven { url "https://oss.sonatype.org/content/repositories/snapshots" } @@ -113,46 +131,59 @@ allprojects { } } +apply plugin: "binary-compatibility-validator" +apiValidation { + ignoredProjects += unpublished + ["kotlinx-coroutines-bom"] + ignoredPackages += "kotlinx.coroutines.internal" +} + +// Configure repositories allprojects { - apply plugin: 'kotlinx-atomicfu' // it also adds all the necessary dependencies - def projectName = it.name + String projectName = it.name repositories { /* * google should be first in the repository list because some of the play services * transitive dependencies was removed from jcenter, thus breaking gradle dependency resolution */ - if (projectName == "kotlinx-coroutines-play-services") { - google() - } + google() jcenter() - maven { url "https://kotlin.bintray.com/kotlin-dev" } + maven { + url "https://kotlin.bintray.com/kotlin-dev" + credentials { + username = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : System.getenv('BINTRAY_USER') ?: "" + password = project.hasProperty('bintrayApiKey') ? project.property('bintrayApiKey') : System.getenv('BINTRAY_API_KEY') ?: "" + } + } maven { url "https://kotlin.bintray.com/kotlin-eap" } maven { url "https://kotlin.bintray.com/kotlinx" } } +} - if (projectName == rootModule || projectName == coreModule) return - - // Add dependency to core source sets. Core is configured in kx-core/build.gradle +// Add dependency to core source sets. Core is configured in kx-core/build.gradle +configure(subprojects.findAll { !sourceless.contains(it.name) && it.name != coreModule }) { evaluationDependsOn(":$coreModule") - if (sourceless.contains(projectName)) return - def platform = platformOf(it) apply from: rootProject.file("gradle/compile-${platform}.gradle") - dependencies { // See comment below for rationale, it will be replaced with "project" dependency compile "org.jetbrains.kotlinx:kotlinx-coroutines-core:$version" - // the only way IDEA can resolve test classes testCompile project(":$coreModule").kotlin.targets.jvm.compilations.test.output.allOutputs } +} + +// Configure subprojects with Kotlin sources +configure(subprojects.findAll { !sourceless.contains(it.name) }) { + // Use atomicfu plugin, it also adds all the necessary dependencies + apply plugin: 'kotlinx-atomicfu' + // 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 += "-progressive" kotlinOptions.freeCompilerArgs += "-XXLanguage:+InlineClasses" - // Binary compatibility support - kotlinOptions.freeCompilerArgs += ["-Xdump-declarations-to=${buildDir}/visibilities.json"] + // Remove null assertions to get smaller bytecode on Android + kotlinOptions.freeCompilerArgs += ["-Xno-param-assertions", "-Xno-receiver-assertions", "-Xno-call-assertions"] } } @@ -166,6 +197,8 @@ if (build_snapshot_train) { exclude '**/*scheduling*' exclude '**/*Timeout*' exclude '**/*definitely/not/kotlinx*' + // Disable because of KT-11567 in 1.4 + exclude '**/*CasesPublicAPITest*' } } @@ -228,7 +261,7 @@ configure(subprojects.findAll { !unpublished.contains(it.name) }) { tasks.withType(dokka.getClass()) { externalDocumentationLink { url = new URL(core_docs_url) - packageListUrl = new URL("file://$core_docs_file") + packageListUrl = new File(core_docs_file).toURI().toURL() } } } @@ -238,9 +271,38 @@ configure(subprojects.findAll { !unpublished.contains(it.name) }) { // Report Kotlin compiler version when building project println("Using Kotlin compiler version: $org.jetbrains.kotlin.config.KotlinCompilerVersion.VERSION") +// --------------- Publish only from under JDK11+ --------------- +task checkJdkForPublish { + doFirst { + String javaVersion = System.properties["java.version"] + int i = javaVersion.indexOf('.') + int javaVersionMajor = (i < 0 ? javaVersion : javaVersion.substring(0, i)).toInteger() + if (javaVersionMajor < 11) { + throw new GradleException("Project can be build for publishing only under JDK 11+, but found ${javaVersion}") + } + } +} + // --------------- Configure sub-projects that are published --------------- -task deploy(dependsOn: getTasksByName("publish", true) + getTasksByName("publishNpm", true)) +def publishTasks = getTasksByName("publish", true) + getTasksByName("publishNpm", true) + +publishTasks.each { + it.dependsOn checkJdkForPublish +} + +task deploy(dependsOn: publishTasks) apply plugin: 'base' clean.dependsOn gradle.includedBuilds.collect { it.task(':clean') } + +// --------------- Knit configuration --------------- + +apply plugin: 'kotlinx-knit' + +knit { + siteRoot = "https://kotlin.github.io/kotlinx.coroutines" + moduleRoots = [".", "integration", "reactive", "ui"] +} + +knitPrepare.dependsOn getTasksByName("dokka", true) \ No newline at end of file diff --git a/docs/basics.md b/docs/basics.md index 6c3c0caa78..c5e931c58a 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -1,20 +1,4 @@ - - - + **Table of contents** @@ -30,8 +14,7 @@ class BasicsGuideTest { * [Coroutines ARE light-weight](#coroutines-are-light-weight) * [Global coroutines are like daemon threads](#global-coroutines-are-like-daemon-threads) - - + ## Coroutine Basics @@ -142,7 +125,7 @@ fun main() = runBlocking { // start main coroutine -> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-basic-02b.kt). +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-basic-03.kt). @@ -396,7 +379,7 @@ fun main() = runBlocking { -> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-basic-07.kt). +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-basic-09.kt). You can run and see that it prints three lines and terminates: diff --git a/docs/cancellation-and-timeouts.md b/docs/cancellation-and-timeouts.md index ef4a9c9e09..b51c45c941 100644 --- a/docs/cancellation-and-timeouts.md +++ b/docs/cancellation-and-timeouts.md @@ -1,20 +1,5 @@ - -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.$$1$$2 ---> - - **Table of contents** @@ -27,7 +12,7 @@ class CancellationTimeOutsGuideTest { * [Run non-cancellable block](#run-non-cancellable-block) * [Timeout](#timeout) - + ## Cancellation and Timeouts diff --git a/docs/channels.md b/docs/channels.md index 6f5f06977b..6769b8dc52 100644 --- a/docs/channels.md +++ b/docs/channels.md @@ -1,20 +1,5 @@ - -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.$$1$$2 ---> - - **Table of contents** @@ -31,7 +16,7 @@ class ChannelsGuideTest { * [Channels are fair](#channels-are-fair) * [Ticker channels](#ticker-channels) - + ## Channels diff --git a/docs/compatibility.md b/docs/compatibility.md index e56fc1be15..8dafae7293 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -1,9 +1,3 @@ - - * [Compatibility](#compatibility) @@ -19,7 +13,7 @@ * [Gradle](#gradle) * [Maven](#maven) - + ## Compatibility This document describes the compatibility policy of `kotlinx.coroutines` library since version 1.0.0 and semantics of compatibility-specific annotations. diff --git a/docs/composing-suspending-functions.md b/docs/composing-suspending-functions.md index 0cd02762ee..6a95d7585e 100644 --- a/docs/composing-suspending-functions.md +++ b/docs/composing-suspending-functions.md @@ -1,20 +1,4 @@ - - - + **Table of contents** @@ -27,7 +11,7 @@ class ComposingGuideTest { * [Async-style functions](#async-style-functions) * [Structured concurrency with async](#structured-concurrency-with-async) - + ## Composing Suspending Functions diff --git a/docs/coroutine-context-and-dispatchers.md b/docs/coroutine-context-and-dispatchers.md index 558b039744..e379842443 100644 --- a/docs/coroutine-context-and-dispatchers.md +++ b/docs/coroutine-context-and-dispatchers.md @@ -1,20 +1,4 @@ - - - + **Table of contents** @@ -33,7 +17,7 @@ class DispatchersGuideTest { * [Coroutine scope](#coroutine-scope) * [Thread-local data](#thread-local-data) - + ## Coroutine Context and Dispatchers diff --git a/docs/debugging.md b/docs/debugging.md index e2c7ec1e07..6c846f235d 100644 --- a/docs/debugging.md +++ b/docs/debugging.md @@ -8,11 +8,12 @@ * [Stacktrace recovery machinery](#stacktrace-recovery-machinery) * [Debug agent](#debug-agent) * [Debug agent and Android](#debug-agent-and-android) +* [Android optimization](#android-optimization) - - + ## Debugging coroutines + Debugging asynchronous programs is challenging, because multiple concurrent coroutines are typically working at the same time. To help with that, `kotlinx.coroutines` comes with additional features for debugging: debug mode, stacktrace recovery and debug agent. @@ -87,6 +88,14 @@ java.lang.NoClassDefFoundError: Failed resolution of: Ljava/lang/management/Mana at kotlinx.coroutines.debug.DebugProbes.install(DebugProbes.kt:49) --> +## Android optimization + +In optimized (release) builds with R8 version 1.6.0 or later both +[Debugging mode](../../docs/debugging.md#debug-mode) and +[Stacktrace recovery](../../docs/debugging.md#stacktrace-recovery) +are permanently turned off. +For more details see ["Optimization" section for Android](../ui/kotlinx-coroutines-android/README.md#optimization). + [DEBUG_PROPERTY_NAME]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-d-e-b-u-g_-p-r-o-p-e-r-t-y_-n-a-m-e.html diff --git a/docs/exception-handling.md b/docs/exception-handling.md index 178f528ae2..08e63ea994 100644 --- a/docs/exception-handling.md +++ b/docs/exception-handling.md @@ -1,20 +1,5 @@ - -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.$$1$$2 ---> - - **Table of contents** @@ -29,7 +14,7 @@ class ExceptionsGuideTest { * [Supervision scope](#supervision-scope) * [Exceptions in supervised coroutines](#exceptions-in-supervised-coroutines) - + ## Exception Handling @@ -260,7 +245,6 @@ to leak to its exception handler. diff --git a/docs/flow.md b/docs/flow.md index ce4e80f1bb..705f338b20 100644 --- a/docs/flow.md +++ b/docs/flow.md @@ -1,20 +1,4 @@ - - - + **Table of contents** @@ -60,7 +44,7 @@ class FlowGuideTest { * [Launching flow](#launching-flow) * [Flow and Reactive Streams](#flow-and-reactive-streams) - + ## Asynchronous Flow diff --git a/docs/knit.code.include b/docs/knit.code.include new file mode 100644 index 0000000000..42f2b50e81 --- /dev/null +++ b/docs/knit.code.include @@ -0,0 +1,6 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from ${file.name} by Knit tool. Do not edit. +package ${knit.package}.${knit.name} \ No newline at end of file diff --git a/docs/knit.properties b/docs/knit.properties new file mode 100644 index 0000000000..ab2508a114 --- /dev/null +++ b/docs/knit.properties @@ -0,0 +1,22 @@ +# +# Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. +# + +knit.package=kotlinx.coroutines.guide +knit.dir=../kotlinx-coroutines-core/jvm/test/guide/ +knit.pattern=example-[a-zA-Z0-9-]+-##\\.kt +knit.include=knit.code.include + +test.package=kotlinx.coroutines.guide.test +test.dir=../kotlinx-coroutines-core/jvm/test/guide/test/ +test.template=knit.test.template + +# Various test validation modes and their corresponding methods from TestUtil +test.mode.=verifyLines +test.mode.STARTS_WITH=verifyLinesStartWith +test.mode.ARBITRARY_TIME=verifyLinesArbitraryTime +test.mode.FLEXIBLE_TIME=verifyLinesFlexibleTime +test.mode.FLEXIBLE_THREAD=verifyLinesFlexibleThread +test.mode.LINES_START_UNORDERED=verifyLinesStartUnordered +test.mode.LINES_START=verifyLinesStart +test.mode.EXCEPTION=verifyExceptions \ No newline at end of file diff --git a/docs/knit.test.template b/docs/knit.test.template new file mode 100644 index 0000000000..a912555a43 --- /dev/null +++ b/docs/knit.test.template @@ -0,0 +1,27 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from ${file.name} by Knit tool. Do not edit. +package ${test.package} + +import org.junit.Test + +class ${test.name} { +<#list cases as case><#assign method = test["mode.${case.param}"]!"custom"> + @Test + fun test${case.name}() { + test("${case.name}") { ${case.knit.package}.${case.knit.name}.main() }<#if method != "custom">.${method}( +<#list case.lines as line> + "${line?j_string}"<#sep>, + + ) +<#else>.also { lines -> + check(${case.param}) + } + + } +<#sep> + + +} \ No newline at end of file diff --git a/docs/select-expression.md b/docs/select-expression.md index f36fa09b6b..5809e7b93e 100644 --- a/docs/select-expression.md +++ b/docs/select-expression.md @@ -1,21 +1,4 @@ - - - - + **Table of contents** @@ -28,9 +11,7 @@ class SelectGuideTest { * [Selecting deferred values](#selecting-deferred-values) * [Switch over a channel of deferred values](#switch-over-a-channel-of-deferred-values) - - - + ## Select Expression (experimental) diff --git a/docs/shared-mutable-state-and-concurrency.md b/docs/shared-mutable-state-and-concurrency.md index 30d7334e6c..1a3c406472 100644 --- a/docs/shared-mutable-state-and-concurrency.md +++ b/docs/shared-mutable-state-and-concurrency.md @@ -1,20 +1,5 @@ - - - + + **Table of contents** @@ -28,7 +13,7 @@ class SharedStateGuideTest { * [Mutual exclusion](#mutual-exclusion) * [Actors](#actors) - + ## Shared mutable state and concurrency diff --git a/gradle.properties b/gradle.properties index b87d2b009f..53a8de2687 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,22 +1,32 @@ # -# Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. +# Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. # # Kotlin -version=1.3.3-SNAPSHOT +version=1.3.4-SNAPSHOT group=org.jetbrains.kotlinx -kotlin_version=1.3.61 +kotlin_version=1.3.70 # Dependencies junit_version=4.12 -atomicfu_version=0.14.1 +atomicfu_version=0.14.2 +knit_version=0.1.3 html_version=0.6.8 -lincheck_version=2.0 +lincheck_version=2.5.3 dokka_version=0.9.16-rdev-2-mpp-hacks byte_buddy_version=1.9.3 reactor_vesion=3.2.5.RELEASE reactive_streams_version=1.0.2 rxjava2_version=2.2.8 +javafx_version=11.0.2 +javafx_plugin_version=0.0.8 +binary_compatibility_validator_version=0.1.1 + +# Android versions +android_version=4.1.1.4 +android_support_version=26.1.0 +robolectric_version=4.0.2 +baksmali_version=2.2.7 # JS gradle_node_version=1.2.0 @@ -30,3 +40,6 @@ source_map_support_version=0.5.3 # Settings kotlin.incremental.multiplatform=true kotlin.native.ignoreDisabledTargets=true + +# Site deneration +jekyll_version=4.0 diff --git a/gradle/compile-common.gradle b/gradle/compile-common.gradle index ebad56a312..403c3345d0 100644 --- a/gradle/compile-common.gradle +++ b/gradle/compile-common.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ kotlin.sourceSets { diff --git a/gradle/compile-js-multiplatform.gradle b/gradle/compile-js-multiplatform.gradle index a9a4ea29c6..a7da5c6377 100644 --- a/gradle/compile-js-multiplatform.gradle +++ b/gradle/compile-js-multiplatform.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ apply from: rootProject.file('gradle/node-js.gradle') diff --git a/gradle/compile-js.gradle b/gradle/compile-js.gradle index 4b1e3bde7f..d0697cfd3a 100644 --- a/gradle/compile-js.gradle +++ b/gradle/compile-js.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ // Platform-specific configuration to compile JS modules diff --git a/gradle/compile-jvm-multiplatform.gradle b/gradle/compile-jvm-multiplatform.gradle index f6d76fcada..b226c97a57 100644 --- a/gradle/compile-jvm-multiplatform.gradle +++ b/gradle/compile-jvm-multiplatform.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ sourceCompatibility = 1.6 diff --git a/gradle/compile-jvm.gradle b/gradle/compile-jvm.gradle index 3ab25456f8..261136bc87 100644 --- a/gradle/compile-jvm.gradle +++ b/gradle/compile-jvm.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ // Platform-specific configuration to compile JVM modules diff --git a/gradle/compile-native-multiplatform.gradle b/gradle/compile-native-multiplatform.gradle index a51057ee3e..378e4f5f98 100644 --- a/gradle/compile-native-multiplatform.gradle +++ b/gradle/compile-native-multiplatform.gradle @@ -1,3 +1,7 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + project.ext.nativeMainSets = [] project.ext.nativeTestSets = [] diff --git a/gradle/dokka.gradle b/gradle/dokka.gradle index 99201a9f28..bc22189e14 100644 --- a/gradle/dokka.gradle +++ b/gradle/dokka.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * 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 diff --git a/gradle/experimental.gradle b/gradle/experimental.gradle index 51bda6c595..b045a1f630 100644 --- a/gradle/experimental.gradle +++ b/gradle/experimental.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ // For new mpp diff --git a/gradle/maven-central.gradle b/gradle/maven-central.gradle index 4f9df6ab75..eef7993921 100644 --- a/gradle/maven-central.gradle +++ b/gradle/maven-central.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ // --------------- pom configuration --------------- diff --git a/gradle/node-js.gradle b/gradle/node-js.gradle index 208f4ad293..d4bd86ca56 100644 --- a/gradle/node-js.gradle +++ b/gradle/node-js.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ apply plugin: 'com.moowork.node' diff --git a/gradle/publish-bintray.gradle b/gradle/publish-bintray.gradle index 9062896b58..c8dd8f12b5 100644 --- a/gradle/publish-bintray.gradle +++ b/gradle/publish-bintray.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ // Configures publishing of Maven artifacts to Bintray diff --git a/gradle/publish-npm-js.gradle b/gradle/publish-npm-js.gradle index a2991db492..f471c48047 100644 --- a/gradle/publish-npm-js.gradle +++ b/gradle/publish-npm-js.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ def prop(name, defVal) { diff --git a/gradle/targets.gradle b/gradle/targets.gradle index d4e560fd5a..08f3d989aa 100644 --- a/gradle/targets.gradle +++ b/gradle/targets.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ /* diff --git a/gradle/test-mocha-js.gradle b/gradle/test-mocha-js.gradle index af9d892b07..464898e584 100644 --- a/gradle/test-mocha-js.gradle +++ b/gradle/test-mocha-js.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ // -- Testing with Mocha under Node diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c141e3345e..7068436015 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ # -# Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. +# Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. # distributionBase=GRADLE_USER_HOME diff --git a/integration/README.md b/integration/README.md index 44f270c059..89100179a8 100644 --- a/integration/README.md +++ b/integration/README.md @@ -23,5 +23,6 @@ Follow the following simple guidelines when contributing integration with your f * List of modules in this document. * List of modules in top-level [`settings.gradle`](../settings.gradle). * List of modules at the root of documentation site in [`site/docs/index.md`](../site/docs/index.md). + * List of integrations in the root [README.md](../README.md). * Update links to documentation website as explained [here](../knit/README.md#usage). * Squash your contribution to a single commit and create pull request to `develop` branch. diff --git a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-guava.txt b/integration/kotlinx-coroutines-guava/api/kotlinx-coroutines-guava.api similarity index 100% rename from binary-compatibility-validator/reference-public-api/kotlinx-coroutines-guava.txt rename to integration/kotlinx-coroutines-guava/api/kotlinx-coroutines-guava.api diff --git a/integration/kotlinx-coroutines-guava/build.gradle b/integration/kotlinx-coroutines-guava/build.gradle index 9e44b99864..16bdea50fd 100644 --- a/integration/kotlinx-coroutines-guava/build.gradle +++ b/integration/kotlinx-coroutines-guava/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ ext.guava_version = '28.0-jre' @@ -13,4 +13,4 @@ tasks.withType(dokka.getClass()) { url = new URL("https://google.github.io/guava/releases/$guava_version/api/docs/") packageListUrl = projectDir.toPath().resolve("package.list").toUri().toURL() } -} \ No newline at end of file +} diff --git a/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt b/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt index e502ff464f..974e246283 100644 --- a/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt +++ b/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.guava diff --git a/integration/kotlinx-coroutines-guava/test/ListenableFutureTest.kt b/integration/kotlinx-coroutines-guava/test/ListenableFutureTest.kt index 80cc22a7bb..a9a7f7ba9d 100644 --- a/integration/kotlinx-coroutines-guava/test/ListenableFutureTest.kt +++ b/integration/kotlinx-coroutines-guava/test/ListenableFutureTest.kt @@ -6,12 +6,11 @@ package kotlinx.coroutines.guava import com.google.common.util.concurrent.* import kotlinx.coroutines.* -import org.hamcrest.core.* import org.junit.* -import org.junit.Assert.* import org.junit.Test import java.util.concurrent.* import java.util.concurrent.CancellationException +import kotlin.test.* class ListenableFutureTest : TestBase() { @Before @@ -27,7 +26,7 @@ class ListenableFutureTest : TestBase() { "O" }).await() + "K" } - assertThat(future.get(), IsEqual("OK")) + assertEquals("OK", future.get()) } @Test @@ -64,7 +63,7 @@ class ListenableFutureTest : TestBase() { val future = GlobalScope.future { toAwait.await() + "K" } - assertThat(future.get(), IsEqual("OK")) + assertEquals("OK", future.get()) } @Test @@ -75,7 +74,7 @@ class ListenableFutureTest : TestBase() { } assertFalse(future.isDone) toAwait.set("O") - assertThat(future.get(), IsEqual("OK")) + assertEquals("OK", future.get()) } @Test @@ -86,11 +85,11 @@ class ListenableFutureTest : TestBase() { try { toAwait.await() } catch (e: RuntimeException) { - assertThat(e, IsInstanceOf(IllegalArgumentException::class.java)) + assertTrue(e is IllegalArgumentException) e.message!! } + "K" } - assertThat(future.get(), IsEqual("OK")) + assertEquals("OK", future.get()) } @Test @@ -100,13 +99,13 @@ class ListenableFutureTest : TestBase() { try { toAwait.await() } catch (e: RuntimeException) { - assertThat(e, IsInstanceOf(IllegalArgumentException::class.java)) + assertTrue(e is IllegalArgumentException) e.message!! } + "K" } assertFalse(future.isDone) toAwait.setException(IllegalArgumentException("O")) - assertThat(future.get(), IsEqual("OK")) + assertEquals("OK", future.get()) } @Test @@ -122,8 +121,8 @@ class ListenableFutureTest : TestBase() { future.get() fail("'get' should've throw an exception") } catch (e: ExecutionException) { - assertThat(e.cause, IsInstanceOf(IllegalStateException::class.java)) - assertThat(e.cause!!.message, IsEqual("OK")) + assertTrue(e.cause is IllegalStateException) + assertEquals("OK", e.cause!!.message) } } @@ -134,7 +133,7 @@ class ListenableFutureTest : TestBase() { GlobalScope.future(start = CoroutineStart.LAZY) {} } - assertThat(e.message, IsEqual("LAZY start is not supported")) + assertEquals("LAZY start is not supported", e.message) finish(2) } @@ -147,7 +146,7 @@ class ListenableFutureTest : TestBase() { } expect(3) val future = deferred.asListenableFuture() - assertThat(future.await(), IsEqual("OK")) + assertEquals("OK", future.await()) finish(4) } @@ -160,7 +159,7 @@ class ListenableFutureTest : TestBase() { } expect(2) val future = deferred.asListenableFuture() - assertThat(future.await(), IsEqual("OK")) // await yields main thread to deferred coroutine + assertEquals("OK", future.await()) // await yields main thread to deferred coroutine finish(4) } @@ -370,9 +369,7 @@ class ListenableFutureTest : TestBase() { assertTrue(asFutureAsDeferred.isCompleted) // By documentation, join() shouldn't throw when asDeferred is already complete. asFutureAsDeferred.join() - assertThat( - asFutureAsDeferred.getCompletionExceptionOrNull(), - IsInstanceOf(CancellationException::class.java)) + assertTrue(asFutureAsDeferred.getCompletionExceptionOrNull() is CancellationException) } @Test @@ -395,9 +392,7 @@ class ListenableFutureTest : TestBase() { assertTrue(asDeferred.isCompleted) // By documentation, join() shouldn't throw when asDeferred is already complete. asDeferred.join() - assertThat( - asDeferred.getCompletionExceptionOrNull(), - IsInstanceOf(CancellationException::class.java)) + assertTrue(asDeferred.getCompletionExceptionOrNull() is CancellationException) } @Test diff --git a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-jdk8.txt b/integration/kotlinx-coroutines-jdk8/api/kotlinx-coroutines-jdk8.api similarity index 100% rename from binary-compatibility-validator/reference-public-api/kotlinx-coroutines-jdk8.txt rename to integration/kotlinx-coroutines-jdk8/api/kotlinx-coroutines-jdk8.api diff --git a/integration/kotlinx-coroutines-jdk8/build.gradle b/integration/kotlinx-coroutines-jdk8/build.gradle index 3b17101f53..099159292e 100644 --- a/integration/kotlinx-coroutines-jdk8/build.gradle +++ b/integration/kotlinx-coroutines-jdk8/build.gradle @@ -1,4 +1,4 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ diff --git a/integration/kotlinx-coroutines-jdk8/src/future/Future.kt b/integration/kotlinx-coroutines-jdk8/src/future/Future.kt index 16829a8018..3cd848f2cf 100644 --- a/integration/kotlinx-coroutines-jdk8/src/future/Future.kt +++ b/integration/kotlinx-coroutines-jdk8/src/future/Future.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.future diff --git a/integration/kotlinx-coroutines-jdk8/src/stream/Stream.kt b/integration/kotlinx-coroutines-jdk8/src/stream/Stream.kt index 1b5e479fd4..641a83a682 100644 --- a/integration/kotlinx-coroutines-jdk8/src/stream/Stream.kt +++ b/integration/kotlinx-coroutines-jdk8/src/stream/Stream.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.stream diff --git a/integration/kotlinx-coroutines-jdk8/src/time/Time.kt b/integration/kotlinx-coroutines-jdk8/src/time/Time.kt index 031ac61fd9..1673116fac 100644 --- a/integration/kotlinx-coroutines-jdk8/src/time/Time.kt +++ b/integration/kotlinx-coroutines-jdk8/src/time/Time.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.time diff --git a/integration/kotlinx-coroutines-jdk8/test/future/AsFutureTest.kt b/integration/kotlinx-coroutines-jdk8/test/future/AsFutureTest.kt index 72a1228b95..743816fa85 100644 --- a/integration/kotlinx-coroutines-jdk8/test/future/AsFutureTest.kt +++ b/integration/kotlinx-coroutines-jdk8/test/future/AsFutureTest.kt @@ -5,10 +5,10 @@ package kotlinx.coroutines.future import kotlinx.coroutines.* -import org.junit.* -import org.junit.Assert.* +import org.junit.Test import java.util.concurrent.* import java.util.concurrent.CancellationException +import kotlin.test.* class AsFutureTest : TestBase() { diff --git a/integration/kotlinx-coroutines-jdk8/test/future/FutureExceptionsTest.kt b/integration/kotlinx-coroutines-jdk8/test/future/FutureExceptionsTest.kt index 86b60e5ff4..0c919b1846 100644 --- a/integration/kotlinx-coroutines-jdk8/test/future/FutureExceptionsTest.kt +++ b/integration/kotlinx-coroutines-jdk8/test/future/FutureExceptionsTest.kt @@ -5,10 +5,10 @@ package kotlinx.coroutines.future import kotlinx.coroutines.* -import org.junit.* -import org.junit.Assert.* +import org.junit.Test import java.io.* import java.util.concurrent.* +import kotlin.test.* class FutureExceptionsTest : TestBase() { diff --git a/integration/kotlinx-coroutines-jdk8/test/future/FutureTest.kt b/integration/kotlinx-coroutines-jdk8/test/future/FutureTest.kt index 4649645efb..f75c96746c 100644 --- a/integration/kotlinx-coroutines-jdk8/test/future/FutureTest.kt +++ b/integration/kotlinx-coroutines-jdk8/test/future/FutureTest.kt @@ -6,9 +6,8 @@ package kotlinx.coroutines.future import kotlinx.coroutines.* import kotlinx.coroutines.CancellationException -import org.hamcrest.core.* import org.junit.* -import org.junit.Assert.* +import org.junit.Test import java.util.concurrent.* import java.util.concurrent.atomic.* import java.util.concurrent.locks.* @@ -16,6 +15,7 @@ import java.util.function.* import kotlin.concurrent.* import kotlin.coroutines.* import kotlin.reflect.* +import kotlin.test.* class FutureTest : TestBase() { @Before @@ -30,7 +30,7 @@ class FutureTest : TestBase() { "O" }.await() + "K" } - assertThat(future.get(), IsEqual("OK")) + assertEquals("OK", future.get()) } @Test @@ -40,7 +40,7 @@ class FutureTest : TestBase() { val future = GlobalScope.future { toAwait.await() + "K" } - assertThat(future.get(), IsEqual("OK")) + assertEquals("OK", future.get()) } @Test @@ -51,7 +51,7 @@ class FutureTest : TestBase() { val future = GlobalScope.future { toAwait.await() + "K" } - assertThat(future.get(), IsEqual("OK")) + assertEquals("OK", future.get()) } @Test @@ -62,7 +62,7 @@ class FutureTest : TestBase() { } assertFalse(future.isDone) toAwait.complete("O") - assertThat(future.get(), IsEqual("OK")) + assertEquals("OK", future.get()) } @Test @@ -74,7 +74,7 @@ class FutureTest : TestBase() { } assertFalse(future.isDone) completable.complete("O") - assertThat(future.get(), IsEqual("OK")) + assertEquals("OK", future.get()) } @Test @@ -88,7 +88,7 @@ class FutureTest : TestBase() { e.message!! } + "K" } - assertThat(future.get(), IsEqual("OK")) + assertEquals("OK", future.get()) } @Test @@ -104,7 +104,7 @@ class FutureTest : TestBase() { e.message!! } + "K" } - assertThat(future.get(), IsEqual("OK")) + assertEquals("OK", future.get()) } @Test @@ -125,7 +125,7 @@ class FutureTest : TestBase() { assertFalse(future.isDone) toAwait.completeExceptionally(TestException("O")) yield() // to future coroutine - assertThat(future.get(), IsEqual("OK")) + assertEquals("OK", future.get()) finish(5) } @@ -142,7 +142,7 @@ class FutureTest : TestBase() { } assertFalse(future.isDone) completable.completeExceptionally(TestException("O")) - assertThat(future.get(), IsEqual("OK")) + assertEquals("OK", future.get()) } @Test @@ -158,7 +158,7 @@ class FutureTest : TestBase() { fail("'get' should've throw an exception") } catch (e: ExecutionException) { assertTrue(e.cause is IllegalStateException) - assertThat(e.cause!!.message, IsEqual("OK")) + assertEquals("OK", e.cause!!.message) } } @@ -191,22 +191,22 @@ class FutureTest : TestBase() { it() depth.andDecrement }) { - assertEquals("Part before first suspension must be wrapped", 1, depth.get()) + assertEquals(1, depth.get(), "Part before first suspension must be wrapped") val result = CompletableFuture.supplyAsync { while (depth.get() > 0); - assertEquals("Part inside suspension point should not be wrapped", 0, depth.get()) + assertEquals(0, depth.get(), "Part inside suspension point should not be wrapped") "OK" }.await() - assertEquals("Part after first suspension should be wrapped", 1, depth.get()) + assertEquals(1, depth.get(), "Part after first suspension should be wrapped") CompletableFuture.supplyAsync { while (depth.get() > 0); - assertEquals("Part inside suspension point should not be wrapped", 0, depth.get()) + assertEquals(0, depth.get(), "Part inside suspension point should not be wrapped") "ignored" }.await() result } - assertThat(future.get(), IsEqual("OK")) + assertEquals("OK", future.get()) } @Test diff --git a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-play-services.txt b/integration/kotlinx-coroutines-play-services/api/kotlinx-coroutines-play-services.api similarity index 100% rename from binary-compatibility-validator/reference-public-api/kotlinx-coroutines-play-services.txt rename to integration/kotlinx-coroutines-play-services/api/kotlinx-coroutines-play-services.api diff --git a/integration/kotlinx-coroutines-play-services/build.gradle b/integration/kotlinx-coroutines-play-services/build.gradle index 61201faeb7..eb554866ed 100644 --- a/integration/kotlinx-coroutines-play-services/build.gradle +++ b/integration/kotlinx-coroutines-play-services/build.gradle @@ -1,32 +1,43 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +import org.gradle.api.artifacts.transform.* + import java.nio.file.Files -import java.nio.file.NoSuchFileException import java.util.zip.ZipEntry import java.util.zip.ZipFile -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - ext.tasks_version = '16.0.1' -def attr = Attribute.of("artifactType", String.class) -configurations { - aar { - attributes { attribute(attr, ArtifactTypeDefinition.JAR_TYPE) } - sourceSets.main.compileClasspath += it - sourceSets.test.compileClasspath += it - sourceSets.test.runtimeClasspath += it +def artifactType = Attribute.of("artifactType", String) +def unpackedAar = Attribute.of("unpackedAar", Boolean) + +configurations.all { + afterEvaluate { + if (canBeResolved) { + attributes.attribute(unpackedAar, true) // request all AARs to be unpacked + } } } dependencies { - registerTransform { - from.attribute(attr, "aar") - to.attribute(attr, "jar") - artifactTransform(ExtractJars.class) + attributesSchema { + attribute(unpackedAar) + } + + artifactTypes { + aar { + attributes.attribute(unpackedAar, false) + } } - aar("com.google.android.gms:play-services-tasks:$tasks_version") { + registerTransform(UnpackAar) { + from.attribute(unpackedAar, false).attribute(artifactType, "aar") + to.attribute(unpackedAar, true).attribute(artifactType, "jar") + } + + api("com.google.android.gms:play-services-tasks:$tasks_version") { exclude group: 'com.android.support' } } @@ -37,48 +48,32 @@ tasks.withType(dokka.getClass()) { // This is workaround for missing package list in Google API packageListUrl = projectDir.toPath().resolve("package.list").toUri().toURL() } - - afterEvaluate { - classpath += project.configurations.aar.files - } } -class ExtractJars extends ArtifactTransform { - @Override - List transform(File input) { - unzip(input) - - List jars = new ArrayList<>() - outputDirectory.traverse(nameFilter: ~/.*\.jar/) { jars += it } +abstract class UnpackAar implements TransformAction { + @InputArtifact + abstract Provider getInputArtifact() - return jars - } - - private void unzip(File zipFile) { - ZipFile zip + @Override + void transform(TransformOutputs outputs) { + ZipFile zip = new ZipFile(inputArtifact.get().asFile) try { - zip = new ZipFile(zipFile) for (entry in zip.entries()) { - unzipEntryTo(zip, entry) + if (!entry.isDirectory() && entry.name.endsWith(".jar")) { + unzipEntryTo(zip, entry, outputs.file(entry.name)) + } } } finally { - if (zip != null) zip.close() + zip.close() } } - private void unzipEntryTo(ZipFile zip, ZipEntry entry) { - File output = new File(outputDirectory, entry.name) - if (entry.isDirectory()) { - output.mkdirs() - } else { - InputStream stream - try { - stream = zip.getInputStream(entry) - Files.copy(stream, output.toPath()) - } catch (NoSuchFileException ignored) { - } finally { - if (stream != null) stream.close() - } + private static void unzipEntryTo(ZipFile zip, ZipEntry entry, File output) { + InputStream stream = zip.getInputStream(entry) + try { + Files.copy(stream, output.toPath()) + } finally { + stream.close() } } } diff --git a/integration/kotlinx-coroutines-play-services/src/Tasks.kt b/integration/kotlinx-coroutines-play-services/src/Tasks.kt index 4952daa7c4..f9b9a60419 100644 --- a/integration/kotlinx-coroutines-play-services/src/Tasks.kt +++ b/integration/kotlinx-coroutines-play-services/src/Tasks.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("RedundantVisibilityModifier") diff --git a/integration/kotlinx-coroutines-play-services/test/TaskTest.kt b/integration/kotlinx-coroutines-play-services/test/TaskTest.kt index b87a295449..0f125ac98c 100644 --- a/integration/kotlinx-coroutines-play-services/test/TaskTest.kt +++ b/integration/kotlinx-coroutines-play-services/test/TaskTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.tasks @@ -61,7 +61,7 @@ class TaskTest : TestBase() { @Test fun testThrowingAsTask() { - val deferred = GlobalScope.async { + val deferred = GlobalScope.async { throw TestException("Fail") } diff --git a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-slf4j.txt b/integration/kotlinx-coroutines-slf4j/api/kotlinx-coroutines-slf4j.api similarity index 100% rename from binary-compatibility-validator/reference-public-api/kotlinx-coroutines-slf4j.txt rename to integration/kotlinx-coroutines-slf4j/api/kotlinx-coroutines-slf4j.api diff --git a/integration/kotlinx-coroutines-slf4j/build.gradle b/integration/kotlinx-coroutines-slf4j/build.gradle index 161a0b845a..05accb75d3 100644 --- a/integration/kotlinx-coroutines-slf4j/build.gradle +++ b/integration/kotlinx-coroutines-slf4j/build.gradle @@ -1,3 +1,7 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + dependencies { compile 'org.slf4j:slf4j-api:1.7.25' testCompile 'io.github.microutils:kotlin-logging:1.5.4' @@ -10,4 +14,4 @@ tasks.withType(dokka.getClass()) { packageListUrl = projectDir.toPath().resolve("package.list").toUri().toURL() url = new URL("https://www.slf4j.org/apidocs/") } -} \ No newline at end of file +} diff --git a/integration/kotlinx-coroutines-slf4j/src/MDCContext.kt b/integration/kotlinx-coroutines-slf4j/src/MDCContext.kt index c9b1dd9a88..6dbcef6ebe 100644 --- a/integration/kotlinx-coroutines-slf4j/src/MDCContext.kt +++ b/integration/kotlinx-coroutines-slf4j/src/MDCContext.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.slf4j diff --git a/integration/kotlinx-coroutines-slf4j/test/MDCContextTest.kt b/integration/kotlinx-coroutines-slf4j/test/MDCContextTest.kt index f3ed957bfb..7d18359c5d 100644 --- a/integration/kotlinx-coroutines-slf4j/test/MDCContextTest.kt +++ b/integration/kotlinx-coroutines-slf4j/test/MDCContextTest.kt @@ -28,7 +28,7 @@ class MDCContextTest : TestBase() { MDC.put("myKey", "myValue") // Standalone launch GlobalScope.launch { - assertEquals(null, MDC.get("myKey")) + assertNull(MDC.get("myKey")) expect(2) }.join() finish(3) @@ -92,7 +92,7 @@ class MDCContextTest : TestBase() { @Test fun testContextMayBeEmpty() { runBlocking(MDCContext()) { - assertEquals(null, MDC.get("myKey")) + assertNull(MDC.get("myKey")) } } diff --git a/js/example-frontend-js/build.gradle b/js/example-frontend-js/build.gradle index 52b8bb97de..ff62455494 100644 --- a/js/example-frontend-js/build.gradle +++ b/js/example-frontend-js/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ apply plugin: 'kotlin-dce-js' diff --git a/js/example-frontend-js/npm/webpack.config.js b/js/example-frontend-js/npm/webpack.config.js index 2124d875eb..a208d047b3 100644 --- a/js/example-frontend-js/npm/webpack.config.js +++ b/js/example-frontend-js/npm/webpack.config.js @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ // This script is copied to "build" directory and run from there @@ -50,4 +50,4 @@ module.exports = { sourceMap: true }) ] -}; \ No newline at end of file +}; diff --git a/js/example-frontend-js/src/ExampleMain.kt b/js/example-frontend-js/src/ExampleMain.kt index e3952a238e..f3e8c081b1 100644 --- a/js/example-frontend-js/src/ExampleMain.kt +++ b/js/example-frontend-js/src/ExampleMain.kt @@ -1,21 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -/* - * Copyright 2016-2017 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ import kotlinx.coroutines.* diff --git a/js/example-frontend-js/src/main/web/main.js b/js/example-frontend-js/src/main/web/main.js index c0cb691db5..d2440ffaef 100644 --- a/js/example-frontend-js/src/main/web/main.js +++ b/js/example-frontend-js/src/main/web/main.js @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ // ------ Main bundle for example application ------ diff --git a/js/example-frontend-js/src/main/web/style.css b/js/example-frontend-js/src/main/web/style.css index 29dc9ff82f..31d0ebc058 100644 --- a/js/example-frontend-js/src/main/web/style.css +++ b/js/example-frontend-js/src/main/web/style.css @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ #scene { @@ -16,4 +16,4 @@ position: absolute; background: #ffa450; border-radius: 50%; -} \ No newline at end of file +} diff --git a/js/js-stub/build.gradle b/js/js-stub/build.gradle index 20067b7c6b..b2ca0398d9 100644 --- a/js/js-stub/build.gradle +++ b/js/js-stub/build.gradle @@ -1,9 +1,9 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ compileKotlin { kotlinOptions { freeCompilerArgs += "-Xallow-kotlin-package" } -} \ No newline at end of file +} diff --git a/js/js-stub/src/Performance.kt b/js/js-stub/src/Performance.kt index 4c63ed0039..353a08fff8 100644 --- a/js/js-stub/src/Performance.kt +++ b/js/js-stub/src/Performance.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 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 { abstract fun now(): Double -} \ No newline at end of file +} diff --git a/js/js-stub/src/Promise.kt b/js/js-stub/src/Promise.kt index a7d501a6e5..7413a872be 100644 --- a/js/js-stub/src/Promise.kt +++ b/js/js-stub/src/Promise.kt @@ -1,7 +1,7 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlin.js -public open class Promise \ No newline at end of file +public open class Promise diff --git a/js/js-stub/src/Window.kt b/js/js-stub/src/Window.kt index 82c6fcfdb1..f54ed0d7bb 100644 --- a/js/js-stub/src/Window.kt +++ b/js/js-stub/src/Window.kt @@ -1,7 +1,7 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 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 \ No newline at end of file +public abstract class Window diff --git a/knit/README.md b/knit/README.md deleted file mode 100644 index ca0560fa6c..0000000000 --- a/knit/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Knit - -This is a very simple tool that produces Kotlin source example files from a markdown document that includes -snippets of Kotlin code in its body. It is used to produce examples for -[coroutines guide](../docs/coroutines-guide.md) and other markdown documents. -It also includes links to the documentation web site into the documents. - -## Usage - -* In project root directory do: - * Run `./gradlew knit` -* Commit updated documents and examples - diff --git a/knit/build.gradle b/knit/build.gradle deleted file mode 100644 index 0410d5d268..0000000000 --- a/knit/build.gradle +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -apply plugin: "application" - -sourceSets { - main.kotlin.srcDirs = ['src'] - main.java.srcDirs = ['src'] - main.resources.srcDirs = ['resources'] -} - -FileTree mdFiles = fileTree(project.rootDir) { - include '**/*.md' - exclude '**/build/**' - exclude '**/.gradle/**' - exclude '**/node_modules/**' -} - -mainClassName = "KnitKt" - -run.dependsOn rootProject.getTasksByName("dokka", true) -run.args = mdFiles -run.workingDir = project.rootDir - -task knit(dependsOn: run) diff --git a/knit/resources/knit.properties b/knit/resources/knit.properties deleted file mode 100644 index 146c182339..0000000000 --- a/knit/resources/knit.properties +++ /dev/null @@ -1,9 +0,0 @@ -# -# Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. -# - -site.root=https://kotlin.github.io/kotlinx.coroutines - -module.roots=. integration reactive ui -module.marker=build.gradle -module.docs=build/dokka \ No newline at end of file diff --git a/knit/src/Knit.kt b/knit/src/Knit.kt deleted file mode 100644 index 30dd678fd1..0000000000 --- a/knit/src/Knit.kt +++ /dev/null @@ -1,598 +0,0 @@ -/* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -import java.io.* -import java.util.* -import kotlin.properties.* - -// --- props in knit.properties - -val knitProperties = ClassLoader.getSystemClassLoader() - .getResource("knit.properties").openStream().use { Properties().apply { load(it) } } - -val siteRoot = knitProperties.getProperty("site.root")!! -val moduleRoots = knitProperties.getProperty("module.roots").split(" ") -val moduleMarker = knitProperties.getProperty("module.marker")!! -val moduleDocs = knitProperties.getProperty("module.docs")!! - -// --- markdown syntax - -const val DIRECTIVE_START = "" - -const val TOC_DIRECTIVE = "TOC" -const val TOC_REF_DIRECTIVE = "TOC_REF" -const val KNIT_DIRECTIVE = "KNIT" -const val INCLUDE_DIRECTIVE = "INCLUDE" -const val CLEAR_DIRECTIVE = "CLEAR" -const val TEST_DIRECTIVE = "TEST" - -const val KNIT_AUTONUMBER_PLACEHOLDER = '#' -const val KNIT_AUTONUMBER_REGEX = "([0-9a-z]+)" - -const val TEST_OUT_DIRECTIVE = "TEST_OUT" - -const val MODULE_DIRECTIVE = "MODULE" -const val INDEX_DIRECTIVE = "INDEX" - -const val CODE_START = "```kotlin" -const val CODE_END = "```" - -const val SAMPLE_START = "//sampleStart" -const val SAMPLE_END = "//sampleEnd" - -const val TEST_START = "```text" -const val TEST_END = "```" - -const val SECTION_START = "##" - -const val PACKAGE_PREFIX = "package " -const val STARTS_WITH_PREDICATE = "STARTS_WITH" -const val ARBITRARY_TIME_PREDICATE = "ARBITRARY_TIME" -const val FLEXIBLE_TIME_PREDICATE = "FLEXIBLE_TIME" -const val FLEXIBLE_THREAD_PREDICATE = "FLEXIBLE_THREAD" -const val LINES_START_UNORDERED_PREDICATE = "LINES_START_UNORDERED" -const val EXCEPTION_MODE = "EXCEPTION" -const val LINES_START_PREDICATE = "LINES_START" - -val API_REF_REGEX = Regex("(^|[ \\](])\\[([A-Za-z0-9_().]+)]($|[^\\[(])") -val LINK_DEF_REGEX = Regex("^\\[([A-Za-z0-9_().]+)]: .*") - -val tocRefMap = HashMap>() -val fileSet = HashSet() -val fileQueue = ArrayDeque() - -fun main(args: Array) { - if (args.isEmpty()) { - println("Usage: Knit ") - return - } - args.map { File(it) }.toCollection(fileQueue) - fileQueue.toCollection(fileSet) - while (!fileQueue.isEmpty()) { - if (!knit(fileQueue.removeFirst())) System.exit(1) // abort on first error with error exit code - } -} - -fun knit(markdownFile: File): Boolean { - println("*** Reading $markdownFile") - val tocLines = arrayListOf() - var knitRegex: Regex? = null - var knitAutonumberGroup = 0 - var knitAutonumberDigits = 0 - var knitAutonumberIndex = 1 - val includes = arrayListOf() - val codeLines = arrayListOf() - val testLines = arrayListOf() - var testOut: String? = null - val testOutLines = arrayListOf() - var lastPgk: String? = null - val files = mutableSetOf() - val allApiRefs = arrayListOf() - val remainingApiRefNames = mutableSetOf() - var moduleName: String by Delegates.notNull() - var docsRoot: String by Delegates.notNull() - var retryKnitLater = false - val tocRefs = ArrayList().also { tocRefMap[markdownFile] = it } - // read markdown file - val markdown = markdownFile.withMarkdownTextReader { - mainLoop@ while (true) { - val inLine = readLine() ?: break - val directive = directive(inLine) - if (directive != null && markdownPart == MarkdownPart.TOC) { - markdownPart = MarkdownPart.POST_TOC - postTocText += inLine - } - when (directive?.name) { - TOC_DIRECTIVE -> { - requireSingleLine(directive) - require(directive.param.isEmpty()) { "$TOC_DIRECTIVE directive must not have parameters" } - require(markdownPart == MarkdownPart.PRE_TOC) { "Only one TOC directive is supported" } - markdownPart = MarkdownPart.TOC - } - TOC_REF_DIRECTIVE -> { - requireSingleLine(directive) - require(!directive.param.isEmpty()) { "$TOC_REF_DIRECTIVE directive must include reference file path" } - val refPath = directive.param - val refFile = File(markdownFile.parent, refPath.replace('/', File.separatorChar)) - require(fileSet.contains(refFile)) { "Referenced file $refFile is missing from the processed file set" } - val toc = tocRefMap[refFile] - if (toc == null) { - retryKnitLater = true // put this file at the end of the queue and retry later - } else { - val lines = toc.map { (levelPrefix, name, ref) -> - "$levelPrefix [$name]($refPath#$ref)" - } - if (!replaceUntilNextDirective(lines)) error("Unexpected end of file after $TOC_REF_DIRECTIVE") - } - } - KNIT_DIRECTIVE -> { - requireSingleLine(directive) - require(!directive.param.isEmpty()) { "$KNIT_DIRECTIVE directive must include regex parameter" } - require(knitRegex == null) { "Only one KNIT directive is supported"} - var str = directive.param - val i = str.indexOf(KNIT_AUTONUMBER_PLACEHOLDER) - if (i >= 0) { - val j = str.lastIndexOf(KNIT_AUTONUMBER_PLACEHOLDER) - knitAutonumberDigits = j - i + 1 - require(str.substring(i, j + 1) == KNIT_AUTONUMBER_PLACEHOLDER.toString().repeat(knitAutonumberDigits)) { - "$KNIT_DIRECTIVE can only use a contiguous range of '$KNIT_AUTONUMBER_PLACEHOLDER' for auto-numbering" - } - knitAutonumberGroup = str.substring(0, i).count { it == '(' } + 2 // note: it does not understand escaped open braces - str = str.substring(0, i) + KNIT_AUTONUMBER_REGEX + str.substring(j + 1) - } - knitRegex = Regex("\\((" + str + ")\\)") - continue@mainLoop - } - INCLUDE_DIRECTIVE -> { - if (directive.param.isEmpty()) { - require(!directive.singleLine) { "$INCLUDE_DIRECTIVE directive without parameters must not be single line" } - readUntilTo(DIRECTIVE_END, codeLines) - } else { - val include = Include(Regex(directive.param)) - if (directive.singleLine) { - include.lines += codeLines - codeLines.clear() - } else { - readUntilTo(DIRECTIVE_END, include.lines) - } - includes += include - } - continue@mainLoop - } - CLEAR_DIRECTIVE -> { - requireSingleLine(directive) - require(directive.param.isEmpty()) { "$CLEAR_DIRECTIVE directive must not have parameters" } - codeLines.clear() - continue@mainLoop - } - TEST_OUT_DIRECTIVE -> { - require(!directive.param.isEmpty()) { "$TEST_OUT_DIRECTIVE directive must include file name parameter" } - flushTestOut(markdownFile.parentFile, testOut, testOutLines) - testOut = directive.param - readUntil(DIRECTIVE_END).forEach { testOutLines += it } - } - TEST_DIRECTIVE -> { - require(lastPgk != null) { "'$PACKAGE_PREFIX' prefix was not found in emitted code"} - require(testOut != null) { "$TEST_OUT_DIRECTIVE directive was not specified" } - val predicate = directive.param - if (testLines.isEmpty()) { - if (directive.singleLine) { - require(!predicate.isEmpty()) { "$TEST_OUT_DIRECTIVE must be preceded by $TEST_START block or contain test predicate"} - } else - testLines += readUntil(DIRECTIVE_END) - } else { - requireSingleLine(directive) - } - makeTest(testOutLines, lastPgk!!, testLines, predicate) - testLines.clear() - } - MODULE_DIRECTIVE -> { - requireSingleLine(directive) - moduleName = directive.param - docsRoot = findModuleRootDir(moduleName) + "/" + moduleDocs + "/" + moduleName - } - INDEX_DIRECTIVE -> { - requireSingleLine(directive) - val indexLines = processApiIndex("$siteRoot/$moduleName", docsRoot, directive.param, remainingApiRefNames) - ?: throw IllegalArgumentException("Failed to load index for ${directive.param}") - if (!replaceUntilNextDirective(indexLines)) error("Unexpected end of file after $INDEX_DIRECTIVE") - } - } - if (inLine.startsWith(CODE_START)) { - require(testOut == null || testLines.isEmpty()) { "Previous test was not emitted with $TEST_DIRECTIVE" } - codeLines += "" - readUntilTo(CODE_END, codeLines) { line -> - !line.startsWith(SAMPLE_START) && !line.startsWith(SAMPLE_END) - } - continue@mainLoop - } - if (inLine.startsWith(TEST_START)) { - require(testOut == null || testLines.isEmpty()) { "Previous test was not emitted with $TEST_DIRECTIVE" } - readUntilTo(TEST_END, testLines) - continue@mainLoop - } - if (inLine.startsWith(SECTION_START) && markdownPart == MarkdownPart.POST_TOC) { - val i = inLine.indexOf(' ') - require(i >= 2) { "Invalid section start" } - val name = inLine.substring(i + 1).trim() - val levelPrefix = " ".repeat(i - 2) + "*" - val sectionRef = makeSectionRef(name) - tocLines += "$levelPrefix [$name](#$sectionRef)" - tocRefs += TocRef(levelPrefix, name, sectionRef) - continue@mainLoop - } - val linkDefMatch = LINK_DEF_REGEX.matchEntire(inLine) - if (linkDefMatch != null) { - val name = linkDefMatch.groups[1]!!.value - remainingApiRefNames -= name - } else { - for (match in API_REF_REGEX.findAll(inLine)) { - val apiRef = ApiRef(lineNumber, match.groups[2]!!.value) - allApiRefs += apiRef - remainingApiRefNames += apiRef.name - } - } - knitRegex?.find(inLine)?.let knitRegexMatch@{ knitMatch -> - val fileName = knitMatch.groups[1]!!.value - if (knitAutonumberDigits != 0) { - val numGroup = knitMatch.groups[knitAutonumberGroup]!! - val num = knitAutonumberIndex.toString().padStart(knitAutonumberDigits, '0') - if (numGroup.value != num) { // update and retry with this line if a different number - val r = numGroup.range - val newLine = inLine.substring(0, r.first) + num + inLine.substring(r.last + 1) - updateLineAndRetry(newLine) - return@knitRegexMatch - } - } - knitAutonumberIndex++ - val file = File(markdownFile.parentFile, fileName) - require(files.add(file)) { "Duplicate file: $file"} - println("Knitting $file ...") - val outLines = arrayListOf() - for (include in includes) { - val includeMatch = include.regex.matchEntire(fileName) ?: continue - include.lines.forEach { includeLine -> - val line = makeReplacements(includeLine, includeMatch) - if (line.startsWith(PACKAGE_PREFIX)) - lastPgk = line.substring(PACKAGE_PREFIX.length).trim() - outLines += line - } - } - for (code in codeLines) { - outLines += code.replace("System.currentTimeMillis()", "currentTimeMillis()") - } - codeLines.clear() - writeLinesIfNeeded(file, outLines) - } - } - } ?: return false // false when failed - // bailout if retry was requested - if (retryKnitLater) { - fileQueue.add(markdownFile) - return true - } - // update markdown file with toc - val newLines = buildList { - addAll(markdown.preTocText) - if (!tocLines.isEmpty()) { - add("") - addAll(tocLines) - add("") - } - addAll(markdown.postTocText) - } - if (newLines != markdown.inText) writeLines(markdownFile, newLines) - // check apiRefs - for (apiRef in allApiRefs) { - if (apiRef.name in remainingApiRefNames) { - println("WARNING: $markdownFile: ${apiRef.line}: Broken reference to [${apiRef.name}]") - } - } - // write test output - flushTestOut(markdownFile.parentFile, testOut, testOutLines) - return true -} - -data class TocRef(val levelPrefix: String, val name: String, val ref: String) - -fun makeTest(testOutLines: MutableList, pgk: String, test: List, predicate: String) { - val funName = buildString { - var cap = true - for (c in pgk) { - if (c == '.') { - cap = true - } else { - append(if (cap) c.toUpperCase() else c) - cap = false - } - } - } - testOutLines += "" - testOutLines += " @Test" - testOutLines += " fun test$funName() {" - val prefix = " test(\"$funName\") { $pgk.main() }" - when (predicate) { - "" -> makeTestLines(testOutLines, prefix, "verifyLines", test) - STARTS_WITH_PREDICATE -> makeTestLines(testOutLines, prefix, "verifyLinesStartWith", test) - ARBITRARY_TIME_PREDICATE -> makeTestLines(testOutLines, prefix, "verifyLinesArbitraryTime", test) - FLEXIBLE_TIME_PREDICATE -> makeTestLines(testOutLines, prefix, "verifyLinesFlexibleTime", test) - FLEXIBLE_THREAD_PREDICATE -> makeTestLines(testOutLines, prefix, "verifyLinesFlexibleThread", test) - LINES_START_UNORDERED_PREDICATE -> makeTestLines(testOutLines, prefix, "verifyLinesStartUnordered", test) - EXCEPTION_MODE -> makeTestLines(testOutLines, prefix, "verifyExceptions", test) - LINES_START_PREDICATE -> makeTestLines(testOutLines, prefix, "verifyLinesStart", test) - else -> { - testOutLines += "$prefix.also { lines ->" - testOutLines += " check($predicate)" - testOutLines += " }" - } - } - testOutLines += " }" -} - -private fun makeTestLines(testOutLines: MutableList, prefix: String, method: String, test: List) { - testOutLines += "$prefix.$method(" - for ((index, testLine) in test.withIndex()) { - val commaOpt = if (index < test.size - 1) "," else "" - val escapedLine = testLine.replace("\"", "\\\"") - testOutLines += " \"$escapedLine\"$commaOpt" - } - testOutLines += " )" -} - -private fun makeReplacements(line: String, match: MatchResult): String { - var result = line - for ((id, group) in match.groups.withIndex()) { - if (group != null) - result = result.replace("\$\$$id", group.value) - } - return result -} - -private fun flushTestOut(parentDir: File?, testOut: String?, testOutLines: MutableList) { - if (testOut == null) return - val file = File(parentDir, testOut) - testOutLines += "}" - writeLinesIfNeeded(file, testOutLines) - testOutLines.clear() -} - -private fun MarkdownTextReader.readUntil(marker: String): List = - arrayListOf().also { readUntilTo(marker, it) } - -private fun MarkdownTextReader.readUntilTo(marker: String, list: MutableList, linePredicate: (String) -> Boolean = { true }) { - while (true) { - val line = readLine() ?: break - if (line.startsWith(marker)) break - if (linePredicate(line)) list += line - } -} - -private inline fun buildList(block: ArrayList.() -> Unit): List { - val result = arrayListOf() - result.block() - return result -} - -private fun requireSingleLine(directive: Directive) { - require(directive.singleLine) { "${directive.name} directive must end on the same line with '$DIRECTIVE_END'" } -} - -fun makeSectionRef(name: String): String = name - .replace(' ', '-') - .replace(".", "") - .replace(",", "") - .replace("(", "") - .replace(")", "") - .replace("`", "") - .toLowerCase() - -class Include(val regex: Regex, val lines: MutableList = arrayListOf()) - -class Directive( - val name: String, - val param: String, - val singleLine: Boolean -) - -fun directive(line: String): Directive? { - if (!line.startsWith(DIRECTIVE_START)) return null - var s = line.substring(DIRECTIVE_START.length).trim() - val singleLine = s.endsWith(DIRECTIVE_END) - if (singleLine) s = s.substring(0, s.length - DIRECTIVE_END.length) - val i = s.indexOf(' ') - val name = if (i < 0) s else s.substring(0, i) - val param = if (i < 0) "" else s.substring(i).trim() - return Directive(name, param, singleLine) -} - -class ApiRef(val line: Int, val name: String) - -enum class MarkdownPart { PRE_TOC, TOC, POST_TOC } - -class MarkdownTextReader(r: Reader) : LineNumberReader(r) { - val inText = arrayListOf() - val preTocText = arrayListOf() - val postTocText = arrayListOf() - var markdownPart: MarkdownPart = MarkdownPart.PRE_TOC - var skip = false - var putBackLine: String? = null - - val outText: MutableList get() = when (markdownPart) { - MarkdownPart.PRE_TOC -> preTocText - MarkdownPart.POST_TOC -> postTocText - else -> throw IllegalStateException("Wrong state: $markdownPart") - } - - override fun readLine(): String? { - putBackLine?.let { - putBackLine = null - return it - } - val line = super.readLine() ?: return null - inText += line - if (!skip && markdownPart != MarkdownPart.TOC) - outText += line - return line - } - - fun updateLineAndRetry(line: String) { - outText.removeAt(outText.lastIndex) - outText += line - putBackLine = line - } - - fun replaceUntilNextDirective(lines: List): Boolean { - skip = true - while (true) { - val skipLine = readLine() ?: return false - if (directive(skipLine) != null) { - putBackLine = skipLine - break - } - } - skip = false - outText += lines - outText += putBackLine!! - return true - } -} - -fun File.withLineNumberReader(factory: (Reader) -> T, block: T.() -> Unit): T? { - val reader = factory(reader()) - reader.use { - try { - it.block() - } catch (e: Exception) { - println("ERROR: ${this@withLineNumberReader}: ${it.lineNumber}: ${e.message}") - return null - } - } - return reader -} - -fun File.withMarkdownTextReader(block: MarkdownTextReader.() -> Unit): MarkdownTextReader? = - withLineNumberReader(::MarkdownTextReader, block) - -fun writeLinesIfNeeded(file: File, outLines: List) { - val oldLines = try { - file.readLines() - } catch (e: IOException) { - emptyList() - } - if (outLines != oldLines) writeLines(file, outLines) -} - -fun writeLines(file: File, lines: List) { - println(" Writing $file ...") - file.parentFile?.mkdirs() - file.printWriter().use { out -> - lines.forEach { out.println(it) } - } -} - -fun findModuleRootDir(name: String): String = - moduleRoots - .map { "$it/$name" } - .firstOrNull { File("$it/$moduleMarker").exists() } - ?: throw IllegalArgumentException("Module $name is not found in any of the module root dirs") - -data class ApiIndexKey( - val docsRoot: String, - val pkg: String -) - -val apiIndexCache: MutableMap>> = HashMap() - -val REF_LINE_REGEX = Regex("([a-zA-z0-9.]+)") -val INDEX_HTML = "/index.html" -val INDEX_MD = "/index.md" -val FUNCTIONS_SECTION_HEADER = "### Functions" - -fun HashMap>.putUnambiguous(key: String, value: String) { - val oldValue = this[key] - if (oldValue != null) { - oldValue.add(value) - put(key, oldValue) - } else { - put(key, mutableListOf(value)) - } -} - -fun loadApiIndex( - docsRoot: String, - path: String, - pkg: String, - namePrefix: String = "" -): Map>? { - val fileName = "$docsRoot/$path$INDEX_MD" - val visited = mutableSetOf() - val map = HashMap>() - var inFunctionsSection = false - File(fileName).withLineNumberReader(::LineNumberReader) { - while (true) { - val line = readLine() ?: break - if (line == FUNCTIONS_SECTION_HEADER) inFunctionsSection = true - val result = REF_LINE_REGEX.matchEntire(line) ?: continue - val link = result.groups[1]!!.value - if (link.startsWith("..")) continue // ignore cross-references - val absLink = "$path/$link" - var name = result.groups[2]!!.value - // a special disambiguation fix for pseudo-constructor functions - if (inFunctionsSection && name[0] in 'A'..'Z') name += "()" - val refName = namePrefix + name - val fqName = "$pkg.$refName" - // Put shorter names for extensions on 3rd party classes (prefix is FQname of those classes) - if (namePrefix != "" && namePrefix[0] in 'a'..'z') { - val i = namePrefix.dropLast(1).lastIndexOf('.') - if (i >= 0) map.putUnambiguous(namePrefix.substring(i + 1) + name, absLink) - map.putUnambiguous(name, absLink) - } - // Disambiguate lower-case names with leading underscore (e.g. Flow class vs flow builder ambiguity) - if (namePrefix == "" && name[0] in 'a'..'z') { - map.putUnambiguous("_$name", absLink) - } - // Always put fully qualified names - map.putUnambiguous(refName, absLink) - map.putUnambiguous(fqName, absLink) - if (link.endsWith(INDEX_HTML)) { - if (visited.add(link)) { - val path2 = path + "/" + link.substring(0, link.length - INDEX_HTML.length) - map += loadApiIndex(docsRoot, path2, pkg, "$refName.") - ?: throw IllegalArgumentException("Failed to parse $docsRoot/$path2") - } - } - } - } ?: return null // return null on failure - return map -} - -fun processApiIndex( - siteRoot: String, - docsRoot: String, - pkg: String, - remainingApiRefNames: MutableSet -): List? { - val key = ApiIndexKey(docsRoot, pkg) - val map = apiIndexCache.getOrPut(key, { - print("Parsing API docs at $docsRoot/$pkg: ") - val result = loadApiIndex(docsRoot, pkg, pkg) ?: return null // null on failure - println("${result.size} definitions") - result - }) - val indexList = arrayListOf() - val it = remainingApiRefNames.iterator() - while (it.hasNext()) { - val refName = it.next() - val refLink = map[refName] ?: continue - if (refLink.size > 1) { - println("INFO: Ambiguous reference to [$refName]: $refLink, taking the shortest one") - } - - val link = refLink.minBy { it.length } - indexList += "[$refName]: $siteRoot/$link" - it.remove() - } - return indexList -} diff --git a/kotlinx-coroutines-bom/build.gradle b/kotlinx-coroutines-bom/build.gradle index d78f079e8d..25221e3609 100644 --- a/kotlinx-coroutines-bom/build.gradle +++ b/kotlinx-coroutines-bom/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ plugins { id 'java-platform' diff --git a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-core.txt b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api similarity index 99% rename from binary-compatibility-validator/reference-public-api/kotlinx-coroutines-core.txt rename to kotlinx-coroutines-core/api/kotlinx-coroutines-core.api index d8d4528eb4..d86a3d5559 100644 --- a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-core.txt +++ b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api @@ -151,6 +151,7 @@ public final class kotlinx/coroutines/CoroutineContextKt { } public abstract class kotlinx/coroutines/CoroutineDispatcher : kotlin/coroutines/AbstractCoroutineContextElement, kotlin/coroutines/ContinuationInterceptor { + public static final field Key Lkotlinx/coroutines/CoroutineDispatcher$Key; public fun ()V public abstract fun dispatch (Lkotlin/coroutines/CoroutineContext;Ljava/lang/Runnable;)V public fun dispatchYield (Lkotlin/coroutines/CoroutineContext;Ljava/lang/Runnable;)V @@ -163,6 +164,9 @@ public abstract class kotlinx/coroutines/CoroutineDispatcher : kotlin/coroutines public fun toString ()Ljava/lang/String; } +public final class kotlinx/coroutines/CoroutineDispatcher$Key : kotlin/coroutines/AbstractCoroutineContextKey { +} + public abstract interface class kotlinx/coroutines/CoroutineExceptionHandler : kotlin/coroutines/CoroutineContext$Element { public static final field Key Lkotlinx/coroutines/CoroutineExceptionHandler$Key; public abstract fun handleException (Lkotlin/coroutines/CoroutineContext;Ljava/lang/Throwable;)V @@ -294,11 +298,15 @@ public final class kotlinx/coroutines/ExceptionsKt { } public abstract class kotlinx/coroutines/ExecutorCoroutineDispatcher : kotlinx/coroutines/CoroutineDispatcher, java/io/Closeable { + public static final field Key Lkotlinx/coroutines/ExecutorCoroutineDispatcher$Key; public fun ()V public abstract fun close ()V public abstract fun getExecutor ()Ljava/util/concurrent/Executor; } +public final class kotlinx/coroutines/ExecutorCoroutineDispatcher$Key : kotlin/coroutines/AbstractCoroutineContextKey { +} + public final class kotlinx/coroutines/ExecutorsKt { public static final fun asExecutor (Lkotlinx/coroutines/CoroutineDispatcher;)Ljava/util/concurrent/Executor; public static final fun from (Ljava/util/concurrent/Executor;)Lkotlinx/coroutines/CoroutineDispatcher; @@ -746,10 +754,10 @@ public final class kotlinx/coroutines/channels/ConflatedBroadcastChannel : kotli public final class kotlinx/coroutines/channels/ProduceKt { public static final fun awaitClose (Lkotlinx/coroutines/channels/ProducerScope;Lkotlin/jvm/functions/Function0;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun awaitClose$default (Lkotlinx/coroutines/channels/ProducerScope;Lkotlin/jvm/functions/Function0;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static final fun produce (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;ILkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel; public static final fun produce (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;ILkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel; - public static synthetic fun produce$default (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;ILkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; + public static final fun produce (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;ILkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel; public static synthetic fun produce$default (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;ILkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; + public static synthetic fun produce$default (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;ILkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; } public abstract interface class kotlinx/coroutines/channels/ProducerScope : kotlinx/coroutines/CoroutineScope, kotlinx/coroutines/channels/SendChannel { @@ -943,6 +951,7 @@ public final class kotlinx/coroutines/flow/FlowKt { public static final fun onStart (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/flow/Flow; public static final fun produceIn (Lkotlinx/coroutines/flow/Flow;Lkotlinx/coroutines/CoroutineScope;)Lkotlinx/coroutines/channels/ReceiveChannel; public static final fun publishOn (Lkotlinx/coroutines/flow/Flow;Lkotlin/coroutines/CoroutineContext;)Lkotlinx/coroutines/flow/Flow; + public static final fun receiveAsFlow (Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/flow/Flow; public static final fun reduce (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final synthetic fun retry (Lkotlinx/coroutines/flow/Flow;ILkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/flow/Flow; public static final fun retry (Lkotlinx/coroutines/flow/Flow;JLkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/flow/Flow; @@ -1000,7 +1009,7 @@ public final class kotlinx/coroutines/flow/internal/FlowExceptions_commonKt { public static final fun checkIndexOverflow (I)I } -public final class kotlinx/coroutines/flow/internal/SafeCollectorKt { +public final class kotlinx/coroutines/flow/internal/SafeCollector_commonKt { public static final fun unsafeFlow (Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/flow/Flow; } diff --git a/kotlinx-coroutines-core/build.gradle b/kotlinx-coroutines-core/build.gradle index 4d516962e9..547a12b4c6 100644 --- a/kotlinx-coroutines-core/build.gradle +++ b/kotlinx-coroutines-core/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ apply plugin: 'kotlin-multiplatform' @@ -46,9 +46,18 @@ configurations { configureKotlinJvmPlatform(kotlinCompilerPluginClasspath) } +// Update module name for metadata artifact to avoid conflicts +// see https://github.com/Kotlin/kotlinx.coroutines/issues/1797 +compileKotlinMetadata { + kotlinOptions { + freeCompilerArgs += ["-module-name", "kotlinx-coroutines-core-common"] + } +} + kotlin.sourceSets { jvmTest.dependencies { - api "com.devexperts.lincheck:lincheck:$lincheck_version" + api "org.jetbrains.kotlinx:lincheck:$lincheck_version" + api "org.jetbrains.kotlinx:kotlinx-knit-test:$knit_version" api "com.esotericsoftware:kryo:4.0.0" implementation project (":android-unit-tests") } @@ -99,7 +108,7 @@ task jdk16Test(type: Test, dependsOn: [compileTestKotlinJvm, checkJdk16]) { testClassesDirs = files { jvmTest.testClassesDirs } executable = "$System.env.JDK_16/bin/java" exclude '**/*LFStressTest.*' // lock-freedom tests use LockFreedomTestEnvironment which needs JDK8 - exclude '**/*LCStressTest.*' // lic-check tests use LinChecker which needs JDK8 + exclude '**/*LCStressTest.*' // lin-check tests use LinChecker which needs JDK8 exclude '**/exceptions/**' // exceptions tests check suppressed exception which needs JDK8 exclude '**/ExceptionsGuideTest.*' } diff --git a/kotlinx-coroutines-core/common/src/AbstractCoroutine.kt b/kotlinx-coroutines-core/common/src/AbstractCoroutine.kt index 18088777a5..22111f0ce2 100644 --- a/kotlinx-coroutines-core/common/src/AbstractCoroutine.kt +++ b/kotlinx-coroutines-core/common/src/AbstractCoroutine.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("DEPRECATION_ERROR") diff --git a/kotlinx-coroutines-core/common/src/Annotations.kt b/kotlinx-coroutines-core/common/src/Annotations.kt index 742a7d7c66..5475c6b10e 100644 --- a/kotlinx-coroutines-core/common/src/Annotations.kt +++ b/kotlinx-coroutines-core/common/src/Annotations.kt @@ -1,10 +1,10 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines -import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.* /** * Marks declarations that are still **experimental** in coroutines API, which means that the design of the @@ -14,7 +14,7 @@ import kotlinx.coroutines.flow.Flow */ @MustBeDocumented @Retention(value = AnnotationRetention.BINARY) -@Experimental(level = Experimental.Level.WARNING) +@RequiresOptIn(level = RequiresOptIn.Level.WARNING) public annotation class ExperimentalCoroutinesApi /** @@ -30,7 +30,12 @@ public annotation class ExperimentalCoroutinesApi */ @MustBeDocumented @Retention(value = AnnotationRetention.BINARY) -@Experimental(level = Experimental.Level.WARNING) +@RequiresOptIn( + level = RequiresOptIn.Level.WARNING, + message = "This declaration is in a preview state and can be changed in a backwards-incompatible manner with a best-effort migration. " + + "Its usage should be marked with '@kotlinx.coroutines.FlowPreview' or '@OptIn(kotlinx.coroutines.FlowPreview::class)' " + + "if you accept the drawback of relying on preview API" +) @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.TYPEALIAS, AnnotationTarget.PROPERTY) public annotation class FlowPreview @@ -42,7 +47,7 @@ public annotation class FlowPreview */ @MustBeDocumented @Retention(value = AnnotationRetention.BINARY) -@Experimental(level = Experimental.Level.WARNING) +@RequiresOptIn(level = RequiresOptIn.Level.WARNING) public annotation class ObsoleteCoroutinesApi /** @@ -51,6 +56,11 @@ public annotation class ObsoleteCoroutinesApi * warnings and without providing any migration aids. */ @Retention(value = AnnotationRetention.BINARY) -@Experimental(level = Experimental.Level.ERROR) @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.TYPEALIAS, AnnotationTarget.PROPERTY) +@RequiresOptIn( + level = RequiresOptIn.Level.ERROR, message = "This is an internal kotlinx.coroutines API that " + + "should not be used from outside of kotlinx.coroutines. No compatibility guarantees are provided." + + "It is recommended to report your use-case of internal API to kotlinx.coroutines issue tracker, " + + "so stable API could be provided instead" +) public annotation class InternalCoroutinesApi diff --git a/kotlinx-coroutines-core/common/src/Await.kt b/kotlinx-coroutines-core/common/src/Await.kt index 3da0ad5ee9..dd1e1771f2 100644 --- a/kotlinx-coroutines-core/common/src/Await.kt +++ b/kotlinx-coroutines-core/common/src/Await.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/common/src/Builders.common.kt b/kotlinx-coroutines-core/common/src/Builders.common.kt index ff5d406643..7dd1b174ee 100644 --- a/kotlinx-coroutines-core/common/src/Builders.common.kt +++ b/kotlinx-coroutines-core/common/src/Builders.common.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmMultifileClass diff --git a/kotlinx-coroutines-core/common/src/CancellableContinuation.kt b/kotlinx-coroutines-core/common/src/CancellableContinuation.kt index d1e99529a5..fd5cd083e2 100644 --- a/kotlinx-coroutines-core/common/src/CancellableContinuation.kt +++ b/kotlinx-coroutines-core/common/src/CancellableContinuation.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt b/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt index f5b5900cb6..0cc9b57dec 100644 --- a/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt +++ b/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines @@ -9,6 +9,7 @@ import kotlinx.coroutines.internal.* import kotlin.coroutines.* import kotlin.coroutines.intrinsics.* import kotlin.jvm.* +import kotlin.native.concurrent.* private const val UNDECIDED = 0 private const val SUSPENDED = 1 diff --git a/kotlinx-coroutines-core/common/src/CompletableDeferred.kt b/kotlinx-coroutines-core/common/src/CompletableDeferred.kt index 732d07225f..f6cf90d515 100644 --- a/kotlinx-coroutines-core/common/src/CompletableDeferred.kt +++ b/kotlinx-coroutines-core/common/src/CompletableDeferred.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("DEPRECATION_ERROR") diff --git a/kotlinx-coroutines-core/common/src/CompletableJob.kt b/kotlinx-coroutines-core/common/src/CompletableJob.kt index 9a632276fe..4b4d16bc53 100644 --- a/kotlinx-coroutines-core/common/src/CompletableJob.kt +++ b/kotlinx-coroutines-core/common/src/CompletableJob.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines @@ -36,4 +36,4 @@ public interface CompletableJob : Job { * a [CancellationException] with the [exception] as a cause for the sake of diagnostic. */ public fun completeExceptionally(exception: Throwable): Boolean -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/common/src/CompletedExceptionally.kt b/kotlinx-coroutines-core/common/src/CompletedExceptionally.kt index b75d43070e..b426785bd7 100644 --- a/kotlinx-coroutines-core/common/src/CompletedExceptionally.kt +++ b/kotlinx-coroutines-core/common/src/CompletedExceptionally.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/common/src/CompletionHandler.common.kt b/kotlinx-coroutines-core/common/src/CompletionHandler.common.kt index 00bac305d5..bf6900087d 100644 --- a/kotlinx-coroutines-core/common/src/CompletionHandler.common.kt +++ b/kotlinx-coroutines-core/common/src/CompletionHandler.common.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/common/src/CoroutineContext.common.kt b/kotlinx-coroutines-core/common/src/CoroutineContext.common.kt index a8b5686253..51374603c3 100644 --- a/kotlinx-coroutines-core/common/src/CoroutineContext.common.kt +++ b/kotlinx-coroutines-core/common/src/CoroutineContext.common.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines @@ -20,4 +20,4 @@ internal expect val DefaultDelay: Delay // countOrElement -- pre-cached value for ThreadContext.kt internal expect inline fun withCoroutineContext(context: CoroutineContext, countOrElement: Any?, block: () -> T): T internal expect fun Continuation<*>.toDebugString(): String -internal expect val CoroutineContext.coroutineName: String? \ No newline at end of file +internal expect val CoroutineContext.coroutineName: String? diff --git a/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt b/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt index 393f6cb526..a618642f72 100644 --- a/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt +++ b/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines @@ -30,6 +30,12 @@ import kotlin.coroutines.* public abstract class CoroutineDispatcher : AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor { + /** @suppress */ + @ExperimentalStdlibApi + public companion object Key : AbstractCoroutineContextKey( + ContinuationInterceptor, + { it as? CoroutineDispatcher }) + /** * Returns `true` if the execution of the coroutine should be performed with [dispatch] method. * The default behavior for most dispatchers is to return `true`. diff --git a/kotlinx-coroutines-core/common/src/CoroutineExceptionHandler.kt b/kotlinx-coroutines-core/common/src/CoroutineExceptionHandler.kt index ee440b5310..cd7fd0d7ca 100644 --- a/kotlinx-coroutines-core/common/src/CoroutineExceptionHandler.kt +++ b/kotlinx-coroutines-core/common/src/CoroutineExceptionHandler.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/common/src/CoroutineName.kt b/kotlinx-coroutines-core/common/src/CoroutineName.kt index 4a7e9ea4f8..7f09a5897c 100644 --- a/kotlinx-coroutines-core/common/src/CoroutineName.kt +++ b/kotlinx-coroutines-core/common/src/CoroutineName.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/common/src/CoroutineScope.kt b/kotlinx-coroutines-core/common/src/CoroutineScope.kt index ca387338dc..a6b79bdb5a 100644 --- a/kotlinx-coroutines-core/common/src/CoroutineScope.kt +++ b/kotlinx-coroutines-core/common/src/CoroutineScope.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/common/src/CoroutineStart.kt b/kotlinx-coroutines-core/common/src/CoroutineStart.kt index 9e283b0db9..1272ce7c3a 100644 --- a/kotlinx-coroutines-core/common/src/CoroutineStart.kt +++ b/kotlinx-coroutines-core/common/src/CoroutineStart.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/common/src/Debug.common.kt b/kotlinx-coroutines-core/common/src/Debug.common.kt index 3bd7aabe92..013b983a74 100644 --- a/kotlinx-coroutines-core/common/src/Debug.common.kt +++ b/kotlinx-coroutines-core/common/src/Debug.common.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/common/src/Deferred.kt b/kotlinx-coroutines-core/common/src/Deferred.kt index 04152f903e..f05abbdb43 100644 --- a/kotlinx-coroutines-core/common/src/Deferred.kt +++ b/kotlinx-coroutines-core/common/src/Deferred.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/common/src/Delay.kt b/kotlinx-coroutines-core/common/src/Delay.kt index acb924020a..3404f63611 100644 --- a/kotlinx-coroutines-core/common/src/Delay.kt +++ b/kotlinx-coroutines-core/common/src/Delay.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/common/src/Dispatchers.common.kt b/kotlinx-coroutines-core/common/src/Dispatchers.common.kt index 5a957e7555..dba57abc88 100644 --- a/kotlinx-coroutines-core/common/src/Dispatchers.common.kt +++ b/kotlinx-coroutines-core/common/src/Dispatchers.common.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/common/src/EventLoop.common.kt b/kotlinx-coroutines-core/common/src/EventLoop.common.kt index a4984b55e5..ba331e20df 100644 --- a/kotlinx-coroutines-core/common/src/EventLoop.common.kt +++ b/kotlinx-coroutines-core/common/src/EventLoop.common.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines @@ -8,6 +8,7 @@ import kotlinx.atomicfu.* import kotlinx.coroutines.internal.* import kotlin.coroutines.* import kotlin.jvm.* +import kotlin.native.concurrent.* /** * Extended by [CoroutineDispatcher] implementations that have event loop inside and can @@ -117,7 +118,7 @@ internal abstract class EventLoop : CoroutineDispatcher() { protected open fun shutdown() {} } -@NativeThreadLocal +@ThreadLocal internal object ThreadLocalEventLoop { private val ref = CommonThreadLocal() diff --git a/kotlinx-coroutines-core/common/src/Exceptions.common.kt b/kotlinx-coroutines-core/common/src/Exceptions.common.kt index 2c0b5ce2cd..64f8911e9d 100644 --- a/kotlinx-coroutines-core/common/src/Exceptions.common.kt +++ b/kotlinx-coroutines-core/common/src/Exceptions.common.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines @@ -29,4 +29,4 @@ internal class CoroutinesInternalError(message: String, cause: Throwable) : Erro internal expect fun Throwable.addSuppressedThrowable(other: Throwable) // For use in tests -internal expect val RECOVER_STACK_TRACES: Boolean \ No newline at end of file +internal expect val RECOVER_STACK_TRACES: Boolean diff --git a/kotlinx-coroutines-core/common/src/Job.kt b/kotlinx-coroutines-core/common/src/Job.kt index 133e24fa69..4d4e37ee25 100644 --- a/kotlinx-coroutines-core/common/src/Job.kt +++ b/kotlinx-coroutines-core/common/src/Job.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmMultifileClass diff --git a/kotlinx-coroutines-core/common/src/JobSupport.kt b/kotlinx-coroutines-core/common/src/JobSupport.kt index eb0d823f38..e52aaeaa8e 100644 --- a/kotlinx-coroutines-core/common/src/JobSupport.kt +++ b/kotlinx-coroutines-core/common/src/JobSupport.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("DEPRECATION_ERROR") @@ -13,6 +13,7 @@ import kotlin.coroutines.* import kotlin.coroutines.intrinsics.* import kotlin.js.* import kotlin.jvm.* +import kotlin.native.concurrent.* /** * A concrete implementation of [Job]. It is optionally a child to a parent job. diff --git a/kotlinx-coroutines-core/common/src/MainCoroutineDispatcher.kt b/kotlinx-coroutines-core/common/src/MainCoroutineDispatcher.kt index bead3c89a4..3f2ddcd69f 100644 --- a/kotlinx-coroutines-core/common/src/MainCoroutineDispatcher.kt +++ b/kotlinx-coroutines-core/common/src/MainCoroutineDispatcher.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/common/src/NonCancellable.kt b/kotlinx-coroutines-core/common/src/NonCancellable.kt index c48faea7f8..45803cf945 100644 --- a/kotlinx-coroutines-core/common/src/NonCancellable.kt +++ b/kotlinx-coroutines-core/common/src/NonCancellable.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("DEPRECATION_ERROR") diff --git a/kotlinx-coroutines-core/common/src/Runnable.common.kt b/kotlinx-coroutines-core/common/src/Runnable.common.kt index 6c258d854d..692c700b50 100644 --- a/kotlinx-coroutines-core/common/src/Runnable.common.kt +++ b/kotlinx-coroutines-core/common/src/Runnable.common.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines @@ -18,4 +18,4 @@ public expect interface Runnable { * Creates [Runnable] task instance. */ @Suppress("FunctionName") -public expect inline fun Runnable(crossinline block: () -> Unit): Runnable \ No newline at end of file +public expect inline fun Runnable(crossinline block: () -> Unit): Runnable diff --git a/kotlinx-coroutines-core/common/src/SchedulerTask.common.kt b/kotlinx-coroutines-core/common/src/SchedulerTask.common.kt index 7b767f5167..dbdf45ead5 100644 --- a/kotlinx-coroutines-core/common/src/SchedulerTask.common.kt +++ b/kotlinx-coroutines-core/common/src/SchedulerTask.common.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/common/src/Supervisor.kt b/kotlinx-coroutines-core/common/src/Supervisor.kt index 63542a9a17..1991119053 100644 --- a/kotlinx-coroutines-core/common/src/Supervisor.kt +++ b/kotlinx-coroutines-core/common/src/Supervisor.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("DEPRECATION_ERROR") diff --git a/kotlinx-coroutines-core/common/src/Timeout.kt b/kotlinx-coroutines-core/common/src/Timeout.kt index 7e6f0d0e2d..8aedba30a1 100644 --- a/kotlinx-coroutines-core/common/src/Timeout.kt +++ b/kotlinx-coroutines-core/common/src/Timeout.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/common/src/Unconfined.kt b/kotlinx-coroutines-core/common/src/Unconfined.kt index 794ee1e181..ce03a28765 100644 --- a/kotlinx-coroutines-core/common/src/Unconfined.kt +++ b/kotlinx-coroutines-core/common/src/Unconfined.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines @@ -37,4 +37,4 @@ internal class YieldContext : AbstractCoroutineContextElement(Key) { @JvmField var dispatcherWasUnconfined = false -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/common/src/Yield.kt b/kotlinx-coroutines-core/common/src/Yield.kt index 5d931340af..e0af04ddb7 100644 --- a/kotlinx-coroutines-core/common/src/Yield.kt +++ b/kotlinx-coroutines-core/common/src/Yield.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt b/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt index f70164485a..8d078e49ca 100644 --- a/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels @@ -11,6 +11,7 @@ import kotlinx.coroutines.intrinsics.* import kotlinx.coroutines.selects.* import kotlin.coroutines.* import kotlin.jvm.* +import kotlin.native.concurrent.* /** * Abstract send channel. It is a base class for all send channel implementations. @@ -29,6 +30,7 @@ internal abstract class AbstractSendChannel : SendChannel { /** * Returns `true` if this channel's buffer is full. + * This operation should be atomic if it is invoked by [enqueueSend]. * @suppress **This is unstable API and it is subject to change.** */ protected abstract val isBufferFull: Boolean @@ -139,8 +141,8 @@ internal abstract class AbstractSendChannel : SendChannel { // ------ SendChannel ------ public final override val isClosedForSend: Boolean get() = closedForSend != null - public final override val isFull: Boolean get() = full - private val full: Boolean get() = queue.nextNode !is ReceiveOrClosed<*> && isBufferFull // TODO rename to `isFull` + public override val isFull: Boolean get() = isFullImpl + protected val isFullImpl: Boolean get() = queue.nextNode !is ReceiveOrClosed<*> && isBufferFull public final override suspend fun send(element: E) { // fast path -- try offer non-blocking @@ -181,7 +183,7 @@ internal abstract class AbstractSendChannel : SendChannel { private suspend fun sendSuspend(element: E): Unit = suspendAtomicCancellableCoroutineReusable sc@ { cont -> loop@ while (true) { - if (full) { + if (isFullImpl) { val send = SendElement(element, cont) val enqueueResult = enqueueSend(send) when { @@ -226,7 +228,7 @@ internal abstract class AbstractSendChannel : SendChannel { * * ENQUEUE_FAILED -- buffer is not full (should not enqueue) * * ReceiveOrClosed<*> -- receiver is waiting or it is closed (should not enqueue) */ - private fun enqueueSend(send: Send): Any? { + protected open fun enqueueSend(send: Send): Any? { if (isBufferAlwaysFull) { queue.addLastIfPrev(send) { prev -> if (prev is ReceiveOrClosed<*>) return@enqueueSend prev @@ -381,7 +383,7 @@ internal abstract class AbstractSendChannel : SendChannel { private fun registerSelectSend(select: SelectInstance, element: E, block: suspend (SendChannel) -> R) { while (true) { if (select.isSelected) return - if (full) { + if (isFullImpl) { val node = SendSelect(element, this, select, block) val enqueueResult = enqueueSend(node) when { @@ -494,6 +496,7 @@ internal abstract class AbstractChannel : AbstractSendChannel(), Channel : AbstractSendChannel(), Channel : AbstractSendChannel(), Channel): Boolean { - val result = if (isBufferAlwaysEmpty) - queue.addLastIfPrev(receive) { it !is Send } else - queue.addLastIfPrevAndIf(receive, { it !is Send }, { isBufferEmpty }) + protected open fun enqueueReceiveInternal(receive: Receive): Boolean = if (isBufferAlwaysEmpty) + queue.addLastIfPrev(receive) { it !is Send } else + queue.addLastIfPrevAndIf(receive, { it !is Send }, { isBufferEmpty }) + + private fun enqueueReceive(receive: Receive) = enqueueReceiveInternal(receive).also { result -> if (result) onReceiveEnqueued() - return result } public final override suspend fun receiveOrNull(): E? { @@ -717,7 +721,7 @@ internal abstract class AbstractChannel : AbstractSendChannel(), Channel registerSelectReceiveMode(select: SelectInstance, receiveMode: Int, block: suspend (Any?) -> R) { while (true) { if (select.isSelected) return - if (isEmpty) { + if (isEmptyImpl) { if (enqueueReceiveSelect(select, block, receiveMode)) return } else { val pollResult = pollSelectInternal(select) @@ -1057,7 +1061,7 @@ internal class Closed( override fun toString(): String = "Closed@$hexAddress[$closeCause]" } -private abstract class Receive : LockFreeLinkedListNode(), ReceiveOrClosed { +internal abstract class Receive : LockFreeLinkedListNode(), ReceiveOrClosed { override val offerResult get() = OFFER_SUCCESS abstract fun resumeReceiveClosed(closed: Closed<*>) } diff --git a/kotlinx-coroutines-core/common/src/channels/ArrayBroadcastChannel.kt b/kotlinx-coroutines-core/common/src/channels/ArrayBroadcastChannel.kt index 5f3eb32d8f..19334ea706 100644 --- a/kotlinx-coroutines-core/common/src/channels/ArrayBroadcastChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/ArrayBroadcastChannel.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels @@ -371,4 +371,4 @@ internal class ArrayBroadcastChannel( override val bufferDebugString: String get() = "(buffer:capacity=${buffer.size},size=$size)" -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt b/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt index da284be525..e26579eff7 100644 --- a/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels @@ -36,34 +36,38 @@ internal open class ArrayChannel( */ private var buffer: Array = arrayOfNulls(min(capacity, 8)) private var head: Int = 0 - private var size = 0 // Invariant: size <= capacity + private val size = atomic(0) // Invariant: size <= capacity protected final override val isBufferAlwaysEmpty: Boolean get() = false - protected final override val isBufferEmpty: Boolean get() = lock.withLock { size == 0 } + protected final override val isBufferEmpty: Boolean get() = size.value == 0 protected final override val isBufferAlwaysFull: Boolean get() = false - protected final override val isBufferFull: Boolean get() = lock.withLock { size == capacity } + protected final override val isBufferFull: Boolean get() = size.value == capacity + + override val isFull: Boolean get() = lock.withLock { isFullImpl } + override val isEmpty: Boolean get() = lock.withLock { isEmptyImpl } + override val isClosedForReceive: Boolean get() = lock.withLock { super.isClosedForReceive } // result is `OFFER_SUCCESS | OFFER_FAILED | Closed` protected override fun offerInternal(element: E): Any { var receive: ReceiveOrClosed? = null lock.withLock { - val size = this.size + val size = this.size.value closedForSend?.let { return it } if (size < capacity) { // tentatively put element to buffer - this.size = size + 1 // update size before checking queue (!!!) + this.size.value = size + 1 // update size before checking queue (!!!) // check for receivers that were waiting on empty queue if (size == 0) { loop@ while (true) { receive = takeFirstReceiveOrPeekClosed() ?: break@loop // break when no receivers queued if (receive is Closed) { - this.size = size // restore size + this.size.value = size // restore size return receive!! } val token = receive!!.tryResumeReceive(element, null) if (token != null) { assert { token === RESUME_TOKEN } - this.size = size // restore size + this.size.value = size // restore size return@withLock } } @@ -84,11 +88,11 @@ internal open class ArrayChannel( protected override fun offerSelectInternal(element: E, select: SelectInstance<*>): Any { var receive: ReceiveOrClosed? = null lock.withLock { - val size = this.size + val size = this.size.value closedForSend?.let { return it } if (size < capacity) { // tentatively put element to buffer - this.size = size + 1 // update size before checking queue (!!!) + this.size.value = size + 1 // update size before checking queue (!!!) // check for receivers that were waiting on empty queue if (size == 0) { loop@ while (true) { @@ -96,14 +100,14 @@ internal open class ArrayChannel( val failure = select.performAtomicTrySelect(offerOp) when { failure == null -> { // offered successfully - this.size = size // restore size + this.size.value = size // restore size receive = offerOp.result return@withLock } failure === OFFER_FAILED -> break@loop // cannot offer -> Ok to queue to buffer failure === RETRY_ATOMIC -> {} // retry failure === ALREADY_SELECTED || failure is Closed<*> -> { - this.size = size // restore size + this.size.value = size // restore size return failure } else -> error("performAtomicTrySelect(describeTryOffer) returned $failure") @@ -112,7 +116,7 @@ internal open class ArrayChannel( } // let's try to select sending this element to buffer if (!select.trySelect()) { // :todo: move trySelect completion outside of lock - this.size = size // restore size + this.size.value = size // restore size return ALREADY_SELECTED } ensureCapacity(size) @@ -127,6 +131,10 @@ internal open class ArrayChannel( return receive!!.offerResult } + override fun enqueueSend(send: Send): Any? = lock.withLock { + super.enqueueSend(send) + } + // Guarded by lock private fun ensureCapacity(currentSize: Int) { if (currentSize >= buffer.size) { @@ -146,12 +154,12 @@ internal open class ArrayChannel( var resumed = false var result: Any? = null lock.withLock { - val size = this.size + val size = this.size.value if (size == 0) return closedForSend ?: POLL_FAILED // when nothing can be read from buffer // size > 0: not empty -- retrieve element result = buffer[head] buffer[head] = null - this.size = size - 1 // update size before checking queue (!!!) + this.size.value = size - 1 // update size before checking queue (!!!) // check for senders that were waiting on full queue var replacement: Any? = POLL_FAILED if (size == capacity) { @@ -167,7 +175,7 @@ internal open class ArrayChannel( } } if (replacement !== POLL_FAILED && replacement !is Closed<*>) { - this.size = size // restore size + this.size.value = size // restore size buffer[(head + size) % buffer.size] = replacement } head = (head + 1) % buffer.size @@ -184,12 +192,12 @@ internal open class ArrayChannel( var success = false var result: Any? = null lock.withLock { - val size = this.size + val size = this.size.value if (size == 0) return closedForSend ?: POLL_FAILED // size > 0: not empty -- retrieve element result = buffer[head] buffer[head] = null - this.size = size - 1 // update size before checking queue (!!!) + this.size.value = size - 1 // update size before checking queue (!!!) // check for senders that were waiting on full queue var replacement: Any? = POLL_FAILED if (size == capacity) { @@ -206,7 +214,7 @@ internal open class ArrayChannel( failure === POLL_FAILED -> break@loop // cannot poll -> Ok to take from buffer failure === RETRY_ATOMIC -> {} // retry failure === ALREADY_SELECTED -> { - this.size = size // restore size + this.size.value = size // restore size buffer[head] = result // restore head return failure } @@ -221,12 +229,12 @@ internal open class ArrayChannel( } } if (replacement !== POLL_FAILED && replacement !is Closed<*>) { - this.size = size // restore size + this.size.value = size // restore size buffer[(head + size) % buffer.size] = replacement } else { // failed to poll or is already closed --> let's try to select receiving this element from buffer if (!select.trySelect()) { // :todo: move trySelect completion outside of lock - this.size = size // restore size + this.size.value = size // restore size buffer[head] = result // restore head return ALREADY_SELECTED } @@ -239,16 +247,20 @@ internal open class ArrayChannel( return result } + override fun enqueueReceiveInternal(receive: Receive): Boolean = lock.withLock { + super.enqueueReceiveInternal(receive) + } + // Note: this function is invoked when channel is already closed override fun onCancelIdempotent(wasClosed: Boolean) { // clear buffer first, but do not wait for it in helpers if (wasClosed) { lock.withLock { - repeat(size) { + repeat(size.value) { buffer[head] = 0 head = (head + 1) % buffer.size } - size = 0 + size.value = 0 } } // then clean all queued senders @@ -258,5 +270,5 @@ internal open class ArrayChannel( // ------ debug ------ override val bufferDebugString: String - get() = "(buffer:capacity=$capacity,size=$size)" + get() = "(buffer:capacity=$capacity,size=${size.value})" } diff --git a/kotlinx-coroutines-core/common/src/channels/Broadcast.kt b/kotlinx-coroutines-core/common/src/channels/Broadcast.kt index e487a53431..318283cca9 100644 --- a/kotlinx-coroutines-core/common/src/channels/Broadcast.kt +++ b/kotlinx-coroutines-core/common/src/channels/Broadcast.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels diff --git a/kotlinx-coroutines-core/common/src/channels/BroadcastChannel.kt b/kotlinx-coroutines-core/common/src/channels/BroadcastChannel.kt index 2981d8394e..312480f943 100644 --- a/kotlinx-coroutines-core/common/src/channels/BroadcastChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/BroadcastChannel.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("FunctionName") diff --git a/kotlinx-coroutines-core/common/src/channels/Channel.kt b/kotlinx-coroutines-core/common/src/channels/Channel.kt index 07e05f07d9..8dff4ec2b7 100644 --- a/kotlinx-coroutines-core/common/src/channels/Channel.kt +++ b/kotlinx-coroutines-core/common/src/channels/Channel.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("FunctionName") @@ -586,4 +586,4 @@ public class ClosedSendChannelException(message: String?) : IllegalStateExceptio * * This exception is a subclass of [NoSuchElementException] to be consistent with plain collections. */ -public class ClosedReceiveChannelException(message: String?) : NoSuchElementException(message) \ No newline at end of file +public class ClosedReceiveChannelException(message: String?) : NoSuchElementException(message) diff --git a/kotlinx-coroutines-core/common/src/channels/ChannelCoroutine.kt b/kotlinx-coroutines-core/common/src/channels/ChannelCoroutine.kt index f824e69f8d..3f53b48c53 100644 --- a/kotlinx-coroutines-core/common/src/channels/ChannelCoroutine.kt +++ b/kotlinx-coroutines-core/common/src/channels/ChannelCoroutine.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels diff --git a/kotlinx-coroutines-core/common/src/channels/Channels.common.kt b/kotlinx-coroutines-core/common/src/channels/Channels.common.kt index cd37bfbc2e..4a73d5d59d 100644 --- a/kotlinx-coroutines-core/common/src/channels/Channels.common.kt +++ b/kotlinx-coroutines-core/common/src/channels/Channels.common.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmMultifileClass @file:JvmName("ChannelsKt") diff --git a/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt b/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt index a3e72a9c1b..4990c933ec 100644 --- a/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels @@ -10,6 +10,7 @@ import kotlinx.coroutines.internal.* import kotlinx.coroutines.intrinsics.* import kotlinx.coroutines.selects.* import kotlin.jvm.* +import kotlin.native.concurrent.* /** * Broadcasts the most recently sent element (aka [value]) to all [openSubscription] subscribers. @@ -90,7 +91,7 @@ public class ConflatedBroadcastChannel() : BroadcastChannel { */ public val valueOrNull: E? get() = when (val state = _state.value) { is Closed -> null - is State<*> -> UNDEFINED.unbox(state.value) + is State<*> -> UNDEFINED.unbox(state.value) else -> error("Invalid state $state") } diff --git a/kotlinx-coroutines-core/common/src/channels/ConflatedChannel.kt b/kotlinx-coroutines-core/common/src/channels/ConflatedChannel.kt index c04ccc4c39..399019c3ee 100644 --- a/kotlinx-coroutines-core/common/src/channels/ConflatedChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/ConflatedChannel.kt @@ -1,94 +1,140 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels -import kotlinx.coroutines.selects.* +import kotlinx.coroutines.* import kotlinx.coroutines.internal.* +import kotlinx.coroutines.selects.* +import kotlin.native.concurrent.* /** * Channel that buffers at most one element and conflates all subsequent `send` and `offer` invocations, * so that the receiver always gets the most recently sent element. - * Back-to-send sent elements are _conflated_ -- only the the most recently sent element is received, + * Back-to-send sent elements are _conflated_ -- only the most recently sent element is received, * while previously sent elements **are lost**. * Sender to this channel never suspends and [offer] always returns `true`. * * This channel is created by `Channel(Channel.CONFLATED)` factory function invocation. - * - * This implementation is fully lock-free. */ internal open class ConflatedChannel : AbstractChannel() { - protected final override val isBufferAlwaysEmpty: Boolean get() = true - protected final override val isBufferEmpty: Boolean get() = true + protected final override val isBufferAlwaysEmpty: Boolean get() = false + protected final override val isBufferEmpty: Boolean get() = value === EMPTY protected final override val isBufferAlwaysFull: Boolean get() = false protected final override val isBufferFull: Boolean get() = false - override fun onClosedIdempotent(closed: LockFreeLinkedListNode) { - @Suppress("UNCHECKED_CAST") - (closed.prevNode as? SendBuffered)?.let { lastBuffered -> - conflatePreviousSendBuffered(lastBuffered) - } - } + override val isEmpty: Boolean get() = lock.withLock { isEmptyImpl } - /** - * Queues conflated element, returns null on success or - * returns node reference if it was already closed or is waiting for receive. - */ - private fun sendConflated(element: E): ReceiveOrClosed<*>? { - val node = SendBuffered(element) - queue.addLastIfPrev(node) { prev -> - if (prev is ReceiveOrClosed<*>) return@sendConflated prev - true - } - conflatePreviousSendBuffered(node) - return null + private val lock = ReentrantLock() + + private var value: Any? = EMPTY + + private companion object { + @SharedImmutable + private val EMPTY = Symbol("EMPTY") } - private fun conflatePreviousSendBuffered(node: SendBuffered) { - // Conflate all previous SendBuffered, helping other sends to conflate - var prev = node.prevNode - while (prev is SendBuffered<*>) { - if (!prev.remove()) { - prev.helpRemove() + // result is `OFFER_SUCCESS | Closed` + protected override fun offerInternal(element: E): Any { + var receive: ReceiveOrClosed? = null + lock.withLock { + closedForSend?.let { return it } + // if there is no element written in buffer + if (value === EMPTY) { + // check for receivers that were waiting on the empty buffer + loop@ while(true) { + receive = takeFirstReceiveOrPeekClosed() ?: break@loop // break when no receivers queued + if (receive is Closed) { + return receive!! + } + val token = receive!!.tryResumeReceive(element, null) + if (token != null) { + assert { token === RESUME_TOKEN } + return@withLock + } + } } - prev = prev.prevNode + value = element + return OFFER_SUCCESS } + // breaks here if offer meets receiver + receive!!.completeResumeReceive(element) + return receive!!.offerResult } - // result is always `OFFER_SUCCESS | Closed` - protected override fun offerInternal(element: E): Any { - while (true) { - val result = super.offerInternal(element) - when { - result === OFFER_SUCCESS -> return OFFER_SUCCESS - result === OFFER_FAILED -> { // try to buffer - when (val sendResult = sendConflated(element)) { - null -> return OFFER_SUCCESS - is Closed<*> -> return sendResult + // result is `ALREADY_SELECTED | OFFER_SUCCESS | Closed` + protected override fun offerSelectInternal(element: E, select: SelectInstance<*>): Any { + var receive: ReceiveOrClosed? = null + lock.withLock { + closedForSend?.let { return it } + if (value === EMPTY) { + loop@ while(true) { + val offerOp = describeTryOffer(element) + val failure = select.performAtomicTrySelect(offerOp) + when { + failure == null -> { // offered successfully + receive = offerOp.result + return@withLock + } + failure === OFFER_FAILED -> break@loop // cannot offer -> Ok to queue to buffer + failure === RETRY_ATOMIC -> {} // retry + failure === ALREADY_SELECTED || failure is Closed<*> -> return failure + else -> error("performAtomicTrySelect(describeTryOffer) returned $failure") } - // otherwise there was receiver in queue, retry super.offerInternal } - result is Closed<*> -> return result - else -> error("Invalid offerInternal result $result") } + // try to select sending this element to buffer + if (!select.trySelect()) { + return ALREADY_SELECTED + } + value = element + return OFFER_SUCCESS } + // breaks here if offer meets receiver + receive!!.completeResumeReceive(element) + return receive!!.offerResult } - // result is always `ALREADY_SELECTED | OFFER_SUCCESS | Closed`. - protected override fun offerSelectInternal(element: E, select: SelectInstance<*>): Any { - while (true) { - val result = if (hasReceiveOrClosed) - super.offerSelectInternal(element, select) else - (select.performAtomicTrySelect(describeSendConflated(element)) ?: OFFER_SUCCESS) - when { - result === ALREADY_SELECTED -> return ALREADY_SELECTED - result === OFFER_SUCCESS -> return OFFER_SUCCESS - result === OFFER_FAILED -> {} // retry - result === RETRY_ATOMIC -> {} // retry - result is Closed<*> -> return result - else -> error("Invalid result $result") + // result is `E | POLL_FAILED | Closed` + protected override fun pollInternal(): Any? { + var result: Any? = null + lock.withLock { + if (value === EMPTY) return closedForSend ?: POLL_FAILED + result = value + value = EMPTY + } + return result + } + + // result is `E | POLL_FAILED | Closed` + protected override fun pollSelectInternal(select: SelectInstance<*>): Any? { + var result: Any? = null + lock.withLock { + if (value === EMPTY) return closedForSend ?: POLL_FAILED + if (!select.trySelect()) + return ALREADY_SELECTED + result = value + value = EMPTY + } + return result + } + + protected override fun onCancelIdempotent(wasClosed: Boolean) { + if (wasClosed) { + lock.withLock { + value = EMPTY } } + super.onCancelIdempotent(wasClosed) + } + + override fun enqueueReceiveInternal(receive: Receive): Boolean = lock.withLock { + super.enqueueReceiveInternal(receive) } + + // ------ debug ------ + + override val bufferDebugString: String + get() = "(value=$value)" } diff --git a/kotlinx-coroutines-core/common/src/channels/LinkedListChannel.kt b/kotlinx-coroutines-core/common/src/channels/LinkedListChannel.kt index d925be1c50..e66bbb2279 100644 --- a/kotlinx-coroutines-core/common/src/channels/LinkedListChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/LinkedListChannel.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels diff --git a/kotlinx-coroutines-core/common/src/channels/Produce.kt b/kotlinx-coroutines-core/common/src/channels/Produce.kt index 68fb09a41c..24fd399bb7 100644 --- a/kotlinx-coroutines-core/common/src/channels/Produce.kt +++ b/kotlinx-coroutines-core/common/src/channels/Produce.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels @@ -27,7 +27,7 @@ public interface ProducerScope : CoroutineScope, SendChannel { /** * Suspends the current coroutine until the channel is either [closed][SendChannel.close] or [cancelled][ReceiveChannel.cancel] - * and invokes the given [block] before resuming the coroutine. + * and invokes the given [block] before resuming the coroutine. This suspending function is cancellable. * * Note that when the producer channel is cancelled, this function resumes with a cancellation exception. * Therefore, in case of cancellation, no code after the call to this function will be executed. @@ -115,6 +115,7 @@ public fun CoroutineScope.produce( public fun CoroutineScope.produce( context: CoroutineContext = EmptyCoroutineContext, capacity: Int = 0, + start: CoroutineStart = CoroutineStart.DEFAULT, onCompletion: CompletionHandler? = null, @BuilderInference block: suspend ProducerScope.() -> Unit ): ReceiveChannel { @@ -122,7 +123,7 @@ public fun CoroutineScope.produce( val newContext = newCoroutineContext(context) val coroutine = ProducerCoroutine(newContext, channel) if (onCompletion != null) coroutine.invokeOnCompletion(handler = onCompletion) - coroutine.start(CoroutineStart.DEFAULT, coroutine, block) + coroutine.start(start, coroutine, block) return coroutine } diff --git a/kotlinx-coroutines-core/common/src/channels/RendezvousChannel.kt b/kotlinx-coroutines-core/common/src/channels/RendezvousChannel.kt index 98df176937..700f50908c 100644 --- a/kotlinx-coroutines-core/common/src/channels/RendezvousChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/RendezvousChannel.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels diff --git a/kotlinx-coroutines-core/common/src/flow/Builders.kt b/kotlinx-coroutines-core/common/src/flow/Builders.kt index 49ad2922e9..4157576aae 100644 --- a/kotlinx-coroutines-core/common/src/flow/Builders.kt +++ b/kotlinx-coroutines-core/common/src/flow/Builders.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmMultifileClass @@ -11,9 +11,9 @@ import kotlinx.coroutines.* import kotlinx.coroutines.channels.* import kotlinx.coroutines.channels.Channel.Factory.BUFFERED import kotlinx.coroutines.flow.internal.* -import kotlinx.coroutines.flow.internal.unsafeFlow as flow import kotlin.coroutines.* import kotlin.jvm.* +import kotlinx.coroutines.flow.internal.unsafeFlow as flow /** * Creates a flow from the given suspendable [block]. @@ -51,7 +51,12 @@ public fun flow(@BuilderInference block: suspend FlowCollector.() -> Unit // Named anonymous object private class SafeFlow(private val block: suspend FlowCollector.() -> Unit) : Flow { override suspend fun collect(collector: FlowCollector) { - SafeCollector(collector, coroutineContext).block() + val safeCollector = SafeCollector(collector, coroutineContext) + try { + safeCollector.block() + } finally { + safeCollector.releaseIntercepted() + } } } @@ -259,10 +264,16 @@ public fun channelFlow(@BuilderInference block: suspend ProducerScope.() * * This builder ensures thread-safety and context preservation, thus the provided [ProducerScope] can be used * from any context, e.g. from a callback-based API. - * The resulting flow completes as soon as the code in the [block] and all its children completes. - * Use [awaitClose] as the last statement to keep it running. - * The [awaitClose] argument is called either when a flow consumer cancels the flow collection - * or when a callback-based API invokes [SendChannel.close] manually. + * The resulting flow completes as soon as the code in the [block] completes. + * [awaitClose] should be used to keep the flow running, otherwise the channel will be closed immediately + * when block completes. + * [awaitClose] argument is called either when a flow consumer cancels the flow collection + * or when a callback-based API invokes [SendChannel.close] manually and is typically used + * to cleanup the resources after the completion, e.g. unregister a callback. + * Using [awaitClose] is mandatory in order to prevent memory leaks when the flow collection is cancelled, + * otherwise the callback may keep running even when the flow collector is already completed. + * To avoid such leaks, this method throws [IllegalStateException] if block returns, but the channel + * is not closed yet. * * A channel with the [default][Channel.BUFFERED] buffer size is used. Use the [buffer] operator on the * resulting flow to specify a user-defined value and to control what happens when data is produced faster @@ -277,9 +288,13 @@ public fun channelFlow(@BuilderInference block: suspend ProducerScope.() * fun flowFrom(api: CallbackBasedApi): Flow = callbackFlow { * val callback = object : Callback { // implementation of some callback interface * override fun onNextValue(value: T) { - * // Note: offer drops value when buffer is full - * // Use either buffer(Channel.CONFLATED) or buffer(Channel.UNLIMITED) to avoid overfill - * offer(value) + * // To avoid blocking you can configure channel capacity using + * // either buffer(Channel.CONFLATED) or buffer(Channel.UNLIMITED) to avoid overfill + * try { + * sendBlocking(value) + * } catch (e: Exception) { + * // Handle exception from the channel: failure in flow or premature closing + * } * } * override fun onApiError(cause: Throwable) { * cancel(CancellationException("API Error", cause)) @@ -287,21 +302,20 @@ public fun channelFlow(@BuilderInference block: suspend ProducerScope.() * override fun onCompleted() = channel.close() * } * api.register(callback) - * // Suspend until either onCompleted or external cancellation are invoked + * /* + * * Suspends until either 'onCompleted'/'onApiError' from the callback is invoked + * * or flow collector is cancelled (e.g. by 'take(1)' or because a collector's coroutine was cancelled). + * * In both cases, callback will be properly unregistered. + * */ * awaitClose { api.unregister(callback) } * } * ``` - * - * This function is an alias for [channelFlow], it has a separate name to reflect - * the intent of the usage (integration with a callback-based API) better. */ -@Suppress("NOTHING_TO_INLINE") @ExperimentalCoroutinesApi -public inline fun callbackFlow(@BuilderInference noinline block: suspend ProducerScope.() -> Unit): Flow = - channelFlow(block) +public fun callbackFlow(@BuilderInference block: suspend ProducerScope.() -> Unit): Flow = CallbackFlowBuilder(block) // ChannelFlow implementation that is the first in the chain of flow operations and introduces (builds) a flow -private class ChannelFlowBuilder( +private open class ChannelFlowBuilder( private val block: suspend ProducerScope.() -> Unit, context: CoroutineContext = EmptyCoroutineContext, capacity: Int = BUFFERED @@ -315,3 +329,31 @@ private class ChannelFlowBuilder( override fun toString(): String = "block[$block] -> ${super.toString()}" } + +private class CallbackFlowBuilder( + private val block: suspend ProducerScope.() -> Unit, + context: CoroutineContext = EmptyCoroutineContext, + capacity: Int = BUFFERED +) : ChannelFlowBuilder(block, context, capacity) { + + override suspend fun collectTo(scope: ProducerScope) { + super.collectTo(scope) + /* + * We expect user either call `awaitClose` from within a block (then the channel is closed at this moment) + * or being closed/cancelled externally/manually. Otherwise "user forgot to call + * awaitClose and receives unhelpful ClosedSendChannelException exceptions" situation is detected. + */ + if (!scope.isClosedForSend) { + throw IllegalStateException( + """ + 'awaitClose { yourCallbackOrListener.cancel() }' should be used in the end of callbackFlow block. + Otherwise, a callback/listener may leak in case of external cancellation. + See callbackFlow API documentation for the details. + """.trimIndent() + ) + } + } + + override fun create(context: CoroutineContext, capacity: Int): ChannelFlow = + CallbackFlowBuilder(block, context, capacity) +} diff --git a/kotlinx-coroutines-core/common/src/flow/Channels.kt b/kotlinx-coroutines-core/common/src/flow/Channels.kt index 1a572e8582..e3a64b9568 100644 --- a/kotlinx-coroutines-core/common/src/flow/Channels.kt +++ b/kotlinx-coroutines-core/common/src/flow/Channels.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmMultifileClass @@ -23,8 +23,11 @@ import kotlinx.coroutines.flow.internal.unsafeFlow as flow * This function provides a more efficient shorthand for `channel.consumeEach { value -> emit(value) }`. * See [consumeEach][ReceiveChannel.consumeEach]. */ -@ExperimentalCoroutinesApi -public suspend fun FlowCollector.emitAll(channel: ReceiveChannel) { +@ExperimentalCoroutinesApi // since version 1.3.0 +public suspend fun FlowCollector.emitAll(channel: ReceiveChannel) = + emitAllImpl(channel, consume = true) + +private suspend fun FlowCollector.emitAllImpl(channel: ReceiveChannel, consume: Boolean) { // Manually inlined "consumeEach" implementation that does not use iterator but works via "receiveOrClosed". // It has smaller and more efficient spilled state which also allows to implement a manual kludge to // fix retention of the last emitted value. @@ -59,20 +62,43 @@ public suspend fun FlowCollector.emitAll(channel: ReceiveChannel) { cause = e throw e } finally { - channel.cancelConsumed(cause) + if (consume) channel.cancelConsumed(cause) } } +/** + * Represents the given receive channel as a hot flow and [receives][ReceiveChannel.receive] from the channel + * in fan-out fashion every time this flow is collected. One element will be emitted to one collector only. + * + * See also [consumeAsFlow] which ensures that the resulting flow is collected just once. + * + * ### Cancellation semantics + * + * * Flow collectors are cancelled when the original channel is [closed][SendChannel.close] with an exception. + * * Flow collectors complete normally when the original channel is [closed][SendChannel.close] normally. + * * Failure or cancellation of the flow collector does not affect the channel. + * + * ### Operator fusion + * + * Adjacent applications of [flowOn], [buffer], [conflate], and [produceIn] to the result of `receiveAsFlow` are fused. + * In particular, [produceIn] returns the original channel. + * Calls to [flowOn] have generally no effect, unless [buffer] is used to explicitly request buffering. + */ +@ExperimentalCoroutinesApi // since version 1.4.0 +public fun ReceiveChannel.receiveAsFlow(): Flow = ChannelAsFlow(this, consume = false) + /** * Represents the given receive channel as a hot flow and [consumes][ReceiveChannel.consume] the channel * on the first collection from this flow. The resulting flow can be collected just once and throws * [IllegalStateException] when trying to collect it more than once. * + * See also [receiveAsFlow] which supports multiple collectors of the resulting flow. + * * ### Cancellation semantics * - * 1) Flow consumer is cancelled when the original channel is cancelled. - * 2) Flow consumer completes normally when the original channel was closed normally and then fully consumed. - * 3) If the flow consumer fails with an exception, channel is cancelled. + * * Flow collector is cancelled when the original channel is [closed][SendChannel.close] with an exception. + * * Flow collector completes normally when the original channel is [closed][SendChannel.close] normally. + * * If the flow collector fails with an exception, the source channel is [cancelled][ReceiveChannel.cancel]. * * ### Operator fusion * @@ -80,8 +106,8 @@ public suspend fun FlowCollector.emitAll(channel: ReceiveChannel) { * In particular, [produceIn] returns the original channel (but throws [IllegalStateException] on repeated calls). * Calls to [flowOn] have generally no effect, unless [buffer] is used to explicitly request buffering. */ -@FlowPreview -public fun ReceiveChannel.consumeAsFlow(): Flow = ConsumeAsFlow(this) +@ExperimentalCoroutinesApi // since version 1.3.0 +public fun ReceiveChannel.consumeAsFlow(): Flow = ChannelAsFlow(this, consume = true) /** * Represents an existing [channel] as [ChannelFlow] implementation. @@ -89,21 +115,25 @@ public fun ReceiveChannel.consumeAsFlow(): Flow = ConsumeAsFlow(this) * However, additional [buffer] calls cause a separate buffering channel to be created and that is where * the context might play a role, because it is used by the producing coroutine. */ -private class ConsumeAsFlow( +private class ChannelAsFlow( private val channel: ReceiveChannel, + private val consume: Boolean, context: CoroutineContext = EmptyCoroutineContext, capacity: Int = Channel.OPTIONAL_CHANNEL ) : ChannelFlow(context, capacity) { private val consumed = atomic(false) - private fun markConsumed() = - check(!consumed.getAndSet(true)) { "ReceiveChannel.consumeAsFlow can be collected just once" } + private fun markConsumed() { + if (consume) { + check(!consumed.getAndSet(true)) { "ReceiveChannel.consumeAsFlow can be collected just once" } + } + } override fun create(context: CoroutineContext, capacity: Int): ChannelFlow = - ConsumeAsFlow(channel, context, capacity) + ChannelAsFlow(channel, consume, context, capacity) override suspend fun collectTo(scope: ProducerScope) = - SendingCollector(scope).emitAll(channel) // use efficient channel receiving code from emitAll + SendingCollector(scope).emitAllImpl(channel, consume) // use efficient channel receiving code from emitAll override fun broadcastImpl(scope: CoroutineScope, start: CoroutineStart): BroadcastChannel { markConsumed() // fail fast on repeated attempt to collect it @@ -121,7 +151,7 @@ private class ConsumeAsFlow( override suspend fun collect(collector: FlowCollector) { if (capacity == Channel.OPTIONAL_CHANNEL) { markConsumed() - collector.emitAll(channel) // direct + collector.emitAllImpl(channel, consume) // direct } else { super.collect(collector) // extra buffering channel, produceImpl will mark it as consumed } @@ -151,7 +181,7 @@ public fun BroadcastChannel.asFlow(): Flow = flow { * that collects the given flow and thus resulting channel should be properly closed or cancelled. * * A channel with [default][Channel.Factory.BUFFERED] buffer size is created. - * Use [buffer] operator on the flow before calling `produce` to specify a value other than + * Use [buffer] operator on the flow before calling `broadcastIn` to specify a value other than * default and to control what happens when data is produced faster than it is consumed, * that is to control backpressure behavior. */ @@ -169,7 +199,7 @@ public fun Flow.broadcastIn( * that collects the given flow and thus resulting channel should be properly closed or cancelled. * * A channel with [default][Channel.Factory.BUFFERED] buffer size is created. - * Use [buffer] operator on the flow before calling `produce` to specify a value other than + * Use [buffer] operator on the flow before calling `produceIn` to specify a value other than * default and to control what happens when data is produced faster than it is consumed, * that is to control backpressure behavior. */ diff --git a/kotlinx-coroutines-core/common/src/flow/Flow.kt b/kotlinx-coroutines-core/common/src/flow/Flow.kt index 6d87c2b9aa..378b9f148e 100644 --- a/kotlinx-coroutines-core/common/src/flow/Flow.kt +++ b/kotlinx-coroutines-core/common/src/flow/Flow.kt @@ -1,11 +1,11 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 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 kotlinx.coroutines.flow.internal.SafeCollector +import kotlinx.coroutines.flow.internal.* import kotlin.coroutines.* /** @@ -149,8 +149,8 @@ import kotlin.coroutines.* * it hard to reason about the code because an exception in the `collect { ... }` could be somehow "caught" * by an upstream flow, limiting the ability of local reasoning about the code. * - * Currently, the flow infrastructure does not enforce exception transparency contracts, however, it might be enforced - * in the future either at run time or at compile time. + * Flow machinery enforces exception transparency at runtime and throws [IllegalStateException] on any attempt to emit a value, + * if an exception has been thrown on previous attempt. * * ### Reactive streams * @@ -199,7 +199,12 @@ public abstract class AbstractFlow : Flow { @InternalCoroutinesApi public final override suspend fun collect(collector: FlowCollector) { - collectSafely(SafeCollector(collector, collectContext = coroutineContext)) + val safeCollector = SafeCollector(collector, coroutineContext) + try { + collectSafely(safeCollector) + } finally { + safeCollector.releaseIntercepted() + } } /** diff --git a/kotlinx-coroutines-core/common/src/flow/FlowCollector.kt b/kotlinx-coroutines-core/common/src/flow/FlowCollector.kt index 7254c6d7bb..8c6208bfd2 100644 --- a/kotlinx-coroutines-core/common/src/flow/FlowCollector.kt +++ b/kotlinx-coroutines-core/common/src/flow/FlowCollector.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.flow diff --git a/kotlinx-coroutines-core/common/src/flow/Migration.kt b/kotlinx-coroutines-core/common/src/flow/Migration.kt index ade6078ff7..16bde89f44 100644 --- a/kotlinx-coroutines-core/common/src/flow/Migration.kt +++ b/kotlinx-coroutines-core/common/src/flow/Migration.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmMultifileClass diff --git a/kotlinx-coroutines-core/common/src/flow/internal/ChannelFlow.kt b/kotlinx-coroutines-core/common/src/flow/internal/ChannelFlow.kt index 4711b88418..8a18bff30e 100644 --- a/kotlinx-coroutines-core/common/src/flow/internal/ChannelFlow.kt +++ b/kotlinx-coroutines-core/common/src/flow/internal/ChannelFlow.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.flow.internal @@ -27,6 +27,14 @@ public abstract class ChannelFlow( // buffer capacity between upstream and downstream context @JvmField val capacity: Int ) : Flow { + + // shared code to create a suspend lambda from collectTo function in one place + internal val collectToFun: suspend (ProducerScope) -> Unit + get() = { collectTo(it) } + + private val produceCapacity: Int + get() = if (capacity == Channel.OPTIONAL_CHANNEL) Channel.BUFFERED else capacity + public fun update( context: CoroutineContext = EmptyCoroutineContext, capacity: Int = Channel.OPTIONAL_CHANNEL @@ -57,29 +65,30 @@ public abstract class ChannelFlow( protected abstract suspend fun collectTo(scope: ProducerScope) - // shared code to create a suspend lambda from collectTo function in one place - internal val collectToFun: suspend (ProducerScope) -> Unit - get() = { collectTo(it) } - - private val produceCapacity: Int - get() = if (capacity == Channel.OPTIONAL_CHANNEL) Channel.BUFFERED else capacity - open fun broadcastImpl(scope: CoroutineScope, start: CoroutineStart): BroadcastChannel = scope.broadcast(context, produceCapacity, start, block = collectToFun) + /** + * Here we use ATOMIC start for a reason (#1825). + * NB: [produceImpl] is used for [flowOn]. + * For non-atomic start it is possible to observe the situation, + * where the pipeline after the [flowOn] call successfully executes (mostly, its `onCompletion`) + * handlers, while the pipeline before does not, because it was cancelled during its dispatch. + * Thus `onCompletion` and `finally` blocks won't be executed and it may lead to a different kinds of memory leaks. + */ open fun produceImpl(scope: CoroutineScope): ReceiveChannel = - scope.produce(context, produceCapacity, block = collectToFun) + scope.produce(context, produceCapacity, start = CoroutineStart.ATOMIC, block = collectToFun) override suspend fun collect(collector: FlowCollector) = coroutineScope { collector.emitAll(produceImpl(this)) } + open fun additionalToStringProps() = "" + // debug toString override fun toString(): String = "$classSimpleName[${additionalToStringProps()}context=$context, capacity=$capacity]" - - open fun additionalToStringProps() = "" } // ChannelFlow implementation that operates on another flow before it @@ -161,7 +170,7 @@ private suspend fun withContextUndispatched( countOrElement: Any = threadContextElements(newContext), // can be precomputed for speed block: suspend (V) -> T, value: V ): T = - suspendCoroutineUninterceptedOrReturn sc@{ uCont -> + suspendCoroutineUninterceptedOrReturn { uCont -> withCoroutineContext(newContext, countOrElement) { block.startCoroutineUninterceptedOrReturn(value, Continuation(newContext) { uCont.resumeWith(it) diff --git a/kotlinx-coroutines-core/common/src/flow/internal/Combine.kt b/kotlinx-coroutines-core/common/src/flow/internal/Combine.kt index 584178d8c4..67da32c9f9 100644 --- a/kotlinx-coroutines-core/common/src/flow/internal/Combine.kt +++ b/kotlinx-coroutines-core/common/src/flow/internal/Combine.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("UNCHECKED_CAST", "NON_APPLICABLE_CALL_FOR_BUILDER_INFERENCE") // KT-32203 diff --git a/kotlinx-coroutines-core/common/src/flow/internal/FlowCoroutine.kt b/kotlinx-coroutines-core/common/src/flow/internal/FlowCoroutine.kt index 1917afb8d7..acc6ca04ec 100644 --- a/kotlinx-coroutines-core/common/src/flow/internal/FlowCoroutine.kt +++ b/kotlinx-coroutines-core/common/src/flow/internal/FlowCoroutine.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.flow.internal @@ -48,8 +48,7 @@ internal suspend fun flowScope(@BuilderInference block: suspend CoroutineSco */ internal fun scopedFlow(@BuilderInference block: suspend CoroutineScope.(FlowCollector) -> Unit): Flow = flow { - val collector = this - flowScope { block(collector) } + flowScope { block(this@flow) } } internal fun CoroutineScope.flowProduce( @@ -60,7 +59,7 @@ internal fun CoroutineScope.flowProduce( val channel = Channel(capacity) val newContext = newCoroutineContext(context) val coroutine = FlowProduceCoroutine(newContext, channel) - coroutine.start(CoroutineStart.DEFAULT, coroutine, block) + coroutine.start(CoroutineStart.ATOMIC, coroutine, block) return coroutine } diff --git a/kotlinx-coroutines-core/common/src/flow/internal/FlowExceptions.common.kt b/kotlinx-coroutines-core/common/src/flow/internal/FlowExceptions.common.kt index 7058dca5f5..3064ed260b 100644 --- a/kotlinx-coroutines-core/common/src/flow/internal/FlowExceptions.common.kt +++ b/kotlinx-coroutines-core/common/src/flow/internal/FlowExceptions.common.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.flow.internal diff --git a/kotlinx-coroutines-core/common/src/flow/internal/Merge.kt b/kotlinx-coroutines-core/common/src/flow/internal/Merge.kt index 6fbbea31dd..798f38b8bd 100644 --- a/kotlinx-coroutines-core/common/src/flow/internal/Merge.kt +++ b/kotlinx-coroutines-core/common/src/flow/internal/Merge.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.flow.internal diff --git a/kotlinx-coroutines-core/common/src/flow/internal/NopCollector.kt b/kotlinx-coroutines-core/common/src/flow/internal/NopCollector.kt index d1b6ad2bf5..62a20a07c3 100644 --- a/kotlinx-coroutines-core/common/src/flow/internal/NopCollector.kt +++ b/kotlinx-coroutines-core/common/src/flow/internal/NopCollector.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.flow.internal @@ -10,4 +10,4 @@ internal object NopCollector : FlowCollector { override suspend fun emit(value: Any?) { // does nothing } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/common/src/flow/internal/NullSurrogate.kt b/kotlinx-coroutines-core/common/src/flow/internal/NullSurrogate.kt index c6ff12fc4e..22e1957419 100644 --- a/kotlinx-coroutines-core/common/src/flow/internal/NullSurrogate.kt +++ b/kotlinx-coroutines-core/common/src/flow/internal/NullSurrogate.kt @@ -1,11 +1,12 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.flow.internal import kotlinx.coroutines.internal.* import kotlin.jvm.* +import kotlin.native.concurrent.* /** * This value is used a a surrogate `null` value when needed. diff --git a/kotlinx-coroutines-core/common/src/flow/internal/SafeCollector.common.kt b/kotlinx-coroutines-core/common/src/flow/internal/SafeCollector.common.kt new file mode 100644 index 0000000000..c89e94f592 --- /dev/null +++ b/kotlinx-coroutines-core/common/src/flow/internal/SafeCollector.common.kt @@ -0,0 +1,111 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.flow.internal + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.internal.ScopeCoroutine +import kotlin.coroutines.* +import kotlin.jvm.* + +internal expect class SafeCollector( + collector: FlowCollector, + collectContext: CoroutineContext +) : FlowCollector { + internal val collector: FlowCollector + internal val collectContext: CoroutineContext + internal val collectContextSize: Int + public fun releaseIntercepted() +} + +@JvmName("checkContext") // For prettier stack traces +internal fun SafeCollector<*>.checkContext(currentContext: CoroutineContext) { + val result = currentContext.fold(0) fold@{ count, element -> + val key = element.key + val collectElement = collectContext[key] + if (key !== Job) { + return@fold if (element !== collectElement) Int.MIN_VALUE + else count + 1 + } + + val collectJob = collectElement as Job? + val emissionParentJob = (element as Job).transitiveCoroutineParent(collectJob) + /* + * Code like + * ``` + * coroutineScope { + * launch { + * emit(1) + * } + * + * launch { + * emit(2) + * } + * } + * ``` + * is prohibited because 'emit' is not thread-safe by default. Use 'channelFlow' instead if you need concurrent emission + * or want to switch context dynamically (e.g. with `withContext`). + * + * Note that collecting from another coroutine is allowed, e.g.: + * ``` + * coroutineScope { + * val channel = produce { + * collect { value -> + * send(value) + * } + * } + * channel.consumeEach { value -> + * emit(value) + * } + * } + * ``` + * is a completely valid. + */ + if (emissionParentJob !== collectJob) { + error( + "Flow invariant is violated:\n" + + "\t\tEmission from another coroutine is detected.\n" + + "\t\tChild of $emissionParentJob, expected child of $collectJob.\n" + + "\t\tFlowCollector is not thread-safe and concurrent emissions are prohibited.\n" + + "\t\tTo mitigate this restriction please use 'channelFlow' builder instead of 'flow'" + ) + } + + /* + * If collect job is null (-> EmptyCoroutineContext, probably run from `suspend fun main`), then invariant is maintained + * (common transitive parent is "null"), but count check will fail, so just do not count job context element when + * flow is collected from EmptyCoroutineContext + */ + if (collectJob == null) count else count + 1 + } + if (result != collectContextSize) { + error( + "Flow invariant is violated:\n" + + "\t\tFlow was collected in $collectContext,\n" + + "\t\tbut emission happened in $currentContext.\n" + + "\t\tPlease refer to 'flow' documentation or use 'flowOn' instead" + ) + } +} + +internal tailrec fun Job?.transitiveCoroutineParent(collectJob: Job?): Job? { + if (this === null) return null + if (this === collectJob) return this + if (this !is ScopeCoroutine<*>) return this + return parent.transitiveCoroutineParent(collectJob) +} + +/** + * An analogue of the [flow] builder that does not check the context of execution of the resulting flow. + * Used in our own operators where we trust the context of invocations. + */ +@PublishedApi +internal inline fun unsafeFlow(@BuilderInference crossinline block: suspend FlowCollector.() -> Unit): Flow { + return object : Flow { + override suspend fun collect(collector: FlowCollector) { + collector.block() + } + } +} diff --git a/kotlinx-coroutines-core/common/src/flow/internal/SafeCollector.kt b/kotlinx-coroutines-core/common/src/flow/internal/SafeCollector.kt deleted file mode 100644 index fec0ee96e0..0000000000 --- a/kotlinx-coroutines-core/common/src/flow/internal/SafeCollector.kt +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.coroutines.flow.internal - -import kotlinx.coroutines.* -import kotlinx.coroutines.flow.* -import kotlinx.coroutines.internal.* -import kotlin.coroutines.* - -internal class SafeCollector( - private val collector: FlowCollector, - private val collectContext: CoroutineContext -) : FlowCollector { - - // Note, it is non-capturing lambda, so no extra allocation during init of SafeCollector - private val collectContextSize = collectContext.fold(0) { count, _ -> count + 1 } - private var lastEmissionContext: CoroutineContext? = null - - override suspend fun emit(value: T) { - /* - * Benign data-race here: - * We read potentially racy published coroutineContext, but we only use it for - * referential comparison (=> thus safe) and are not using it for structural comparisons. - */ - val currentContext = coroutineContext - // This check is triggered once per flow on happy path. - if (lastEmissionContext !== currentContext) { - checkContext(currentContext) - lastEmissionContext = currentContext - } - collector.emit(value) // TCE - } - - private fun checkContext(currentContext: CoroutineContext) { - val result = currentContext.fold(0) fold@{ count, element -> - val key = element.key - val collectElement = collectContext[key] - if (key !== Job) { - return@fold if (element !== collectElement) Int.MIN_VALUE - else count + 1 - } - - val collectJob = collectElement as Job? - val emissionParentJob = (element as Job).transitiveCoroutineParent(collectJob) - /* - * Things like - * ``` - * coroutineScope { - * launch { - * emit(1) - * } - * - * launch { - * emit(2) - * } - * } - * ``` - * are prohibited because 'emit' is not thread-safe by default. Use channelFlow instead if you need concurrent emission - * or want to switch context dynamically (e.g. with `withContext`). - * - * Note that collecting from another coroutine is allowed, e.g.: - * ``` - * coroutineScope { - * val channel = produce { - * collect { value -> - * send(value) - * } - * } - * channel.consumeEach { value -> - * emit(value) - * } - * } - * ``` - * is a completely valid. - */ - if (emissionParentJob !== collectJob) { - error( - "Flow invariant is violated:\n" + - "\t\tEmission from another coroutine is detected.\n" + - "\t\tChild of $emissionParentJob, expected child of $collectJob.\n" + - "\t\tFlowCollector is not thread-safe and concurrent emissions are prohibited.\n" + - "\t\tTo mitigate this restriction please use 'channelFlow' builder instead of 'flow'" - ) - } - - /* - * If collect job is null (-> EmptyCoroutineContext, probably run from `suspend fun main`), then invariant is maintained - * (common transitive parent is "null"), but count check will fail, so just do not count job context element when - * flow is collected from EmptyCoroutineContext - */ - if (collectJob == null) count else count + 1 - } - if (result != collectContextSize) { - error( - "Flow invariant is violated:\n" + - "\t\tFlow was collected in $collectContext,\n" + - "\t\tbut emission happened in $currentContext.\n" + - "\t\tPlease refer to 'flow' documentation or use 'flowOn' instead" - ) - } - } - - private tailrec fun Job?.transitiveCoroutineParent(collectJob: Job?): Job? { - if (this === null) return null - if (this === collectJob) return this - if (this !is ScopeCoroutine<*>) return this - return parent.transitiveCoroutineParent(collectJob) - } -} - -/** - * An analogue of the [flow] builder that does not check the context of execution of the resulting flow. - * Used in our own operators where we trust the context of invocations. - */ -@PublishedApi -internal inline fun unsafeFlow(@BuilderInference crossinline block: suspend FlowCollector.() -> Unit): Flow { - return object : Flow { - override suspend fun collect(collector: FlowCollector) { - collector.block() - } - } -} diff --git a/kotlinx-coroutines-core/common/src/flow/internal/SendingCollector.kt b/kotlinx-coroutines-core/common/src/flow/internal/SendingCollector.kt index b6d578fedc..1620a2ac7c 100644 --- a/kotlinx-coroutines-core/common/src/flow/internal/SendingCollector.kt +++ b/kotlinx-coroutines-core/common/src/flow/internal/SendingCollector.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.flow.internal diff --git a/kotlinx-coroutines-core/common/src/flow/operators/Context.kt b/kotlinx-coroutines-core/common/src/flow/operators/Context.kt index 043c839fff..4e6167f0cd 100644 --- a/kotlinx-coroutines-core/common/src/flow/operators/Context.kt +++ b/kotlinx-coroutines-core/common/src/flow/operators/Context.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmMultifileClass diff --git a/kotlinx-coroutines-core/common/src/flow/operators/Delay.kt b/kotlinx-coroutines-core/common/src/flow/operators/Delay.kt index 5f2f46629f..e473561e3d 100644 --- a/kotlinx-coroutines-core/common/src/flow/operators/Delay.kt +++ b/kotlinx-coroutines-core/common/src/flow/operators/Delay.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmMultifileClass diff --git a/kotlinx-coroutines-core/common/src/flow/operators/Distinct.kt b/kotlinx-coroutines-core/common/src/flow/operators/Distinct.kt index 89491f4166..53c4d6d2aa 100644 --- a/kotlinx-coroutines-core/common/src/flow/operators/Distinct.kt +++ b/kotlinx-coroutines-core/common/src/flow/operators/Distinct.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmMultifileClass diff --git a/kotlinx-coroutines-core/common/src/flow/operators/Emitters.kt b/kotlinx-coroutines-core/common/src/flow/operators/Emitters.kt index 6a910764b7..9236fd26d8 100644 --- a/kotlinx-coroutines-core/common/src/flow/operators/Emitters.kt +++ b/kotlinx-coroutines-core/common/src/flow/operators/Emitters.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmMultifileClass @@ -71,7 +71,12 @@ internal inline fun Flow.unsafeTransform( public fun Flow.onStart( action: suspend FlowCollector.() -> Unit ): Flow = unsafeFlow { // Note: unsafe flow is used here, but safe collector is used to invoke start action - SafeCollector(this, coroutineContext).action() + val safeCollector = SafeCollector(this, coroutineContext) + try { + safeCollector.action() + } finally { + safeCollector.releaseIntercepted() + } collect(this) // directly delegate } @@ -141,7 +146,12 @@ public fun Flow.onCompletion( throw e } // Exception from the upstream or normal completion - SafeCollector(this, coroutineContext).invokeSafely(action, exception) + val safeCollector = SafeCollector(this, coroutineContext) + try { + safeCollector.invokeSafely(action, exception) + } finally { + safeCollector.releaseIntercepted() + } exception?.let { throw it } } diff --git a/kotlinx-coroutines-core/common/src/flow/operators/Errors.kt b/kotlinx-coroutines-core/common/src/flow/operators/Errors.kt index 9b7a91f155..ad77ba9b14 100644 --- a/kotlinx-coroutines-core/common/src/flow/operators/Errors.kt +++ b/kotlinx-coroutines-core/common/src/flow/operators/Errors.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmMultifileClass diff --git a/kotlinx-coroutines-core/common/src/flow/operators/Limit.kt b/kotlinx-coroutines-core/common/src/flow/operators/Limit.kt index 6f4e8e754c..988c22f489 100644 --- a/kotlinx-coroutines-core/common/src/flow/operators/Limit.kt +++ b/kotlinx-coroutines-core/common/src/flow/operators/Limit.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmMultifileClass diff --git a/kotlinx-coroutines-core/common/src/flow/operators/Merge.kt b/kotlinx-coroutines-core/common/src/flow/operators/Merge.kt index d69afad2f3..85fe90b91f 100644 --- a/kotlinx-coroutines-core/common/src/flow/operators/Merge.kt +++ b/kotlinx-coroutines-core/common/src/flow/operators/Merge.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmMultifileClass diff --git a/kotlinx-coroutines-core/common/src/flow/operators/Transform.kt b/kotlinx-coroutines-core/common/src/flow/operators/Transform.kt index da5e288a66..f446c80c23 100644 --- a/kotlinx-coroutines-core/common/src/flow/operators/Transform.kt +++ b/kotlinx-coroutines-core/common/src/flow/operators/Transform.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmMultifileClass diff --git a/kotlinx-coroutines-core/common/src/flow/operators/Zip.kt b/kotlinx-coroutines-core/common/src/flow/operators/Zip.kt index ebc1dcd9d8..03bddf8330 100644 --- a/kotlinx-coroutines-core/common/src/flow/operators/Zip.kt +++ b/kotlinx-coroutines-core/common/src/flow/operators/Zip.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmMultifileClass diff --git a/kotlinx-coroutines-core/common/src/flow/terminal/Collect.kt b/kotlinx-coroutines-core/common/src/flow/terminal/Collect.kt index de7d260d69..52d060f255 100644 --- a/kotlinx-coroutines-core/common/src/flow/terminal/Collect.kt +++ b/kotlinx-coroutines-core/common/src/flow/terminal/Collect.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmMultifileClass diff --git a/kotlinx-coroutines-core/common/src/flow/terminal/Collection.kt b/kotlinx-coroutines-core/common/src/flow/terminal/Collection.kt index e07be61688..6b05ca1840 100644 --- a/kotlinx-coroutines-core/common/src/flow/terminal/Collection.kt +++ b/kotlinx-coroutines-core/common/src/flow/terminal/Collection.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmMultifileClass diff --git a/kotlinx-coroutines-core/common/src/flow/terminal/Count.kt b/kotlinx-coroutines-core/common/src/flow/terminal/Count.kt index d57dfdefc2..63cf52434b 100644 --- a/kotlinx-coroutines-core/common/src/flow/terminal/Count.kt +++ b/kotlinx-coroutines-core/common/src/flow/terminal/Count.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmMultifileClass diff --git a/kotlinx-coroutines-core/common/src/flow/terminal/Reduce.kt b/kotlinx-coroutines-core/common/src/flow/terminal/Reduce.kt index eb3ce288a2..ccf8241f41 100644 --- a/kotlinx-coroutines-core/common/src/flow/terminal/Reduce.kt +++ b/kotlinx-coroutines-core/common/src/flow/terminal/Reduce.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmMultifileClass diff --git a/kotlinx-coroutines-core/common/src/internal/ArrayQueue.kt b/kotlinx-coroutines-core/common/src/internal/ArrayQueue.kt index 61a3233809..09806cd22b 100644 --- a/kotlinx-coroutines-core/common/src/internal/ArrayQueue.kt +++ b/kotlinx-coroutines-core/common/src/internal/ArrayQueue.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal diff --git a/kotlinx-coroutines-core/common/src/internal/Atomic.kt b/kotlinx-coroutines-core/common/src/internal/Atomic.kt index 8a1185ae13..56fd35b731 100644 --- a/kotlinx-coroutines-core/common/src/internal/Atomic.kt +++ b/kotlinx-coroutines-core/common/src/internal/Atomic.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal @@ -7,6 +7,7 @@ package kotlinx.coroutines.internal import kotlinx.atomicfu.atomic import kotlinx.coroutines.* import kotlin.jvm.* +import kotlin.native.concurrent.* /** * The most abstract operation that can be in process. Other threads observing an instance of this diff --git a/kotlinx-coroutines-core/common/src/internal/Concurrent.common.kt b/kotlinx-coroutines-core/common/src/internal/Concurrent.common.kt index 6b096f0449..1836a5284d 100644 --- a/kotlinx-coroutines-core/common/src/internal/Concurrent.common.kt +++ b/kotlinx-coroutines-core/common/src/internal/Concurrent.common.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal @@ -22,7 +22,3 @@ internal expect class ReentrantLock() { internal expect inline fun ReentrantLock.withLock(action: () -> T): T internal expect fun identitySet(expectedSize: Int): MutableSet - -@ExperimentalMultiplatform -@OptionalExpectation -internal expect annotation class SharedImmutable() diff --git a/kotlinx-coroutines-core/common/src/internal/DispatchedContinuation.kt b/kotlinx-coroutines-core/common/src/internal/DispatchedContinuation.kt index f04dde1cbc..acb6c48513 100644 --- a/kotlinx-coroutines-core/common/src/internal/DispatchedContinuation.kt +++ b/kotlinx-coroutines-core/common/src/internal/DispatchedContinuation.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines @@ -8,6 +8,7 @@ import kotlinx.atomicfu.* import kotlinx.coroutines.internal.* import kotlin.coroutines.* import kotlin.jvm.* +import kotlin.native.concurrent.* @SharedImmutable private val UNDEFINED = Symbol("UNDEFINED") diff --git a/kotlinx-coroutines-core/common/src/internal/DispatchedTask.kt b/kotlinx-coroutines-core/common/src/internal/DispatchedTask.kt index 98b45d56a3..9588d22b17 100644 --- a/kotlinx-coroutines-core/common/src/internal/DispatchedTask.kt +++ b/kotlinx-coroutines-core/common/src/internal/DispatchedTask.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/common/src/internal/InlineList.kt b/kotlinx-coroutines-core/common/src/internal/InlineList.kt index 062a9100f9..bac8610c1a 100644 --- a/kotlinx-coroutines-core/common/src/internal/InlineList.kt +++ b/kotlinx-coroutines-core/common/src/internal/InlineList.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("UNCHECKED_CAST") diff --git a/kotlinx-coroutines-core/common/src/internal/LockFreeLinkedList.common.kt b/kotlinx-coroutines-core/common/src/internal/LockFreeLinkedList.common.kt index 39dc1d2884..216ce7b56b 100644 --- a/kotlinx-coroutines-core/common/src/internal/LockFreeLinkedList.common.kt +++ b/kotlinx-coroutines-core/common/src/internal/LockFreeLinkedList.common.kt @@ -1,10 +1,11 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal import kotlin.jvm.* +import kotlin.native.concurrent.* /** @suppress **This is unstable API and it is subject to change.** */ public expect open class LockFreeLinkedListNode() { @@ -84,4 +85,4 @@ public expect class PrepareOp: OpDescriptor { @JvmField @SharedImmutable -internal val REMOVE_PREPARED: Any = Symbol("REMOVE_PREPARED") \ No newline at end of file +internal val REMOVE_PREPARED: Any = Symbol("REMOVE_PREPARED") diff --git a/kotlinx-coroutines-core/common/src/internal/LockFreeTaskQueue.kt b/kotlinx-coroutines-core/common/src/internal/LockFreeTaskQueue.kt index c764f51792..dfee8e9f2b 100644 --- a/kotlinx-coroutines-core/common/src/internal/LockFreeTaskQueue.kt +++ b/kotlinx-coroutines-core/common/src/internal/LockFreeTaskQueue.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal @@ -304,4 +304,4 @@ internal class LockFreeTaskQueueCore( // FROZEN | CLOSED fun Long.addFailReason(): Int = if (this and CLOSED_MASK != 0L) ADD_CLOSED else ADD_FROZEN } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/common/src/internal/MainDispatcherFactory.kt b/kotlinx-coroutines-core/common/src/internal/MainDispatcherFactory.kt index d5eb42de3e..93142df2cd 100644 --- a/kotlinx-coroutines-core/common/src/internal/MainDispatcherFactory.kt +++ b/kotlinx-coroutines-core/common/src/internal/MainDispatcherFactory.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal diff --git a/kotlinx-coroutines-core/common/src/internal/ProbesSupport.common.kt b/kotlinx-coroutines-core/common/src/internal/ProbesSupport.common.kt index 1124ff313b..763c1ca364 100644 --- a/kotlinx-coroutines-core/common/src/internal/ProbesSupport.common.kt +++ b/kotlinx-coroutines-core/common/src/internal/ProbesSupport.common.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal diff --git a/kotlinx-coroutines-core/common/src/internal/Scopes.kt b/kotlinx-coroutines-core/common/src/internal/Scopes.kt index 6adab128e3..9bb2ce3d29 100644 --- a/kotlinx-coroutines-core/common/src/internal/Scopes.kt +++ b/kotlinx-coroutines-core/common/src/internal/Scopes.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal diff --git a/kotlinx-coroutines-core/common/src/internal/SegmentQueue.kt b/kotlinx-coroutines-core/common/src/internal/SegmentQueue.kt index 370fcfc502..0091d13671 100644 --- a/kotlinx-coroutines-core/common/src/internal/SegmentQueue.kt +++ b/kotlinx-coroutines-core/common/src/internal/SegmentQueue.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal @@ -176,4 +176,4 @@ internal abstract class Segment>(val id: Long, prev: S?) { if (this.prev.compareAndSet(curPrev, prev)) return } } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/common/src/internal/StackTraceRecovery.common.kt b/kotlinx-coroutines-core/common/src/internal/StackTraceRecovery.common.kt index 06d6b694b0..15ba40f2f4 100644 --- a/kotlinx-coroutines-core/common/src/internal/StackTraceRecovery.common.kt +++ b/kotlinx-coroutines-core/common/src/internal/StackTraceRecovery.common.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal diff --git a/kotlinx-coroutines-core/common/src/internal/Symbol.kt b/kotlinx-coroutines-core/common/src/internal/Symbol.kt index 6f7f673f83..4fa8f540af 100644 --- a/kotlinx-coroutines-core/common/src/internal/Symbol.kt +++ b/kotlinx-coroutines-core/common/src/internal/Symbol.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal diff --git a/kotlinx-coroutines-core/common/src/internal/Synchronized.common.kt b/kotlinx-coroutines-core/common/src/internal/Synchronized.common.kt index 8bcc8f0419..3afc7e1802 100644 --- a/kotlinx-coroutines-core/common/src/internal/Synchronized.common.kt +++ b/kotlinx-coroutines-core/common/src/internal/Synchronized.common.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal @@ -16,4 +16,4 @@ public expect open class SynchronizedObject() // marker abstract class * @suppress **This an internal API and should not be used from general code.** */ @InternalCoroutinesApi -public expect inline fun synchronized(lock: SynchronizedObject, block: () -> T): T \ No newline at end of file +public expect inline fun synchronized(lock: SynchronizedObject, block: () -> T): T diff --git a/kotlinx-coroutines-core/common/src/internal/SystemProps.common.kt b/kotlinx-coroutines-core/common/src/internal/SystemProps.common.kt index b7e67ec0bd..4cb629e9df 100644 --- a/kotlinx-coroutines-core/common/src/internal/SystemProps.common.kt +++ b/kotlinx-coroutines-core/common/src/internal/SystemProps.common.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmName("SystemPropsKt") @@ -62,4 +62,4 @@ internal fun systemProp( * * **Note: this function should be used in JVM tests only, other platforms use the default value.** */ -internal expect fun systemProp(propertyName: String): String? \ No newline at end of file +internal expect fun systemProp(propertyName: String): String? diff --git a/kotlinx-coroutines-core/common/src/internal/ThreadContext.common.kt b/kotlinx-coroutines-core/common/src/internal/ThreadContext.common.kt index 43b5dbe652..94695e8a05 100644 --- a/kotlinx-coroutines-core/common/src/internal/ThreadContext.common.kt +++ b/kotlinx-coroutines-core/common/src/internal/ThreadContext.common.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal diff --git a/kotlinx-coroutines-core/common/src/internal/ThreadLocal.common.kt b/kotlinx-coroutines-core/common/src/internal/ThreadLocal.common.kt index ddf29888b2..0f4ec342df 100644 --- a/kotlinx-coroutines-core/common/src/internal/ThreadLocal.common.kt +++ b/kotlinx-coroutines-core/common/src/internal/ThreadLocal.common.kt @@ -1,13 +1,9 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal -@OptionalExpectation -@UseExperimental(ExperimentalMultiplatform::class) -internal expect annotation class NativeThreadLocal() - internal expect class CommonThreadLocal() { fun get(): T fun set(value: T) diff --git a/kotlinx-coroutines-core/common/src/internal/ThreadSafeHeap.kt b/kotlinx-coroutines-core/common/src/internal/ThreadSafeHeap.kt index 0ee570d154..12d6a38a81 100644 --- a/kotlinx-coroutines-core/common/src/internal/ThreadSafeHeap.kt +++ b/kotlinx-coroutines-core/common/src/internal/ThreadSafeHeap.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal diff --git a/kotlinx-coroutines-core/common/src/intrinsics/Cancellable.kt b/kotlinx-coroutines-core/common/src/intrinsics/Cancellable.kt index 2027d9bd50..0951349ebd 100644 --- a/kotlinx-coroutines-core/common/src/intrinsics/Cancellable.kt +++ b/kotlinx-coroutines-core/common/src/intrinsics/Cancellable.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.intrinsics diff --git a/kotlinx-coroutines-core/common/src/intrinsics/Undispatched.kt b/kotlinx-coroutines-core/common/src/intrinsics/Undispatched.kt index 0aa9cd7f84..525e322f08 100644 --- a/kotlinx-coroutines-core/common/src/intrinsics/Undispatched.kt +++ b/kotlinx-coroutines-core/common/src/intrinsics/Undispatched.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.intrinsics diff --git a/kotlinx-coroutines-core/common/src/selects/Select.kt b/kotlinx-coroutines-core/common/src/selects/Select.kt index 63e4dfa31b..3ba1b06215 100644 --- a/kotlinx-coroutines-core/common/src/selects/Select.kt +++ b/kotlinx-coroutines-core/common/src/selects/Select.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.selects @@ -13,6 +13,7 @@ import kotlinx.coroutines.sync.* import kotlin.coroutines.* import kotlin.coroutines.intrinsics.* import kotlin.jvm.* +import kotlin.native.concurrent.* /** * Scope for [select] invocation. @@ -34,7 +35,7 @@ public interface SelectBuilder { public operator fun SelectClause2.invoke(param: P, block: suspend (Q) -> R) /** - * Registers clause in this [select] expression with additional parameter nullable parameter of type [P] + * Registers clause in this [select] expression with additional nullable parameter of type [P] * with the `null` value for this parameter that selects value of type [Q]. */ public operator fun SelectClause2.invoke(block: suspend (Q) -> R) = invoke(null, block) @@ -213,6 +214,7 @@ internal class SeqNumber { fun next() = number.incrementAndGet() } +@SharedImmutable private val selectOpSequenceNumber = SeqNumber() @PublishedApi @@ -262,7 +264,10 @@ internal class SelectBuilderImpl( assert { isSelected } // "Must be selected first" _result.loop { result -> when { - result === UNDECIDED -> if (_result.compareAndSet(UNDECIDED, value())) return + result === UNDECIDED -> { + val update = value() + if (_result.compareAndSet(UNDECIDED, update)) return + } result === COROUTINE_SUSPENDED -> if (_result.compareAndSet(COROUTINE_SUSPENDED, RESUMED)) { block() return diff --git a/kotlinx-coroutines-core/common/src/selects/SelectUnbiased.kt b/kotlinx-coroutines-core/common/src/selects/SelectUnbiased.kt index 37521d8b49..edcf123b0a 100644 --- a/kotlinx-coroutines-core/common/src/selects/SelectUnbiased.kt +++ b/kotlinx-coroutines-core/common/src/selects/SelectUnbiased.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.selects @@ -66,4 +66,4 @@ internal class UnbiasedSelectBuilderImpl(uCont: Continuation) : override fun onTimeout(timeMillis: Long, block: suspend () -> R) { clauses += { instance.onTimeout(timeMillis, block) } } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/common/src/selects/WhileSelect.kt b/kotlinx-coroutines-core/common/src/selects/WhileSelect.kt index 1726f5f01a..33d4d7ec16 100644 --- a/kotlinx-coroutines-core/common/src/selects/WhileSelect.kt +++ b/kotlinx-coroutines-core/common/src/selects/WhileSelect.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.selects diff --git a/kotlinx-coroutines-core/common/src/sync/Mutex.kt b/kotlinx-coroutines-core/common/src/sync/Mutex.kt index ea4a510775..1b11bc96cc 100644 --- a/kotlinx-coroutines-core/common/src/sync/Mutex.kt +++ b/kotlinx-coroutines-core/common/src/sync/Mutex.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.sync @@ -11,6 +11,7 @@ import kotlinx.coroutines.intrinsics.* import kotlinx.coroutines.selects.* import kotlin.coroutines.* import kotlin.jvm.* +import kotlin.native.concurrent.* /** * Mutual exclusion for coroutines. diff --git a/kotlinx-coroutines-core/common/src/sync/Semaphore.kt b/kotlinx-coroutines-core/common/src/sync/Semaphore.kt index b6ebc501ff..aa7ed63d3d 100644 --- a/kotlinx-coroutines-core/common/src/sync/Semaphore.kt +++ b/kotlinx-coroutines-core/common/src/sync/Semaphore.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package kotlinx.coroutines.sync import kotlinx.atomicfu.* @@ -6,6 +10,7 @@ import kotlinx.coroutines.internal.* import kotlin.coroutines.* import kotlin.jvm.* import kotlin.math.* +import kotlin.native.concurrent.* /** * A counting semaphore for coroutines that logically maintains a number of available permits. @@ -208,4 +213,4 @@ private val RESUMED = Symbol("RESUMED") @SharedImmutable private val CANCELLED = Symbol("CANCELLED") @SharedImmutable -private val SEGMENT_SIZE = systemProp("kotlinx.coroutines.semaphore.segmentSize", 16) \ No newline at end of file +private val SEGMENT_SIZE = systemProp("kotlinx.coroutines.semaphore.segmentSize", 16) diff --git a/kotlinx-coroutines-core/common/test/AbstractCoroutineTest.kt b/kotlinx-coroutines-core/common/test/AbstractCoroutineTest.kt index ffde0f9635..ce20837e25 100644 --- a/kotlinx-coroutines-core/common/test/AbstractCoroutineTest.kt +++ b/kotlinx-coroutines-core/common/test/AbstractCoroutineTest.kt @@ -19,7 +19,7 @@ class AbstractCoroutineTest : TestBase() { } override fun onCancelling(cause: Throwable?) { - assertEquals(null, cause) + assertNull(cause) expect(5) } @@ -34,12 +34,12 @@ class AbstractCoroutineTest : TestBase() { } coroutine.invokeOnCompletion(onCancelling = true) { - assertEquals(null, it) + assertNull(it) expect(7) } coroutine.invokeOnCompletion { - assertEquals(null, it) + assertNull(it) expect(8) } expect(2) diff --git a/kotlinx-coroutines-core/common/test/CompletableDeferredTest.kt b/kotlinx-coroutines-core/common/test/CompletableDeferredTest.kt index f751660756..1f3978d301 100644 --- a/kotlinx-coroutines-core/common/test/CompletableDeferredTest.kt +++ b/kotlinx-coroutines-core/common/test/CompletableDeferredTest.kt @@ -50,7 +50,7 @@ class CompletableDeferredTest : TestBase() { assertEquals(false, c.isCancelled) assertEquals(true, c.isCompleted) assertTrue(c.getCancellationException() is JobCancellationException) - assertEquals(null, c.getCompletionExceptionOrNull()) + assertNull(c.getCompletionExceptionOrNull()) } private fun checkCancel(c: CompletableDeferred) { diff --git a/kotlinx-coroutines-core/common/test/TestBase.common.kt b/kotlinx-coroutines-core/common/test/TestBase.common.kt index 0fdce91fb4..a6119ee8a6 100644 --- a/kotlinx-coroutines-core/common/test/TestBase.common.kt +++ b/kotlinx-coroutines-core/common/test/TestBase.common.kt @@ -50,7 +50,7 @@ public suspend inline fun assertFailsWith(flow: Flow<*>) flow.collect() fail("Should be unreached") } catch (e: Throwable) { - assertTrue(e is T) + assertTrue(e is T, "Expected exception ${T::class}, but had $e instead") } } diff --git a/kotlinx-coroutines-core/common/test/WithTimeoutOrNullTest.kt b/kotlinx-coroutines-core/common/test/WithTimeoutOrNullTest.kt index 5d41efc07c..3faf900cb9 100644 --- a/kotlinx-coroutines-core/common/test/WithTimeoutOrNullTest.kt +++ b/kotlinx-coroutines-core/common/test/WithTimeoutOrNullTest.kt @@ -77,7 +77,7 @@ class WithTimeoutOrNullTest : TestBase() { yield() } } - assertEquals(null, result) + assertNull(result) finish(2) } @@ -135,11 +135,11 @@ class WithTimeoutOrNullTest : TestBase() { yield() } } - assertEquals(null, inner) + assertNull(inner) counter++ } } - assertEquals(null, result) + assertNull(result) check(counter in 1..2) {"Executed: $counter times"} } @@ -167,7 +167,7 @@ class WithTimeoutOrNullTest : TestBase() { expectUnreached() "OK" } - assertEquals(null, result) + assertNull(result) finish(3) } @@ -183,7 +183,7 @@ class WithTimeoutOrNullTest : TestBase() { } "OK" } - assertEquals(null, result) + assertNull(result) finish(4) } diff --git a/kotlinx-coroutines-core/common/test/channels/ArrayChannelTest.kt b/kotlinx-coroutines-core/common/test/channels/ArrayChannelTest.kt index ceef21edcb..a57b519f61 100644 --- a/kotlinx-coroutines-core/common/test/channels/ArrayChannelTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/ArrayChannelTest.kt @@ -48,7 +48,7 @@ class ArrayChannelTest : TestBase() { assertEquals(42, q.receiveOrNull()) expect(6) check(!q.isEmpty && q.isClosedForSend && q.isClosedForReceive) - assertEquals(null, q.receiveOrNull()) + assertNull(q.receiveOrNull()) expect(7) } expect(2) @@ -94,13 +94,13 @@ class ArrayChannelTest : TestBase() { expect(3) assertEquals(1, q.poll()) expect(4) - assertEquals(null, q.poll()) + assertNull(q.poll()) expect(5) assertEquals(2, q.receive()) // suspends expect(9) assertEquals(3, q.poll()) expect(10) - assertEquals(null, q.poll()) + assertNull(q.poll()) expect(11) } expect(2) diff --git a/kotlinx-coroutines-core/common/test/channels/ChannelsTest.kt b/kotlinx-coroutines-core/common/test/channels/ChannelsTest.kt index 42cc85558b..ba786d53cc 100644 --- a/kotlinx-coroutines-core/common/test/channels/ChannelsTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/ChannelsTest.kt @@ -258,8 +258,8 @@ class ChannelsTest: TestBase() { testList.indices.forEach { i -> assertEquals(testList[i], testList.asReceiveChannel().elementAtOrNull(i)) } - assertEquals(null, testList.asReceiveChannel().elementAtOrNull(-1)) - assertEquals(null, testList.asReceiveChannel().elementAtOrNull(testList.size)) + assertNull(testList.asReceiveChannel().elementAtOrNull(-1)) + assertNull(testList.asReceiveChannel().elementAtOrNull(testList.size)) } @Test @@ -310,14 +310,14 @@ class ChannelsTest: TestBase() { @Test fun testLastOrNull() = runTest { assertEquals(testList.lastOrNull(), testList.asReceiveChannel().lastOrNull()) - assertEquals(null, emptyList().asReceiveChannel().lastOrNull()) + assertNull(emptyList().asReceiveChannel().lastOrNull()) } @Test fun testSingleOrNull() = runTest { assertEquals(1, listOf(1).asReceiveChannel().singleOrNull()) - assertEquals(null, listOf(1, 2).asReceiveChannel().singleOrNull()) - assertEquals(null, emptyList().asReceiveChannel().singleOrNull()) + assertNull(listOf(1, 2).asReceiveChannel().singleOrNull()) + assertNull(emptyList().asReceiveChannel().singleOrNull()) repeat(testList.size + 1) { i -> assertEquals(testList.singleOrNull { it == i }, testList.asReceiveChannel().singleOrNull { it == i }) diff --git a/kotlinx-coroutines-core/common/test/channels/ProduceTest.kt b/kotlinx-coroutines-core/common/test/channels/ProduceTest.kt index bf85c74f64..885f1d6c8f 100644 --- a/kotlinx-coroutines-core/common/test/channels/ProduceTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/ProduceTest.kt @@ -5,6 +5,7 @@ package kotlinx.coroutines.channels import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* import kotlin.coroutines.* import kotlin.test.* @@ -143,9 +144,20 @@ class ProduceTest : TestBase() { @Test fun testAwaitIllegalState() = runTest { - val channel = produce { } - @Suppress("RemoveExplicitTypeArguments") // KT-31525 + val channel = produce { } assertFailsWith { (channel as ProducerScope<*>).awaitClose() } + callbackFlow { + expect(1) + launch { + expect(2) + assertFailsWith { + awaitClose { expectUnreached() } + expectUnreached() + } + } + close() + }.collect() + finish(3) } private suspend fun cancelOnCompletion(coroutineContext: CoroutineContext) = CoroutineScope(coroutineContext).apply { diff --git a/kotlinx-coroutines-core/common/test/channels/RendezvousChannelTest.kt b/kotlinx-coroutines-core/common/test/channels/RendezvousChannelTest.kt index 54d6938481..d036af9395 100644 --- a/kotlinx-coroutines-core/common/test/channels/RendezvousChannelTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/RendezvousChannelTest.kt @@ -44,7 +44,7 @@ class RendezvousChannelTest : TestBase() { expect(3) assertEquals(42, q.receiveOrNull()) expect(4) - assertEquals(null, q.receiveOrNull()) + assertNull(q.receiveOrNull()) expect(6) } expect(2) @@ -86,11 +86,11 @@ class RendezvousChannelTest : TestBase() { expect(1) launch { expect(3) - assertEquals(null, q.poll()) + assertNull(q.poll()) expect(4) assertEquals(2, q.receive()) expect(7) - assertEquals(null, q.poll()) + assertNull(q.poll()) yield() expect(9) assertEquals(3, q.poll()) diff --git a/kotlinx-coroutines-core/common/test/channels/TestChannelKind.kt b/kotlinx-coroutines-core/common/test/channels/TestChannelKind.kt index 27c58165c1..69d8fd03e3 100644 --- a/kotlinx-coroutines-core/common/test/channels/TestChannelKind.kt +++ b/kotlinx-coroutines-core/common/test/channels/TestChannelKind.kt @@ -7,45 +7,26 @@ package kotlinx.coroutines.channels import kotlinx.coroutines.* import kotlinx.coroutines.selects.* -enum class TestChannelKind { - RENDEZVOUS { - override fun create(): Channel = Channel(Channel.RENDEZVOUS) - override fun toString(): String = "RendezvousChannel" - }, - ARRAY_1 { - override fun create(): Channel = Channel(1) - override fun toString(): String = "ArrayChannel(1)" - }, - ARRAY_10 { - override fun create(): Channel = Channel(10) - override fun toString(): String = "ArrayChannel(10)" - }, - LINKED_LIST { - override fun create(): Channel = Channel(Channel.UNLIMITED) - override fun toString(): String = "LinkedListChannel" - }, - CONFLATED { - override fun create(): Channel = Channel(Channel.CONFLATED) - override fun toString(): String = "ConflatedChannel" - override val isConflated: Boolean get() = true - }, - ARRAY_BROADCAST_1 { - override fun create(): Channel = ChannelViaBroadcast(BroadcastChannel(1)) - override fun toString(): String = "ArrayBroadcastChannel(1)" - }, - ARRAY_BROADCAST_10 { - override fun create(): Channel = ChannelViaBroadcast(BroadcastChannel(10)) - override fun toString(): String = "ArrayBroadcastChannel(10)" - }, - CONFLATED_BROADCAST { - override fun create(): Channel = ChannelViaBroadcast(ConflatedBroadcastChannel()) - override fun toString(): String = "ConflatedBroadcastChannel" - override val isConflated: Boolean get() = true - } +enum class TestChannelKind(val capacity: Int, + private val description: String, + private val viaBroadcast: Boolean = false +) { + RENDEZVOUS(0, "RendezvousChannel"), + ARRAY_1(1, "ArrayChannel(1)"), + ARRAY_2(2, "ArrayChannel(2)"), + ARRAY_10(10, "ArrayChannel(10)"), + LINKED_LIST(Channel.UNLIMITED, "LinkedListChannel"), + CONFLATED(Channel.CONFLATED, "ConflatedChannel"), + ARRAY_1_BROADCAST(1, "ArrayBroadcastChannel(1)", viaBroadcast = true), + ARRAY_10_BROADCAST(10, "ArrayBroadcastChannel(10)", viaBroadcast = true), + CONFLATED_BROADCAST(Channel.CONFLATED, "ConflatedBroadcastChannel", viaBroadcast = true) ; - abstract fun create(): Channel - open val isConflated: Boolean get() = false + fun create(): Channel = if (viaBroadcast) ChannelViaBroadcast(BroadcastChannel(capacity)) + else Channel(capacity) + + val isConflated get() = capacity == Channel.CONFLATED + override fun toString(): String = description } private class ChannelViaBroadcast( diff --git a/kotlinx-coroutines-core/common/test/flow/FlowInvariantsTest.kt b/kotlinx-coroutines-core/common/test/flow/FlowInvariantsTest.kt index 5dbc6e24ef..ce93f1fdb2 100644 --- a/kotlinx-coroutines-core/common/test/flow/FlowInvariantsTest.kt +++ b/kotlinx-coroutines-core/common/test/flow/FlowInvariantsTest.kt @@ -59,25 +59,6 @@ class FlowInvariantsTest : TestBase() { } } - @Test - fun testCachedInvariantCheckResult() = runParametrizedTest { flow -> - flow { - emit(1) - try { - withContext(NamedDispatchers("foo")) { - emit(1) - } - fail() - } catch (e: IllegalStateException) { - expect(2) - } - emit(3) - }.collect { - expect(it) - } - finish(4) - } - @Test fun testWithNameContractViolated() = runParametrizedTest(IllegalStateException::class) { flow -> flow { @@ -146,9 +127,9 @@ class FlowInvariantsTest : TestBase() { } } - val flow = flowOf(1) - assertFailsWith { flow.merge(flow).toList() } - assertFailsWith { flow.trickyMerge(flow).toList() } + val flowInstance = flowOf(1) + assertFailsWith { flowInstance.merge(flowInstance).toList() } + assertFailsWith { flowInstance.trickyMerge(flowInstance).toList() } } @Test @@ -237,7 +218,7 @@ class FlowInvariantsTest : TestBase() { emptyContextTest { transform { expect(it) - kotlinx.coroutines.withContext(Dispatchers.Unconfined) { + withContext(Dispatchers.Unconfined) { emit(it + 1) } } diff --git a/kotlinx-coroutines-core/common/test/flow/SafeFlowTest.kt b/kotlinx-coroutines-core/common/test/flow/SafeFlowTest.kt new file mode 100644 index 0000000000..eaba11b950 --- /dev/null +++ b/kotlinx-coroutines-core/common/test/flow/SafeFlowTest.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2016-2020 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 kotlin.test.* + +class SafeFlowTest : TestBase() { + + @Test + fun testEmissionsFromDifferentStateMachine() = runTest { + val result = flow { + emit1(1) + emit2(2) + }.onEach { yield() }.toList() + assertEquals(listOf(1, 2), result) + finish(3) + } + + private suspend fun FlowCollector.emit1(expect: Int) { + emit(expect) + expect(expect) + } + + private suspend fun FlowCollector.emit2(expect: Int) { + emit(expect) + expect(expect) + } +} diff --git a/kotlinx-coroutines-core/common/test/flow/channels/ChannelBuildersFlowTest.kt b/kotlinx-coroutines-core/common/test/flow/channels/ChannelBuildersFlowTest.kt index de5c220f31..f93d039933 100644 --- a/kotlinx-coroutines-core/common/test/flow/channels/ChannelBuildersFlowTest.kt +++ b/kotlinx-coroutines-core/common/test/flow/channels/ChannelBuildersFlowTest.kt @@ -21,6 +21,18 @@ class ChannelBuildersFlowTest : TestBase() { assertFailsWith { flow.collect() } } + @Test + fun testChannelReceiveAsFlow() = runTest { + val channel = produce { + repeat(10) { + send(it + 1) + } + } + val flow = channel.receiveAsFlow() + assertEquals(55, flow.sum()) + assertEquals(emptyList(), flow.toList()) + } + @Test fun testConsumeAsFlowCancellation() = runTest { val channel = produce(NonCancellable) { // otherwise failure will cancel scope as well @@ -36,6 +48,20 @@ class ChannelBuildersFlowTest : TestBase() { assertFailsWith { flow.collect() } } + @Test + fun testReceiveAsFlowCancellation() = runTest { + val channel = produce(NonCancellable) { // otherwise failure will cancel scope as well + repeat(10) { + send(it + 1) + } + throw TestException() + } + val flow = channel.receiveAsFlow() + assertEquals(15, flow.take(5).sum()) // sum of first 5 + assertEquals(40, flow.take(5).sum()) // sum the rest 5 + assertFailsWith { flow.sum() } // exception in the rest + } + @Test fun testConsumeAsFlowException() = runTest { val channel = produce(NonCancellable) { // otherwise failure will cancel scope as well @@ -49,6 +75,19 @@ class ChannelBuildersFlowTest : TestBase() { assertFailsWith { flow.collect() } } + @Test + fun testReceiveAsFlowException() = runTest { + val channel = produce(NonCancellable) { // otherwise failure will cancel scope as well + repeat(10) { + send(it + 1) + } + throw TestException() + } + val flow = channel.receiveAsFlow() + assertFailsWith { flow.sum() } + assertFailsWith { flow.collect() } // repeated collection -- same exception + } + @Test fun testConsumeAsFlowProduceFusing() = runTest { val channel = produce { send("OK") } @@ -58,6 +97,15 @@ class ChannelBuildersFlowTest : TestBase() { channel.cancel() } + @Test + fun testReceiveAsFlowProduceFusing() = runTest { + val channel = produce { send("OK") } + val flow = channel.receiveAsFlow() + assertSame(channel, flow.produceIn(this)) + assertSame(channel, flow.produceIn(this)) // can use produce multiple times + channel.cancel() + } + @Test fun testConsumeAsFlowProduceBuffered() = runTest { expect(1) @@ -217,4 +265,15 @@ class ChannelBuildersFlowTest : TestBase() { fail() } } + + @Test + fun testProduceInAtomicity() = runTest { + val flow = flowOf(1).onCompletion { expect(2) } + val scope = CoroutineScope(wrapperDispatcher()) + flow.produceIn(scope) + expect(1) + scope.cancel() + scope.coroutineContext[Job]?.join() + finish(3) + } } diff --git a/kotlinx-coroutines-core/common/test/flow/channels/ChannelFlowTest.kt b/kotlinx-coroutines-core/common/test/flow/channels/ChannelFlowTest.kt index 32c2afc65b..b115150a0b 100644 --- a/kotlinx-coroutines-core/common/test/flow/channels/ChannelFlowTest.kt +++ b/kotlinx-coroutines-core/common/test/flow/channels/ChannelFlowTest.kt @@ -160,4 +160,38 @@ class ChannelFlowTest : TestBase() { finish(6) } + + @Test + fun testClosedPrematurely() = runTest(unhandled = listOf({ e -> e is ClosedSendChannelException })) { + val outerScope = this + val flow = channelFlow { + // ~ callback-based API, no children + outerScope.launch(Job()) { + expect(2) + send(1) + expectUnreached() + } + expect(1) + } + assertEquals(emptyList(), flow.toList()) + finish(3) + } + + @Test + fun testNotClosedPrematurely() = runTest { + val outerScope = this + val flow = channelFlow { + // ~ callback-based API + outerScope.launch(Job()) { + expect(2) + send(1) + close() + } + expect(1) + awaitClose() + } + + assertEquals(listOf(1), flow.toList()) + finish(3) + } } diff --git a/kotlinx-coroutines-core/common/test/flow/channels/FlowCallbackTest.kt b/kotlinx-coroutines-core/common/test/flow/channels/FlowCallbackTest.kt index a6b5340555..cfbf242c35 100644 --- a/kotlinx-coroutines-core/common/test/flow/channels/FlowCallbackTest.kt +++ b/kotlinx-coroutines-core/common/test/flow/channels/FlowCallbackTest.kt @@ -12,25 +12,35 @@ import kotlin.test.* class FlowCallbackTest : TestBase() { @Test - fun testClosedPrematurely() = runTest(unhandled = listOf({ e -> e is ClosedSendChannelException })) { + fun testClosedPrematurely() = runTest { val outerScope = this - val flow = channelFlow { + val flow = callbackFlow { // ~ callback-based API outerScope.launch(Job()) { expect(2) - send(1) - expectUnreached() + try { + send(1) + expectUnreached() + } catch (e: IllegalStateException) { + expect(3) + assertTrue(e.message!!.contains("awaitClose")) + } } expect(1) } - assertEquals(emptyList(), flow.toList()) - finish(3) + try { + flow.collect() + } catch (e: IllegalStateException) { + expect(4) + assertTrue(e.message!!.contains("awaitClose")) + } + finish(5) } @Test fun testNotClosedPrematurely() = runTest { val outerScope = this - val flow = channelFlow { + val flow = callbackFlow { // ~ callback-based API outerScope.launch(Job()) { expect(2) diff --git a/kotlinx-coroutines-core/common/test/flow/operators/FlatMapMergeTest.kt b/kotlinx-coroutines-core/common/test/flow/operators/FlatMapMergeTest.kt index 511a003a8e..684923c861 100644 --- a/kotlinx-coroutines-core/common/test/flow/operators/FlatMapMergeTest.kt +++ b/kotlinx-coroutines-core/common/test/flow/operators/FlatMapMergeTest.kt @@ -37,6 +37,35 @@ class FlatMapMergeTest : FlatMapMergeBaseTest() { finish(3) } + @Test + fun testAtomicStart() = runTest { + try { + coroutineScope { + val job = coroutineContext[Job]!! + val flow = flow { + expect(3) + emit(1) + } + .onCompletion { expect(5) } + .flatMapMerge { + expect(4) + flowOf(it).onCompletion { expectUnreached() } } + .onCompletion { expect(6) } + + launch { + expect(1) + flow.collect() + } + launch { + expect(2) + job.cancel() + } + } + } catch (e: CancellationException) { + finish(7) + } + } + @Test fun testCancellationExceptionDownstream() = runTest { val flow = flow { diff --git a/kotlinx-coroutines-core/common/test/flow/operators/FlowContextOptimizationsTest.kt b/kotlinx-coroutines-core/common/test/flow/operators/FlowContextOptimizationsTest.kt index bf5297408f..7194a70d15 100644 --- a/kotlinx-coroutines-core/common/test/flow/operators/FlowContextOptimizationsTest.kt +++ b/kotlinx-coroutines-core/common/test/flow/operators/FlowContextOptimizationsTest.kt @@ -80,7 +80,7 @@ class FlowContextOptimizationsTest : TestBase() { } .flowOn(CoroutineName("Name")) .collect { value -> - assertEquals(null, currentContext[CoroutineName]?.name) + assertNull(currentContext[CoroutineName]?.name) if (value == 1) expect(2) else expect(4) } @@ -102,8 +102,8 @@ class FlowContextOptimizationsTest : TestBase() { .flowOn(CoroutineName("Name2")) .flowOn(CoroutineName("Name3") + CustomContextElement("OK")) // but this is not lost .collect { value -> - assertEquals(null, currentContext[CoroutineName]?.name) - assertEquals(null, currentContext[CustomContextElement]?.str) + assertNull(currentContext[CoroutineName]?.name) + assertNull(currentContext[CustomContextElement]?.str) if (value == 1) expect(2) else expect(4) } diff --git a/kotlinx-coroutines-core/common/test/flow/operators/FlowOnTest.kt b/kotlinx-coroutines-core/common/test/flow/operators/FlowOnTest.kt index 34c0476ef6..f8350ff584 100644 --- a/kotlinx-coroutines-core/common/test/flow/operators/FlowOnTest.kt +++ b/kotlinx-coroutines-core/common/test/flow/operators/FlowOnTest.kt @@ -276,6 +276,33 @@ class FlowOnTest : TestBase() { assertEquals(listOf(1, 2), result) } + @Test + fun testAtomicStart() = runTest { + try { + coroutineScope { + val job = coroutineContext[Job]!! + val flow = flow { + expect(3) + emit(1) + } + .onCompletion { expect(4) } + .flowOn(wrapperDispatcher()) + .onCompletion { expect(5) } + + launch { + expect(1) + flow.collect() + } + launch { + expect(2) + job.cancel() + } + } + } catch (e: CancellationException) { + finish(6) + } + } + @Test fun testException() = runTest { val flow = flow { diff --git a/kotlinx-coroutines-core/js/src/CompletionHandler.kt b/kotlinx-coroutines-core/js/src/CompletionHandler.kt index 19a9be884a..e81e43511a 100644 --- a/kotlinx-coroutines-core/js/src/CompletionHandler.kt +++ b/kotlinx-coroutines-core/js/src/CompletionHandler.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/js/src/CoroutineContext.kt b/kotlinx-coroutines-core/js/src/CoroutineContext.kt index 3390fc1b8c..c0b0c511f9 100644 --- a/kotlinx-coroutines-core/js/src/CoroutineContext.kt +++ b/kotlinx-coroutines-core/js/src/CoroutineContext.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines @@ -50,4 +50,4 @@ public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): // No debugging facilities on JS internal actual inline fun withCoroutineContext(context: CoroutineContext, countOrElement: Any?, block: () -> T): T = block() internal actual fun Continuation<*>.toDebugString(): String = toString() -internal actual val CoroutineContext.coroutineName: String? get() = null // not supported on JS \ No newline at end of file +internal actual val CoroutineContext.coroutineName: String? get() = null // not supported on JS diff --git a/kotlinx-coroutines-core/js/src/CoroutineExceptionHandlerImpl.kt b/kotlinx-coroutines-core/js/src/CoroutineExceptionHandlerImpl.kt index 4bc378d376..524d4b5543 100644 --- a/kotlinx-coroutines-core/js/src/CoroutineExceptionHandlerImpl.kt +++ b/kotlinx-coroutines-core/js/src/CoroutineExceptionHandlerImpl.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/js/src/Debug.kt b/kotlinx-coroutines-core/js/src/Debug.kt index 57a94d4364..7cd9bedd2f 100644 --- a/kotlinx-coroutines-core/js/src/Debug.kt +++ b/kotlinx-coroutines-core/js/src/Debug.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines @@ -21,4 +21,4 @@ internal actual val Any.hexAddress: String internal actual val Any.classSimpleName: String get() = this::class.simpleName ?: "Unknown" -internal actual inline fun assert(value: () -> Boolean) {} \ No newline at end of file +internal actual inline fun assert(value: () -> Boolean) {} diff --git a/kotlinx-coroutines-core/js/src/Dispatchers.kt b/kotlinx-coroutines-core/js/src/Dispatchers.kt index 3667f2e1b5..995801ea0d 100644 --- a/kotlinx-coroutines-core/js/src/Dispatchers.kt +++ b/kotlinx-coroutines-core/js/src/Dispatchers.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/js/src/EventLoop.kt b/kotlinx-coroutines-core/js/src/EventLoop.kt index 19d75c0925..0039678a93 100644 --- a/kotlinx-coroutines-core/js/src/EventLoop.kt +++ b/kotlinx-coroutines-core/js/src/EventLoop.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/js/src/Exceptions.kt b/kotlinx-coroutines-core/js/src/Exceptions.kt index 2a8968849a..39b3344ac1 100644 --- a/kotlinx-coroutines-core/js/src/Exceptions.kt +++ b/kotlinx-coroutines-core/js/src/Exceptions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines @@ -39,4 +39,4 @@ internal actual class JobCancellationException public actual constructor( internal actual inline fun Throwable.addSuppressedThrowable(other: Throwable) { /* empty */ } // For use in tests -internal actual val RECOVER_STACK_TRACES: Boolean = false \ No newline at end of file +internal actual val RECOVER_STACK_TRACES: Boolean = false diff --git a/kotlinx-coroutines-core/js/src/JSDispatcher.kt b/kotlinx-coroutines-core/js/src/JSDispatcher.kt index dc98332fe3..a0dfcba2b7 100644 --- a/kotlinx-coroutines-core/js/src/JSDispatcher.kt +++ b/kotlinx-coroutines-core/js/src/JSDispatcher.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/js/src/Promise.kt b/kotlinx-coroutines-core/js/src/Promise.kt index 44615d2806..6c3de76426 100644 --- a/kotlinx-coroutines-core/js/src/Promise.kt +++ b/kotlinx-coroutines-core/js/src/Promise.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/js/src/Runnable.kt b/kotlinx-coroutines-core/js/src/Runnable.kt index b240a943d0..19710f971b 100644 --- a/kotlinx-coroutines-core/js/src/Runnable.kt +++ b/kotlinx-coroutines-core/js/src/Runnable.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/js/src/SchedulerTask.kt b/kotlinx-coroutines-core/js/src/SchedulerTask.kt index dce72f5b71..29a92cfb24 100644 --- a/kotlinx-coroutines-core/js/src/SchedulerTask.kt +++ b/kotlinx-coroutines-core/js/src/SchedulerTask.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/js/src/Window.kt b/kotlinx-coroutines-core/js/src/Window.kt index 3c1cdb42a6..8284daef8d 100644 --- a/kotlinx-coroutines-core/js/src/Window.kt +++ b/kotlinx-coroutines-core/js/src/Window.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines @@ -60,4 +60,4 @@ private class WindowAnimationQueue(private val window: Window) { with(element) { dispatcher.resumeUndispatched(timestamp) } } } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/js/src/flow/internal/FlowExceptions.kt b/kotlinx-coroutines-core/js/src/flow/internal/FlowExceptions.kt index 2df8a5b73e..f818bfbb37 100644 --- a/kotlinx-coroutines-core/js/src/flow/internal/FlowExceptions.kt +++ b/kotlinx-coroutines-core/js/src/flow/internal/FlowExceptions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.flow.internal diff --git a/kotlinx-coroutines-core/js/src/flow/internal/SafeCollector.kt b/kotlinx-coroutines-core/js/src/flow/internal/SafeCollector.kt new file mode 100644 index 0000000000..78f1bdb2a9 --- /dev/null +++ b/kotlinx-coroutines-core/js/src/flow/internal/SafeCollector.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.flow.internal + +import kotlinx.coroutines.flow.* +import kotlin.coroutines.* + +internal actual class SafeCollector actual constructor( + internal actual val collector: FlowCollector, + internal actual val collectContext: CoroutineContext +) : FlowCollector { + + // Note, it is non-capturing lambda, so no extra allocation during init of SafeCollector + internal actual val collectContextSize = collectContext.fold(0) { count, _ -> count + 1 } + private var lastEmissionContext: CoroutineContext? = null + + override suspend fun emit(value: T) { + val currentContext = coroutineContext + if (lastEmissionContext !== currentContext) { + checkContext(currentContext) + lastEmissionContext = currentContext + } + collector.emit(value) + } + + public actual fun releaseIntercepted() { + } +} diff --git a/kotlinx-coroutines-core/js/src/internal/Concurrent.kt b/kotlinx-coroutines-core/js/src/internal/Concurrent.kt index ec8f6b1150..5555137f7f 100644 --- a/kotlinx-coroutines-core/js/src/internal/Concurrent.kt +++ b/kotlinx-coroutines-core/js/src/internal/Concurrent.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal diff --git a/kotlinx-coroutines-core/js/src/internal/CopyOnWriteList.kt b/kotlinx-coroutines-core/js/src/internal/CopyOnWriteList.kt index 8a7a55d987..8f42160b55 100644 --- a/kotlinx-coroutines-core/js/src/internal/CopyOnWriteList.kt +++ b/kotlinx-coroutines-core/js/src/internal/CopyOnWriteList.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal diff --git a/kotlinx-coroutines-core/js/src/internal/LinkedList.kt b/kotlinx-coroutines-core/js/src/internal/LinkedList.kt index 7adc7a7865..7daeef2d94 100644 --- a/kotlinx-coroutines-core/js/src/internal/LinkedList.kt +++ b/kotlinx-coroutines-core/js/src/internal/LinkedList.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("unused") diff --git a/kotlinx-coroutines-core/js/src/internal/ProbesSupport.kt b/kotlinx-coroutines-core/js/src/internal/ProbesSupport.kt index 81b6476bc2..a13a141f4d 100644 --- a/kotlinx-coroutines-core/js/src/internal/ProbesSupport.kt +++ b/kotlinx-coroutines-core/js/src/internal/ProbesSupport.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal diff --git a/kotlinx-coroutines-core/js/src/internal/StackTraceRecovery.kt b/kotlinx-coroutines-core/js/src/internal/StackTraceRecovery.kt index 75b1d7915f..234bbcadc2 100644 --- a/kotlinx-coroutines-core/js/src/internal/StackTraceRecovery.kt +++ b/kotlinx-coroutines-core/js/src/internal/StackTraceRecovery.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal @@ -22,4 +22,4 @@ internal actual interface CoroutineStackFrame { internal actual typealias StackTraceElement = Any internal actual fun Throwable.initCause(cause: Throwable) { -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/js/src/internal/Synchronized.kt b/kotlinx-coroutines-core/js/src/internal/Synchronized.kt index fbed54622c..0911dbe115 100644 --- a/kotlinx-coroutines-core/js/src/internal/Synchronized.kt +++ b/kotlinx-coroutines-core/js/src/internal/Synchronized.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal @@ -17,4 +17,4 @@ public actual typealias SynchronizedObject = Any */ @InternalCoroutinesApi public actual inline fun synchronized(lock: SynchronizedObject, block: () -> T): T = - block() \ No newline at end of file + block() diff --git a/kotlinx-coroutines-core/js/src/internal/SystemProps.kt b/kotlinx-coroutines-core/js/src/internal/SystemProps.kt index 6779d4e5d8..564630f623 100644 --- a/kotlinx-coroutines-core/js/src/internal/SystemProps.kt +++ b/kotlinx-coroutines-core/js/src/internal/SystemProps.kt @@ -1,7 +1,7 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal -internal actual fun systemProp(propertyName: String): String? = null \ No newline at end of file +internal actual fun systemProp(propertyName: String): String? = null diff --git a/kotlinx-coroutines-core/js/src/internal/ThreadContext.kt b/kotlinx-coroutines-core/js/src/internal/ThreadContext.kt index d9daf256e3..4a9513ab9e 100644 --- a/kotlinx-coroutines-core/js/src/internal/ThreadContext.kt +++ b/kotlinx-coroutines-core/js/src/internal/ThreadContext.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal diff --git a/kotlinx-coroutines-core/js/src/internal/ThreadLocal.kt b/kotlinx-coroutines-core/js/src/internal/ThreadLocal.kt index 2ee0a4fd0d..09f501a4f5 100644 --- a/kotlinx-coroutines-core/js/src/internal/ThreadLocal.kt +++ b/kotlinx-coroutines-core/js/src/internal/ThreadLocal.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal @@ -9,4 +9,4 @@ internal actual class CommonThreadLocal actual constructor() { @Suppress("UNCHECKED_CAST") actual fun get(): T = value as T actual fun set(value: T) { this.value = value } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/jvm/src/Builders.kt b/kotlinx-coroutines-core/jvm/src/Builders.kt index c6e121f957..b8a250fef6 100644 --- a/kotlinx-coroutines-core/jvm/src/Builders.kt +++ b/kotlinx-coroutines-core/jvm/src/Builders.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmMultifileClass diff --git a/kotlinx-coroutines-core/jvm/src/CommonPool.kt b/kotlinx-coroutines-core/jvm/src/CommonPool.kt index 1b5aae988f..60f30cfe14 100644 --- a/kotlinx-coroutines-core/jvm/src/CommonPool.kt +++ b/kotlinx-coroutines-core/jvm/src/CommonPool.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/jvm/src/CompletionHandler.kt b/kotlinx-coroutines-core/jvm/src/CompletionHandler.kt index d425d6d8d5..706f6c498e 100644 --- a/kotlinx-coroutines-core/jvm/src/CompletionHandler.kt +++ b/kotlinx-coroutines-core/jvm/src/CompletionHandler.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines @@ -19,4 +19,4 @@ internal actual abstract class CancelHandlerBase actual constructor() : Completi internal actual inline val CancelHandlerBase.asHandler: CompletionHandler get() = this @Suppress("NOTHING_TO_INLINE") -internal actual inline fun CompletionHandler.invokeIt(cause: Throwable?) = invoke(cause) \ No newline at end of file +internal actual inline fun CompletionHandler.invokeIt(cause: Throwable?) = invoke(cause) diff --git a/kotlinx-coroutines-core/jvm/src/CoroutineContext.kt b/kotlinx-coroutines-core/jvm/src/CoroutineContext.kt index d0375a61e1..5a69d48aac 100644 --- a/kotlinx-coroutines-core/jvm/src/CoroutineContext.kt +++ b/kotlinx-coroutines-core/jvm/src/CoroutineContext.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/jvm/src/CoroutineExceptionHandlerImpl.kt b/kotlinx-coroutines-core/jvm/src/CoroutineExceptionHandlerImpl.kt index 7eb7576fdd..af37e73c39 100644 --- a/kotlinx-coroutines-core/jvm/src/CoroutineExceptionHandlerImpl.kt +++ b/kotlinx-coroutines-core/jvm/src/CoroutineExceptionHandlerImpl.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/jvm/src/Debug.kt b/kotlinx-coroutines-core/jvm/src/Debug.kt index 5aa37931ef..aac06ade79 100644 --- a/kotlinx-coroutines-core/jvm/src/Debug.kt +++ b/kotlinx-coroutines-core/jvm/src/Debug.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ // Need InlineOnly for efficient bytecode on Android diff --git a/kotlinx-coroutines-core/jvm/src/DebugStrings.kt b/kotlinx-coroutines-core/jvm/src/DebugStrings.kt index 78ad41811a..184fb655e3 100644 --- a/kotlinx-coroutines-core/jvm/src/DebugStrings.kt +++ b/kotlinx-coroutines-core/jvm/src/DebugStrings.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/jvm/src/DefaultExecutor.kt b/kotlinx-coroutines-core/jvm/src/DefaultExecutor.kt index 19adcef2be..4e107a7b1d 100644 --- a/kotlinx-coroutines-core/jvm/src/DefaultExecutor.kt +++ b/kotlinx-coroutines-core/jvm/src/DefaultExecutor.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/jvm/src/Dispatchers.kt b/kotlinx-coroutines-core/jvm/src/Dispatchers.kt index 1ddad469cc..b9df7ab2e6 100644 --- a/kotlinx-coroutines-core/jvm/src/Dispatchers.kt +++ b/kotlinx-coroutines-core/jvm/src/Dispatchers.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("unused") diff --git a/kotlinx-coroutines-core/jvm/src/EventLoop.kt b/kotlinx-coroutines-core/jvm/src/EventLoop.kt index 598f424b61..d86f632a69 100644 --- a/kotlinx-coroutines-core/jvm/src/EventLoop.kt +++ b/kotlinx-coroutines-core/jvm/src/EventLoop.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines @@ -46,4 +46,4 @@ internal actual fun createEventLoop(): EventLoop = BlockingEventLoop(Thread.curr */ @InternalCoroutinesApi public fun processNextEventInCurrentThread(): Long = - ThreadLocalEventLoop.currentOrNull()?.processNextEvent() ?: Long.MAX_VALUE \ No newline at end of file + ThreadLocalEventLoop.currentOrNull()?.processNextEvent() ?: Long.MAX_VALUE diff --git a/kotlinx-coroutines-core/jvm/src/Exceptions.kt b/kotlinx-coroutines-core/jvm/src/Exceptions.kt index 2aa399c21c..22a02a30bb 100644 --- a/kotlinx-coroutines-core/jvm/src/Exceptions.kt +++ b/kotlinx-coroutines-core/jvm/src/Exceptions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("FunctionName") @@ -71,4 +71,4 @@ internal actual class JobCancellationException public actual constructor( @Suppress("NOTHING_TO_INLINE") internal actual inline fun Throwable.addSuppressedThrowable(other: Throwable) = - addSuppressed(other) \ No newline at end of file + addSuppressed(other) diff --git a/kotlinx-coroutines-core/jvm/src/Executors.kt b/kotlinx-coroutines-core/jvm/src/Executors.kt index 7d7f4ba7be..a4d6b46c43 100644 --- a/kotlinx-coroutines-core/jvm/src/Executors.kt +++ b/kotlinx-coroutines-core/jvm/src/Executors.kt @@ -1,11 +1,11 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines import kotlinx.coroutines.internal.* -import java.io.Closeable +import java.io.* import java.util.concurrent.* import kotlin.coroutines.* @@ -17,17 +17,23 @@ import kotlin.coroutines.* * asynchronous API which requires instance of the [Executor]. */ public abstract class ExecutorCoroutineDispatcher: CoroutineDispatcher(), Closeable { + /** @suppress */ + @ExperimentalStdlibApi + public companion object Key : AbstractCoroutineContextKey( + CoroutineDispatcher, + { it as? ExecutorCoroutineDispatcher }) + + /** + * Underlying executor of current [CoroutineDispatcher]. + */ + public abstract val executor: Executor + /** * Closes this coroutine dispatcher and shuts down its executor. * * It may throw an exception if this dispatcher is global and cannot be closed. */ public abstract override fun close() - - /** - * Underlying executor of current [CoroutineDispatcher]. - */ - public abstract val executor: Executor } /** diff --git a/kotlinx-coroutines-core/jvm/src/Future.kt b/kotlinx-coroutines-core/jvm/src/Future.kt index 19a375d49e..54825c309d 100644 --- a/kotlinx-coroutines-core/jvm/src/Future.kt +++ b/kotlinx-coroutines-core/jvm/src/Future.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmMultifileClass diff --git a/kotlinx-coroutines-core/jvm/src/Runnable.kt b/kotlinx-coroutines-core/jvm/src/Runnable.kt index 2acd55461a..14d011053d 100644 --- a/kotlinx-coroutines-core/jvm/src/Runnable.kt +++ b/kotlinx-coroutines-core/jvm/src/Runnable.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/jvm/src/SchedulerTask.kt b/kotlinx-coroutines-core/jvm/src/SchedulerTask.kt index da2c85503b..478df822f4 100644 --- a/kotlinx-coroutines-core/jvm/src/SchedulerTask.kt +++ b/kotlinx-coroutines-core/jvm/src/SchedulerTask.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/jvm/src/ThreadContextElement.kt b/kotlinx-coroutines-core/jvm/src/ThreadContextElement.kt index 4e8b6cc42e..1fd851104d 100644 --- a/kotlinx-coroutines-core/jvm/src/ThreadContextElement.kt +++ b/kotlinx-coroutines-core/jvm/src/ThreadContextElement.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/jvm/src/ThreadPoolDispatcher.kt b/kotlinx-coroutines-core/jvm/src/ThreadPoolDispatcher.kt index ce2f81c62c..7291f0c4fc 100644 --- a/kotlinx-coroutines-core/jvm/src/ThreadPoolDispatcher.kt +++ b/kotlinx-coroutines-core/jvm/src/ThreadPoolDispatcher.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/jvm/src/TimeSource.kt b/kotlinx-coroutines-core/jvm/src/TimeSource.kt index 99a0ca4586..4b6fd9915c 100644 --- a/kotlinx-coroutines-core/jvm/src/TimeSource.kt +++ b/kotlinx-coroutines-core/jvm/src/TimeSource.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ // Need InlineOnly for efficient bytecode on Android diff --git a/kotlinx-coroutines-core/jvm/src/channels/Actor.kt b/kotlinx-coroutines-core/jvm/src/channels/Actor.kt index b951505839..95fbb6fca8 100644 --- a/kotlinx-coroutines-core/jvm/src/channels/Actor.kt +++ b/kotlinx-coroutines-core/jvm/src/channels/Actor.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels diff --git a/kotlinx-coroutines-core/jvm/src/channels/Channels.kt b/kotlinx-coroutines-core/jvm/src/channels/Channels.kt index 78889e70ab..2c9499597f 100644 --- a/kotlinx-coroutines-core/jvm/src/channels/Channels.kt +++ b/kotlinx-coroutines-core/jvm/src/channels/Channels.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmMultifileClass @@ -9,8 +9,6 @@ package kotlinx.coroutines.channels import kotlinx.coroutines.* -// -------- Operations on SendChannel -------- - /** * Adds [element] into to this channel, **blocking** the caller while this channel [Channel.isFull], * or throws exception if the channel [Channel.isClosedForSend] (see [Channel.close] for details). diff --git a/kotlinx-coroutines-core/jvm/src/channels/TickerChannels.kt b/kotlinx-coroutines-core/jvm/src/channels/TickerChannels.kt index 4bbf77d604..0f9321a58d 100644 --- a/kotlinx-coroutines-core/jvm/src/channels/TickerChannels.kt +++ b/kotlinx-coroutines-core/jvm/src/channels/TickerChannels.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels diff --git a/kotlinx-coroutines-core/jvm/src/flow/internal/FlowExceptions.kt b/kotlinx-coroutines-core/jvm/src/flow/internal/FlowExceptions.kt index 3e714321f7..863c2cc4ab 100644 --- a/kotlinx-coroutines-core/jvm/src/flow/internal/FlowExceptions.kt +++ b/kotlinx-coroutines-core/jvm/src/flow/internal/FlowExceptions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.flow.internal diff --git a/kotlinx-coroutines-core/jvm/src/flow/internal/SafeCollector.kt b/kotlinx-coroutines-core/jvm/src/flow/internal/SafeCollector.kt new file mode 100644 index 0000000000..4647a14245 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/src/flow/internal/SafeCollector.kt @@ -0,0 +1,134 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.flow.internal + +import kotlinx.coroutines.flow.* +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* +import kotlin.coroutines.jvm.internal.* + +@Suppress("UNCHECKED_CAST") +private val emitFun = + FlowCollector::emit as Function3, Any?, Continuation, Any?> +/* + * Implementor of ContinuationImpl (that will be preserved as ABI nearly forever) + * in order to properly control 'intercepted()' lifecycle. + */ +@Suppress("CANNOT_OVERRIDE_INVISIBLE_MEMBER", "INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "UNCHECKED_CAST") +internal actual class SafeCollector actual constructor( + @JvmField internal actual val collector: FlowCollector, + @JvmField internal actual val collectContext: CoroutineContext +) : FlowCollector, ContinuationImpl(NoOpContinuation, EmptyCoroutineContext) { + + @JvmField // Note, it is non-capturing lambda, so no extra allocation during init of SafeCollector + internal actual val collectContextSize = collectContext.fold(0) { count, _ -> count + 1 } + private var lastEmissionContext: CoroutineContext? = null + private var completion: Continuation? = null + + // ContinuationImpl + override val context: CoroutineContext + get() = completion?.context ?: EmptyCoroutineContext + + override fun invokeSuspend(result: Result): Any? { + result.onFailure { lastEmissionContext = DownstreamExceptionElement(it) } + completion?.resumeWith(result as Result) + return COROUTINE_SUSPENDED + } + + // Escalate visibility to manually release intercepted continuation + public actual override fun releaseIntercepted() { + super.releaseIntercepted() + } + + /** + * This is a crafty implementation of state-machine reusing. + * First it checks that it is not used concurrently (which we explicitly prohibit) and + * then just cache an instance of the completion in order to avoid extra allocation on each emit, + * making it effectively garbage-free on its hot-path. + */ + override suspend fun emit(value: T) { + return suspendCoroutineUninterceptedOrReturn sc@{ uCont -> + try { + emit(uCont, value) + } catch (e: Throwable) { + // Save the fact that exception from emit (or even check context) has been thrown + lastEmissionContext = DownstreamExceptionElement(e) + throw e + } + } + } + + private fun emit(uCont: Continuation, value: T): Any? { + val currentContext = uCont.context + // This check is triggered once per flow on happy path. + val previousContext = lastEmissionContext + if (previousContext !== currentContext) { + checkContext(currentContext, previousContext, value) + } + completion = uCont + return emitFun(collector as FlowCollector, value, this as Continuation) + } + + private fun checkContext( + currentContext: CoroutineContext, + previousContext: CoroutineContext?, + value: T + ) { + if (previousContext is DownstreamExceptionElement) { + exceptionTransparencyViolated(previousContext, value) + } + checkContext(currentContext) + lastEmissionContext = currentContext + } + + private fun exceptionTransparencyViolated(exception: DownstreamExceptionElement, value: Any?) { + /* + * Exception transparency ensures that if a `collect` block or any intermediate operator + * throws an exception, then no more values will be received by it. + * For example, the following code: + * ``` + * val flow = flow { + * emit(1) + * try { + * emit(2) + * } catch (e: Exception) { + * emit(3) + * } + * } + * // Collector + * flow.collect { value -> + * if (value == 2) { + * throw CancellationException("No more elements required, received enough") + * } else { + * println("Collected $value") + * } + * } + * ``` + * is expected to print "Collected 1" and then "No more elements required, received enough" exception, + * but if exception transparency wasn't enforced, "Collected 1" and "Collected 3" would be printed instead. + */ + error(""" + Flow exception transparency is violated: + Previous 'emit' call has thrown exception ${exception.e}, but then emission attempt of value '$value' has been detected. + Emissions from 'catch' blocks are prohibited in order to avoid unspecified behaviour, 'Flow.catch' operator can be used instead. + For a more detailed explanation, please refer to Flow documentation. + """.trimIndent()) + } + +} + +internal class DownstreamExceptionElement(@JvmField val e: Throwable) : CoroutineContext.Element { + companion object Key : CoroutineContext.Key + + override val key: CoroutineContext.Key<*> = Key +} + +private object NoOpContinuation : Continuation { + override val context: CoroutineContext = EmptyCoroutineContext + + override fun resumeWith(result: Result) { + // Nothing + } +} diff --git a/kotlinx-coroutines-core/jvm/src/internal/Concurrent.kt b/kotlinx-coroutines-core/jvm/src/internal/Concurrent.kt index e835d343f3..171748ff68 100644 --- a/kotlinx-coroutines-core/jvm/src/internal/Concurrent.kt +++ b/kotlinx-coroutines-core/jvm/src/internal/Concurrent.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal @@ -16,7 +16,9 @@ internal actual typealias ReentrantLock = java.util.concurrent.locks.ReentrantLo internal actual inline fun ReentrantLock.withLock(action: () -> T) = this.withLockJvm(action) -internal actual fun identitySet(expectedSize: Int): MutableSet = Collections.newSetFromMap(IdentityHashMap(expectedSize)) +@Suppress("NOTHING_TO_INLINE") // So that R8 can completely remove ConcurrentKt class +internal actual inline fun identitySet(expectedSize: Int): MutableSet = + Collections.newSetFromMap(IdentityHashMap(expectedSize)) private val REMOVE_FUTURE_ON_CANCEL: Method? = try { ScheduledThreadPoolExecutor::class.java.getMethod("setRemoveOnCancelPolicy", Boolean::class.java) diff --git a/kotlinx-coroutines-core/jvm/src/internal/ExceptionsConstuctor.kt b/kotlinx-coroutines-core/jvm/src/internal/ExceptionsConstuctor.kt index 8d11c6fdb7..a03163db36 100644 --- a/kotlinx-coroutines-core/jvm/src/internal/ExceptionsConstuctor.kt +++ b/kotlinx-coroutines-core/jvm/src/internal/ExceptionsConstuctor.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal diff --git a/kotlinx-coroutines-core/jvm/src/internal/FastServiceLoader.kt b/kotlinx-coroutines-core/jvm/src/internal/FastServiceLoader.kt index 6267581f3c..30cd09ef36 100644 --- a/kotlinx-coroutines-core/jvm/src/internal/FastServiceLoader.kt +++ b/kotlinx-coroutines-core/jvm/src/internal/FastServiceLoader.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package kotlinx.coroutines.internal import java.io.* diff --git a/kotlinx-coroutines-core/jvm/src/internal/LockFreeLinkedList.kt b/kotlinx-coroutines-core/jvm/src/internal/LockFreeLinkedList.kt index 70568aa278..f718df04b5 100644 --- a/kotlinx-coroutines-core/jvm/src/internal/LockFreeLinkedList.kt +++ b/kotlinx-coroutines-core/jvm/src/internal/LockFreeLinkedList.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal @@ -40,14 +40,18 @@ public actual typealias PrepareOp = LockFreeLinkedListNode.PrepareOp * Doubly-linked concurrent list node with remove support. * Based on paper * ["Lock-Free and Practical Doubly Linked List-Based Deques Using Single-Word Compare-and-Swap"](https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.140.4693&rep=rep1&type=pdf) - * by Sundell and Tsigas. + * by Sundell and Tsigas with considerable changes. + * + * The core idea of the algorithm is to maintain a doubly-linked list with an ever-present sentinel node (it is + * never removed) that serves both as a list head and tail and to linearize all operations (both insert and remove) on + * the update of the next pointer. Removed nodes have their next pointer marked with [Removed] class. * * Important notes: - * * The instance of this class serves both as list head/tail sentinel and as the list item. - * Sentinel node should be never removed. * * There are no operations to add items to left side of the list, only to the end (right side), because we cannot * efficiently linearize them with atomic multi-step head-removal operations. In short, * support for [describeRemoveFirst] operation precludes ability to add items at the beginning. + * * Previous pointers are not marked for removal. We don't support linearizable backwards traversal. + * * Remove-helping logic is simplified and consolidated in [correctPrev] method. * * @suppress **This is unstable API and it is subject to change.** */ @@ -55,7 +59,7 @@ public actual typealias PrepareOp = LockFreeLinkedListNode.PrepareOp @InternalCoroutinesApi public actual open class LockFreeLinkedListNode { private val _next = atomic(this) // Node | Removed | OpDescriptor - private val _prev = atomic(this) // Node | Removed + private val _prev = atomic(this) // Node to the left (cannot be marked as removed) private val _removedRef = atomic(null) // lazily cached removed ref to this private fun removed(): Removed = @@ -83,7 +87,7 @@ public actual open class LockFreeLinkedListNode { override fun prepare(affected: Node): Any? = if (condition()) null else CONDITION_FALSE } - public actual val isRemoved: Boolean get() = next is Removed + public actual open val isRemoved: Boolean get() = next is Removed // LINEARIZABLE. Returns Node | Removed public val next: Any get() { @@ -96,19 +100,19 @@ public actual open class LockFreeLinkedListNode { // LINEARIZABLE. Returns next non-removed Node public actual val nextNode: Node get() = next.unwrap() - // LINEARIZABLE. Returns Node | Removed - public val prev: Any get() { - _prev.loop { prev -> - if (prev is Removed) return prev - prev as Node // otherwise, it can be only node - if (prev.next === this) return prev - correctPrev(prev, null) - } + // LINEARIZABLE WHEN THIS NODE IS NOT REMOVED: + // Returns prev non-removed Node, makes sure prev is correct (prev.next === this) + // NOTE: if this node is removed, then returns non-removed previous node without applying + // prev.next correction, which does not provide linearizable backwards iteration, but can be used to + // resume forward iteration when current node was removed. + public actual val prevNode: Node + get() = correctPrev(null) ?: findPrevNonRemoved(_prev.value) + + private tailrec fun findPrevNonRemoved(current: Node): Node { + if (!current.isRemoved) return current + return findPrevNonRemoved(current._prev.value) } - // LINEARIZABLE. Returns prev non-removed Node - public actual val prevNode: Node get() = prev.unwrap() - // ------ addOneIfEmpty ------ public actual fun addOneIfEmpty(node: Node): Boolean { @@ -132,8 +136,7 @@ public actual open class LockFreeLinkedListNode { */ public actual fun addLast(node: Node) { while (true) { // lock-free loop on prev.next - val prev = prev as Node // sentinel node is never removed, so prev is always defined - if (prev.addNext(node, this)) return + if (prevNode.addNext(node, this)) return } } @@ -145,7 +148,7 @@ public actual open class LockFreeLinkedListNode { public actual inline fun addLastIf(node: Node, crossinline condition: () -> Boolean): Boolean { val condAdd = makeCondAddOp(node, condition) while (true) { // lock-free loop on prev.next - val prev = prev as Node // sentinel node is never removed, so prev is always defined + val prev = prevNode // sentinel node is never removed, so prev is always defined when (prev.tryCondAddNext(node, this, condAdd)) { SUCCESS -> return true FAILURE -> return false @@ -155,7 +158,7 @@ public actual open class LockFreeLinkedListNode { public actual inline fun addLastIfPrev(node: Node, predicate: (Node) -> Boolean): Boolean { while (true) { // lock-free loop on prev.next - val prev = prev as Node // sentinel node is never removed, so prev is always defined + val prev = prevNode // sentinel node is never removed, so prev is always defined if (!predicate(prev)) return false if (prev.addNext(node, this)) return true } @@ -168,7 +171,7 @@ public actual open class LockFreeLinkedListNode { ): Boolean { val condAdd = makeCondAddOp(node, condition) while (true) { // lock-free loop on prev.next - val prev = prev as Node // sentinel node is never removed, so prev is always defined + val prev = prevNode // sentinel node is never removed, so prev is always defined if (!predicate(prev)) return false when (prev.tryCondAddNext(node, this, condAdd)) { SUCCESS -> return true @@ -233,55 +236,71 @@ public actual open class LockFreeLinkedListNode { * In particular, invoking [nextNode].[prevNode] might still return this node even though it is "already removed". * Invoke [helpRemove] to make sure that remove was completed. */ - public actual open fun remove(): Boolean { + public actual open fun remove(): Boolean = + removeOrNext() == null + + // returns null if removed successfully or next node if this node is already removed + @PublishedApi + internal fun removeOrNext(): Node? { while (true) { // lock-free loop on next val next = this.next - if (next is Removed) return false // was already removed -- don't try to help (original thread will take care) - if (next === this) return false // was not even added + if (next is Removed) return next.ref // was already removed -- don't try to help (original thread will take care) + if (next === this) return next // was not even added val removed = (next as Node).removed() if (_next.compareAndSet(next, removed)) { // was removed successfully (linearized remove) -- fixup the list - finishRemove(next) - return true + next.correctPrev(null) + return null } } } + // Helps with removal of this node public actual fun helpRemove() { - val removed = this.next as? Removed ?: error("Must be invoked on a removed node") - finishRemove(removed.ref) + // Note: this node must be already removed + (next as Removed).ref.correctPrev(null) } - public actual fun removeFirstOrNull(): Node? { - while (true) { // try to linearize - val first = next as Node - if (first === this) return null - if (first.remove()) return first - first.helpDelete() // must help delete, or loose lock-freedom + // Helps with removal of nodes that are previous to this + @PublishedApi + internal fun helpRemovePrev() { + // We need to call correctPrev on a non-removed node to ensure progress, since correctPrev bails out when + // called on a removed node. There's always at least one non-removed node (list head). + var node = this + while (true) { + val next = node.next + if (next !is Removed) break + node = next.ref } + // Found a non-removed node + node.correctPrev(null) } - public fun describeRemoveFirst(): RemoveFirstDesc = RemoveFirstDesc(this) - - public inline fun removeFirstIfIsInstanceOf(): T? { + public actual fun removeFirstOrNull(): Node? { while (true) { // try to linearize val first = next as Node if (first === this) return null - if (first !is T) return null if (first.remove()) return first - first.helpDelete() // must help delete, or loose lock-freedom + first.helpRemove() // must help remove to ensure lock-freedom } } + public fun describeRemoveFirst(): RemoveFirstDesc = RemoveFirstDesc(this) + // just peek at item when predicate is true public actual inline fun removeFirstIfIsInstanceOfOrPeekIf(predicate: (T) -> Boolean): T? { - while (true) { // try to linearize - val first = next as Node - if (first === this) return null + while (true) { + val first = this.next as Node + if (first === this) return null // got list head -- nothing to remove if (first !is T) return null - if (predicate(first)) return first // just peek when predicate is true - if (first.remove()) return first - first.helpDelete() // must help delete, or loose lock-freedom + if (predicate(first)) { + // check for removal of the current node to make sure "peek" operation is linearizable + if (!first.isRemoved) return first + } + val next = first.removeOrNext() + if (next === null) return first // removed successfully -- return it + // help and start from the beginning + next.helpRemovePrev() } } @@ -296,25 +315,9 @@ public actual open class LockFreeLinkedListNode { assert { node._next.value === node && node._prev.value === node } } - // Returns null when atomic op got into deadlock trying to help operation that started later - final override fun takeAffectedNode(op: OpDescriptor): Node? { - while (true) { - val prev = queue._prev.value as Node // this sentinel node is never removed - val next = prev._next.value - if (next === queue) return prev // all is good -> linked properly - if (next === op) return prev // all is good -> our operation descriptor is already there - if (next is OpDescriptor) { // some other operation descriptor -> help & retry - if (op.isEarlierThan(next)) - return null // RETRY_ATOMIC - next.perform(prev) - continue - } - // linked improperly -- help insert - val affected = queue.correctPrev(prev, op) - // we can find node which this operation is already affecting while trying to correct prev - if (affected != null) return affected - } - } + // Returns null when atomic op got into deadlock trying to help operation that started later (RETRY_ATOMIC) + final override fun takeAffectedNode(op: OpDescriptor): Node? = + queue.correctPrev(op) // queue head is never removed, so null result can only mean RETRY_ATOMIC private val _affectedNode = atomic(null) final override val affectedNode: Node? get() = _affectedNode.value @@ -368,11 +371,11 @@ public actual open class LockFreeLinkedListNode { // check node predicates here, must signal failure if affect is not of type T protected override fun failure(affected: Node): Any? = - if (affected === queue) LIST_EMPTY else null + if (affected === queue) LIST_EMPTY else null final override fun retry(affected: Node, next: Any): Boolean { if (next !is Removed) return false - affected.helpDelete() // must help delete, or loose lock-freedom + next.ref.helpRemovePrev() // must help delete to ensure lock-freedom return true } @@ -385,7 +388,12 @@ public actual open class LockFreeLinkedListNode { } final override fun updatedNext(affected: Node, next: Node): Any = next.removed() - final override fun finishOnSuccess(affected: Node, next: Node) = affected.finishRemove(next) + + final override fun finishOnSuccess(affected: Node, next: Node) { + // Complete removal operation here. It bails out if next node is also removed. It becomes + // responsibility of the next's removes to call correctPrev which would help fix all the links. + next.correctPrev(null) + } } // This is Harris's RDCSS (Restricted Double-Compare Single Swap) operation @@ -404,9 +412,12 @@ public actual open class LockFreeLinkedListNode { val decision = desc.onPrepare(this) if (decision === REMOVE_PREPARED) { // remove element on failure -- do not mark as decided, will try another one + val next = this.next val removed = next.removed() if (affected._next.compareAndSet(this, removed)) { - affected.helpDelete() + // Complete removal operation here. It bails out if next node is also removed and it becomes + // responsibility of the next's removes to call correctPrev which would help fix all the links. + next.correctPrev(null) } return REMOVE_PREPARED } @@ -519,139 +530,73 @@ public actual open class LockFreeLinkedListNode { */ private fun finishAdd(next: Node) { next._prev.loop { nextPrev -> - if (nextPrev is Removed || this.next !== next) return // next was removed, remover fixes up links + if (this.next !== next) return // this or next was removed or another node added, remover/adder fixes up links if (next._prev.compareAndSet(nextPrev, this)) { - if (this.next is Removed) { - // already removed - next.correctPrev(nextPrev as Node, null) - } + // This newly added node could have been removed, and the above CAS would have added it physically again. + // Let us double-check for this situation and correct if needed + if (isRemoved) next.correctPrev(null) return } } } - private fun finishRemove(next: Node) { - helpDelete() - next.correctPrev(_prev.value.unwrap(), null) - } - - private fun markPrev(): Node { - _prev.loop { prev -> - if (prev is Removed) return prev.ref - // See detailed comment in findHead on why `prev === this` is a special case for which we know that - // the prev should have being pointing to the head of list but finishAdd that was supposed - // to do that is not complete yet. - val removedPrev = (if (prev === this) findHead() else (prev as Node)).removed() - if (_prev.compareAndSet(prev, removedPrev)) return prev - } - } + protected open fun nextIfRemoved(): Node? = (next as? Removed)?.ref /** - * Finds the head of the list (implementing [LockFreeLinkedListHead]) by following [next] pointers. + * Returns the corrected value of the previous node while also correcting the `prev` pointer + * (so that `this.prev.next === this`) and helps complete node removals to the left ot this node. * - * The code in [kotlinx.coroutines.JobSupport] performs upgrade of a single node to a list. - * It uses [addOneIfEmpty] to add the list head to "empty list of a single node" once. - * During upgrade a transient state of the list looks like this: + * It returns `null` in two special cases: * - * ``` - * +-----------------+ - * | | - * node V head | - * +---+---+ +---+---+ | - * +-> | P | N | --> | P | N |-+ - * | +---+---+ +---+---+ - * | | ^ | - * +---- + +---------+ - * ``` - * - * The [prev] pointer in `node` still points to itself when [finishAdd] (invoked inside [addOneIfEmpty]) - * has not completed yet. If this state is observed, then we know that [prev] should have been pointing - * to the list head. This function is looking up the head by following consistent chain of [next] pointers. + * * When this node is removed. In this case there is no need to waste time on corrections, because + * remover of this node will ultimately call [correctPrev] on the next node and that will fix all + * the links from this node, too. + * * When [op] descriptor is not `null` and operation descriptor that is [OpDescriptor.isEarlierThan] + * that current [op] is found while traversing the list. This `null` result will be translated + * by callers to [RETRY_ATOMIC]. */ - private fun findHead(): Node { - var cur = this - while (true) { - if (cur is LockFreeLinkedListHead) return cur - cur = cur.nextNode - assert { cur !== this } // "Cannot loop to this while looking for list head" - } - } - - // fixes next links to the left of this node - @PublishedApi - internal fun helpDelete() { - var last: Node? = null // will set to the node left of prev when found - var prev: Node = markPrev() - var next: Node = (this._next.value as Removed).ref - while (true) { - // move to the right until first non-removed node - val nextNext = next.next - if (nextNext is Removed) { - next.markPrev() - next = nextNext.ref - continue - } - // move the the left until first non-removed node - val prevNext = prev.next - if (prevNext is Removed) { - if (last != null) { - prev.markPrev() - last._next.compareAndSet(prev, prevNext.ref) - prev = last - last = null - } else { - prev = prev._prev.value.unwrap() - } - continue - } - if (prevNext !== this) { - // skipped over some removed nodes to the left -- setup to fixup the next links - last = prev - prev = prevNext as Node - if (prev === next) return // already done!!! - continue - } - // Now prev & next are Ok - if (prev._next.compareAndSet(this, next)) return // success! - } - } - - // fixes prev links from this node - // returns affected node by this operation when this op is in progress (and nothing can be corrected) - // returns null otherwise (prev was corrected) - private fun correctPrev(_prev: Node, op: OpDescriptor?): Node? { - var prev: Node = _prev + private tailrec fun correctPrev(op: OpDescriptor?): Node? { + val oldPrev = _prev.value + var prev: Node = oldPrev var last: Node? = null // will be set so that last.next === prev - while (true) { - // move the the left until first non-removed node - val prevNext = prev._next.value - if (prevNext === op) return prev // part of the same op -- don't recurse, didn't correct prev - if (prevNext is OpDescriptor) { // help & retry - prevNext.perform(prev) - continue - } - if (prevNext is Removed) { - if (last !== null) { - prev.markPrev() - last._next.compareAndSet(prev, prevNext.ref) - prev = last - last = null - } else { - prev = prev._prev.value.unwrap() + while (true) { // move the left until first non-removed node + val prevNext: Any = prev._next.value + when { + // fast path to find quickly find prev node when everything is properly linked + prevNext === this -> { + if (oldPrev === prev) return prev // nothing to update -- all is fine, prev found + // otherwise need to update prev + if (!this._prev.compareAndSet(oldPrev, prev)) { + // Note: retry from scratch on failure to update prev + return correctPrev(op) + } + return prev // return the correct prev + } + // slow path when we need to help remove operations + this.isRemoved -> return null // nothing to do, this node was removed, bail out asap to save time + prevNext === op -> return prev // part of the same op -- don't recurse, didn't correct prev + prevNext is OpDescriptor -> { // help & retry + if (op != null && op.isEarlierThan(prevNext)) + return null // RETRY_ATOMIC + prevNext.perform(prev) + return correctPrev(op) // retry from scratch + } + prevNext is Removed -> { + if (last !== null) { + // newly added (prev) node is already removed, correct last.next around it + if (!last._next.compareAndSet(prev, prevNext.ref)) { + return correctPrev(op) // retry from scratch on failure to update next + } + prev = last + last = null + } else { + prev = prev._prev.value + } + } + else -> { // prevNext is a regular node, but not this -- help delete + last = prev + prev = prevNext as Node } - continue - } - val oldPrev = this._prev.value - if (oldPrev is Removed) return null // this node was removed, too -- its remover will take care - if (prevNext !== this) { - // need to fixup next - last = prev - prev = prevNext as Node - continue - } - if (oldPrev === prev) return null // it is already linked as needed - if (this._prev.compareAndSet(oldPrev, prev)) { - if (prev._prev.value !is Removed) return null // finish only if prev was not concurrently removed } } } @@ -691,7 +636,11 @@ public actual open class LockFreeLinkedListHead : LockFreeLinkedListNode() { } // just a defensive programming -- makes sure that list head sentinel is never removed - public actual final override fun remove(): Boolean = throw UnsupportedOperationException() + public actual final override fun remove(): Boolean = error("head cannot be removed") + + // optimization: because head is never removed, we don't have to read _next.value to check these: + override val isRemoved: Boolean get() = false + override fun nextIfRemoved(): Node? = null internal fun validate() { var prev: Node = this diff --git a/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt b/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt index 6f11cdf795..b3e49752e0 100644 --- a/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt +++ b/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package kotlinx.coroutines.internal import kotlinx.coroutines.* @@ -30,11 +34,12 @@ internal object MainDispatcherLoader { MainDispatcherFactory::class.java.classLoader ).iterator().asSequence().toList() } + @Suppress("ConstantConditionIf") factories.maxBy { it.loadPriority }?.tryCreateDispatcher(factories) - ?: MissingMainCoroutineDispatcher(null) + ?: createMissingDispatcher() } catch (e: Throwable) { // Service loader can throw an exception as well - MissingMainCoroutineDispatcher(e) + createMissingDispatcher(e) } } } @@ -51,13 +56,30 @@ public fun MainDispatcherFactory.tryCreateDispatcher(factories: List): MainCoroutineDispatcher { return MissingMainCoroutineDispatcher(null) } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/jvm/src/internal/ProbesSupport.kt b/kotlinx-coroutines-core/jvm/src/internal/ProbesSupport.kt index baa9682788..2f4d1e052e 100644 --- a/kotlinx-coroutines-core/jvm/src/internal/ProbesSupport.kt +++ b/kotlinx-coroutines-core/jvm/src/internal/ProbesSupport.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("NOTHING_TO_INLINE", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") diff --git a/kotlinx-coroutines-core/jvm/src/internal/StackTraceRecovery.kt b/kotlinx-coroutines-core/jvm/src/internal/StackTraceRecovery.kt index b512815556..f5c5c24b47 100644 --- a/kotlinx-coroutines-core/jvm/src/internal/StackTraceRecovery.kt +++ b/kotlinx-coroutines-core/jvm/src/internal/StackTraceRecovery.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("UNCHECKED_CAST") @@ -52,7 +52,8 @@ private fun E.sanitizeStackTrace(): E { return this } -internal actual fun recoverStackTrace(exception: E, continuation: Continuation<*>): E { +@Suppress("NOTHING_TO_INLINE") // Inline for better R8 optimization +internal actual inline fun recoverStackTrace(exception: E, continuation: Continuation<*>): E { if (!RECOVER_STACK_TRACES || continuation !is CoroutineStackFrame) return exception return recoverFromStackFrame(exception, continuation) } @@ -155,8 +156,11 @@ internal actual suspend inline fun recoverAndThrow(exception: Throwable): Nothin } } -internal actual fun unwrap(exception: E): E { - if (!RECOVER_STACK_TRACES) return exception +@Suppress("NOTHING_TO_INLINE") // Inline for better R8 optimizations +internal actual inline fun unwrap(exception: E): E = + if (!RECOVER_STACK_TRACES) exception else unwrapImpl(exception) + +internal fun unwrapImpl(exception: E): E { val cause = exception.cause // Fast-path to avoid array cloning if (cause == null || cause.javaClass != exception.javaClass) { @@ -209,4 +213,4 @@ internal actual typealias StackTraceElement = java.lang.StackTraceElement internal actual fun Throwable.initCause(cause: Throwable) { // Resolved to member, verified by test initCause(cause) -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/jvm/src/internal/Synchronized.kt b/kotlinx-coroutines-core/jvm/src/internal/Synchronized.kt index 51dcee4baf..2b57b26cbd 100644 --- a/kotlinx-coroutines-core/jvm/src/internal/Synchronized.kt +++ b/kotlinx-coroutines-core/jvm/src/internal/Synchronized.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal @@ -17,4 +17,4 @@ public actual typealias SynchronizedObject = Any */ @InternalCoroutinesApi public actual inline fun synchronized(lock: SynchronizedObject, block: () -> T): T = - kotlin.synchronized(lock, block) \ No newline at end of file + kotlin.synchronized(lock, block) diff --git a/kotlinx-coroutines-core/jvm/src/internal/SystemProps.kt b/kotlinx-coroutines-core/jvm/src/internal/SystemProps.kt index ef5ab24118..bf34c1a97b 100644 --- a/kotlinx-coroutines-core/jvm/src/internal/SystemProps.kt +++ b/kotlinx-coroutines-core/jvm/src/internal/SystemProps.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmName("SystemPropsKt") diff --git a/kotlinx-coroutines-core/jvm/src/internal/ThreadContext.kt b/kotlinx-coroutines-core/jvm/src/internal/ThreadContext.kt index 375dc60b66..9d9d30e41d 100644 --- a/kotlinx-coroutines-core/jvm/src/internal/ThreadContext.kt +++ b/kotlinx-coroutines-core/jvm/src/internal/ThreadContext.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal diff --git a/kotlinx-coroutines-core/jvm/src/internal/ThreadLocal.kt b/kotlinx-coroutines-core/jvm/src/internal/ThreadLocal.kt index 39c87bed1c..ff0970a219 100644 --- a/kotlinx-coroutines-core/jvm/src/internal/ThreadLocal.kt +++ b/kotlinx-coroutines-core/jvm/src/internal/ThreadLocal.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal diff --git a/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt b/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt index 09e9deb838..815fa26941 100644 --- a/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt +++ b/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.scheduling @@ -73,7 +73,7 @@ import kotlin.random.* * Only [corePoolSize] workers can be created for regular CPU tasks) * * ### Support for blocking tasks - * The scheduler also supports the notion of [blocking][TaskMode.PROBABLY_BLOCKING] tasks. + * The scheduler also supports the notion of [blocking][TASK_PROBABLY_BLOCKING] tasks. * When executing or enqueuing blocking tasks, the scheduler notifies or creates one more worker in * addition to core pool size, so at any given moment, it has [corePoolSize] threads (potentially not yet created) * to serve CPU-bound tasks. To properly guarantee liveness, the scheduler maintains @@ -372,25 +372,34 @@ internal class CoroutineScheduler( * Dispatches execution of a runnable [block] with a hint to a scheduler whether * this [block] may execute blocking operations (IO, system calls, locking primitives etc.) * - * @param taskContext concurrency context of given [block] - * @param fair whether the task should be dispatched fairly (strict FIFO) or not (semi-FIFO) + * [taskContext] -- concurrency context of given [block]. + * [tailDispatch] -- whether this [dispatch] call is the last action the (presumably) worker thread does in its current task. + * If `true`, then the task will be dispatched in a FIFO manner and no additional workers will be requested, + * but only if the current thread is a corresponding worker thread. + * Note that caller cannot be ensured that it is being executed on worker thread for the following reasons: + * * [CoroutineStart.UNDISPATCHED] + * * Concurrent [close] that effectively shutdowns the worker thread */ - fun dispatch(block: Runnable, taskContext: TaskContext = NonBlockingContext, fair: Boolean = false) { + fun dispatch(block: Runnable, taskContext: TaskContext = NonBlockingContext, tailDispatch: Boolean = false) { trackTask() // this is needed for virtual time support val task = createTask(block, taskContext) // try to submit the task to the local queue and act depending on the result - val notAdded = submitToLocalQueue(task, fair) + val currentWorker = currentWorker() + val notAdded = currentWorker.submitToLocalQueue(task, tailDispatch) if (notAdded != null) { if (!addToGlobalQueue(notAdded)) { // Global queue is closed in the last step of close/shutdown -- no more tasks should be accepted throw RejectedExecutionException("$schedulerName was terminated") } } + val skipUnpark = tailDispatch && currentWorker != null // Checking 'task' instead of 'notAdded' is completely okay - if (task.mode == TaskMode.NON_BLOCKING) { + if (task.mode == TASK_NON_BLOCKING) { + if (skipUnpark) return signalCpuWork() } else { - signalBlockingWork() + // Increment blocking tasks anyway + signalBlockingWork(skipUnpark = skipUnpark) } } @@ -404,9 +413,10 @@ internal class CoroutineScheduler( return TaskImpl(block, nanoTime, taskContext) } - private fun signalBlockingWork() { + private fun signalBlockingWork(skipUnpark: Boolean) { // Use state snapshot to avoid thread overprovision val stateSnapshot = incrementBlockingTasks() + if (skipUnpark) return if (tryUnpark()) return if (tryCreateWorker(stateSnapshot)) return tryUnpark() // Try unpark again in case there was race between permit release and parking @@ -481,19 +491,19 @@ internal class CoroutineScheduler( * Returns `null` if task was successfully added or an instance of the * task that was not added or replaced (thus should be added to global queue). */ - private fun submitToLocalQueue(task: Task, fair: Boolean): Task? { - val worker = currentWorker() ?: return task + private fun Worker?.submitToLocalQueue(task: Task, tailDispatch: Boolean): Task? { + if (this == null) return task /* * This worker could have been already terminated from this thread by close/shutdown and it should not * accept any more tasks into its local queue. */ - if (worker.state === WorkerState.TERMINATED) return task + if (state === WorkerState.TERMINATED) return task // Do not add CPU tasks in local queue if we are not able to execute it - if (task.mode === TaskMode.NON_BLOCKING && worker.state === WorkerState.BLOCKING) { + if (task.mode == TASK_NON_BLOCKING && state === WorkerState.BLOCKING) { return task } - worker.mayHaveLocalTasks = true - return worker.localQueue.add(task, fair = fair) + mayHaveLocalTasks = true + return localQueue.add(task, fair = tailDispatch) } private fun currentWorker(): Worker? = (Thread.currentThread() as? Worker)?.takeIf { it.scheduler == this } @@ -653,6 +663,7 @@ internal class CoroutineScheduler( } override fun run() = runWorker() + @JvmField var mayHaveLocalTasks = false @@ -728,16 +739,16 @@ internal class CoroutineScheduler( afterTask(taskMode) } - private fun beforeTask(taskMode: TaskMode) { - if (taskMode == TaskMode.NON_BLOCKING) return + private fun beforeTask(taskMode: Int) { + if (taskMode == TASK_NON_BLOCKING) return // Always notify about new work when releasing CPU-permit to execute some blocking task if (tryReleaseCpu(WorkerState.BLOCKING)) { signalCpuWork() } } - private fun afterTask(taskMode: TaskMode) { - if (taskMode == TaskMode.NON_BLOCKING) return + private fun afterTask(taskMode: Int) { + if (taskMode == TASK_NON_BLOCKING) return decrementBlockingTasks() val currentState = state // Shutdown sequence of blocking dispatcher @@ -835,10 +846,10 @@ internal class CoroutineScheduler( } // It is invoked by this worker when it finds a task - private fun idleReset(mode: TaskMode) { + private fun idleReset(mode: Int) { terminationDeadline = 0L // reset deadline for termination if (state == WorkerState.PARKING) { - assert { mode == TaskMode.PROBABLY_BLOCKING } + assert { mode == TASK_PROBABLY_BLOCKING } state = WorkerState.BLOCKING } } @@ -915,12 +926,12 @@ internal class CoroutineScheduler( enum class WorkerState { /** - * Has CPU token and either executes [TaskMode.NON_BLOCKING] task or tries to find one. + * Has CPU token and either executes [TASK_NON_BLOCKING] task or tries to find one. */ CPU_ACQUIRED, /** - * Executing task with [TaskMode.PROBABLY_BLOCKING]. + * Executing task with [TASK_PROBABLY_BLOCKING]. */ BLOCKING, diff --git a/kotlinx-coroutines-core/jvm/src/scheduling/Dispatcher.kt b/kotlinx-coroutines-core/jvm/src/scheduling/Dispatcher.kt index bd1ba95dd8..7398f12fe7 100644 --- a/kotlinx-coroutines-core/jvm/src/scheduling/Dispatcher.kt +++ b/kotlinx-coroutines-core/jvm/src/scheduling/Dispatcher.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.scheduling @@ -65,7 +65,7 @@ open class ExperimentalCoroutineDispatcher( override fun dispatchYield(context: CoroutineContext, block: Runnable): Unit = try { - coroutineScheduler.dispatch(block, fair = true) + coroutineScheduler.dispatch(block, tailDispatch = true) } catch (e: RejectedExecutionException) { DefaultExecutor.dispatchYield(context, block) } @@ -85,7 +85,7 @@ open class ExperimentalCoroutineDispatcher( */ public fun blocking(parallelism: Int = BLOCKING_DEFAULT_PARALLELISM): CoroutineDispatcher { require(parallelism > 0) { "Expected positive parallelism level, but have $parallelism" } - return LimitingDispatcher(this, parallelism, TaskMode.PROBABLY_BLOCKING) + return LimitingDispatcher(this, parallelism, TASK_PROBABLY_BLOCKING) } /** @@ -98,12 +98,12 @@ open class ExperimentalCoroutineDispatcher( public fun limited(parallelism: Int): CoroutineDispatcher { require(parallelism > 0) { "Expected positive parallelism level, but have $parallelism" } require(parallelism <= corePoolSize) { "Expected parallelism level lesser than core pool size ($corePoolSize), but have $parallelism" } - return LimitingDispatcher(this, parallelism, TaskMode.NON_BLOCKING) + return LimitingDispatcher(this, parallelism, TASK_NON_BLOCKING) } - internal fun dispatchWithContext(block: Runnable, context: TaskContext, fair: Boolean) { + internal fun dispatchWithContext(block: Runnable, context: TaskContext, tailDispatch: Boolean) { try { - coroutineScheduler.dispatch(block, context, fair) + coroutineScheduler.dispatch(block, context, tailDispatch) } catch (e: RejectedExecutionException) { // Context shouldn't be lost here to properly invoke before/after task DefaultExecutor.enqueue(coroutineScheduler.createTask(block, context)) @@ -132,7 +132,7 @@ open class ExperimentalCoroutineDispatcher( private class LimitingDispatcher( val dispatcher: ExperimentalCoroutineDispatcher, val parallelism: Int, - override val taskMode: TaskMode + override val taskMode: Int ) : ExecutorCoroutineDispatcher(), TaskContext, Executor { private val queue = ConcurrentLinkedQueue() @@ -147,7 +147,7 @@ private class LimitingDispatcher( override fun dispatch(context: CoroutineContext, block: Runnable) = dispatch(block, false) - private fun dispatch(block: Runnable, fair: Boolean) { + private fun dispatch(block: Runnable, tailDispatch: Boolean) { var taskToSchedule = block while (true) { // Commit in-flight tasks slot @@ -155,7 +155,7 @@ private class LimitingDispatcher( // Fast path, if parallelism limit is not reached, dispatch task and return if (inFlight <= parallelism) { - dispatcher.dispatchWithContext(taskToSchedule, this, fair) + dispatcher.dispatchWithContext(taskToSchedule, this, tailDispatch) return } @@ -185,6 +185,10 @@ private class LimitingDispatcher( } } + override fun dispatchYield(context: CoroutineContext, block: Runnable) { + dispatch(block, tailDispatch = true) + } + override fun toString(): String { return "${super.toString()}[dispatcher = $dispatcher]" } diff --git a/kotlinx-coroutines-core/jvm/src/scheduling/Tasks.kt b/kotlinx-coroutines-core/jvm/src/scheduling/Tasks.kt index c0a3e6435d..247615d777 100644 --- a/kotlinx-coroutines-core/jvm/src/scheduling/Tasks.kt +++ b/kotlinx-coroutines-core/jvm/src/scheduling/Tasks.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.scheduling @@ -51,26 +51,23 @@ internal val IDLE_WORKER_KEEP_ALIVE_NS = TimeUnit.SECONDS.toNanos( @JvmField internal var schedulerTimeSource: TimeSource = NanoTimeSource -internal enum class TaskMode { - - /** - * Marker indicating that task is CPU-bound and will not block - */ - NON_BLOCKING, +/** + * Marker indicating that task is CPU-bound and will not block + */ +internal const val TASK_NON_BLOCKING = 0 - /** - * Marker indicating that task may potentially block, thus giving scheduler a hint that additional thread may be required - */ - PROBABLY_BLOCKING, -} +/** + * Marker indicating that task may potentially block, thus giving scheduler a hint that additional thread may be required + */ +internal const val TASK_PROBABLY_BLOCKING = 1 internal interface TaskContext { - val taskMode: TaskMode + val taskMode: Int // TASK_XXX fun afterTask() } internal object NonBlockingContext : TaskContext { - override val taskMode: TaskMode = TaskMode.NON_BLOCKING + override val taskMode: Int = TASK_NON_BLOCKING override fun afterTask() { // Nothing for non-blocking context @@ -82,10 +79,10 @@ internal abstract class Task( @JvmField var taskContext: TaskContext ) : Runnable { constructor() : this(0, NonBlockingContext) - inline val mode: TaskMode get() = taskContext.taskMode + inline val mode: Int get() = taskContext.taskMode // TASK_XXX } -internal inline val Task.isBlocking get() = taskContext.taskMode == TaskMode.PROBABLY_BLOCKING +internal inline val Task.isBlocking get() = taskContext.taskMode == TASK_PROBABLY_BLOCKING // Non-reusable Task implementation to wrap Runnable instances that do not otherwise implement task internal class TaskImpl( diff --git a/kotlinx-coroutines-core/jvm/src/scheduling/WorkQueue.kt b/kotlinx-coroutines-core/jvm/src/scheduling/WorkQueue.kt index 1a0603e413..354b3a1b3b 100644 --- a/kotlinx-coroutines-core/jvm/src/scheduling/WorkQueue.kt +++ b/kotlinx-coroutines-core/jvm/src/scheduling/WorkQueue.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.scheduling diff --git a/kotlinx-coroutines-core/jvm/src/test_/TestCoroutineContext.kt b/kotlinx-coroutines-core/jvm/src/test_/TestCoroutineContext.kt index 3a1bf3a664..8a0bd1a187 100644 --- a/kotlinx-coroutines-core/jvm/src/test_/TestCoroutineContext.kt +++ b/kotlinx-coroutines-core/jvm/src/test_/TestCoroutineContext.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.test @@ -293,4 +293,4 @@ public fun withTestContext(testContext: TestCoroutineContext = TestCoroutineCont throw AssertionError("Coroutine encountered unhandled exceptions:\n$exceptions") } } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/jvm/test/AsyncJvmTest.kt b/kotlinx-coroutines-core/jvm/test/AsyncJvmTest.kt index dab7d5d033..026752086c 100644 --- a/kotlinx-coroutines-core/jvm/test/AsyncJvmTest.kt +++ b/kotlinx-coroutines-core/jvm/test/AsyncJvmTest.kt @@ -6,6 +6,7 @@ package kotlinx.coroutines import kotlin.test.* + class AsyncJvmTest : TestBase() { // This must be a common test but it fails on JS because of KT-21961 @Test diff --git a/kotlinx-coroutines-core/jvm/test/AwaitStressTest.kt b/kotlinx-coroutines-core/jvm/test/AwaitStressTest.kt index 1e53c55276..6b711db5a6 100644 --- a/kotlinx-coroutines-core/jvm/test/AwaitStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/AwaitStressTest.kt @@ -11,12 +11,8 @@ import java.util.concurrent.* class AwaitStressTest : TestBase() { private val iterations = 50_000 * stressTestMultiplier - private val pool = newFixedThreadPoolContext(4, "AwaitStressTest") - - @After - fun tearDown() { - pool.close() - } + @get:Rule + public val pool = ExecutorRule(4) @Test fun testMultipleExceptions() = runTest { diff --git a/kotlinx-coroutines-core/jvm/test/CancellableContinuationResumeCloseStressTest.kt b/kotlinx-coroutines-core/jvm/test/CancellableContinuationResumeCloseStressTest.kt index 7255b4ab2c..b8bc3f00be 100644 --- a/kotlinx-coroutines-core/jvm/test/CancellableContinuationResumeCloseStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/CancellableContinuationResumeCloseStressTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines @@ -11,8 +11,9 @@ import kotlin.test.* import kotlin.test.Test class CancellableContinuationResumeCloseStressTest : TestBase() { - private val dispatcher = - newFixedThreadPoolContext(2, "CancellableContinuationResumeCloseStressTest") + @get:Rule + public val dispatcher = ExecutorRule(2) + private val startBarrier = CyclicBarrier(3) private val doneBarrier = CyclicBarrier(2) private val nRepeats = 1_000 * stressTestMultiplier @@ -20,11 +21,6 @@ class CancellableContinuationResumeCloseStressTest : TestBase() { private val closed = atomic(false) private var returnedOk = false - @After - fun tearDown() { - dispatcher.close() - } - @Test @Suppress("BlockingMethodInNonBlockingContext") fun testStress() = runTest { @@ -65,4 +61,4 @@ class CancellableContinuationResumeCloseStressTest : TestBase() { fun close() { assertFalse(closed.getAndSet(true)) } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/jvm/test/CommonPoolTest.kt b/kotlinx-coroutines-core/jvm/test/CommonPoolTest.kt index 9af8c28555..8f9f855477 100644 --- a/kotlinx-coroutines-core/jvm/test/CommonPoolTest.kt +++ b/kotlinx-coroutines-core/jvm/test/CommonPoolTest.kt @@ -4,11 +4,10 @@ package kotlinx.coroutines -import kotlinx.coroutines.* -import org.junit.* -import org.junit.Assert.* +import org.junit.Test import java.lang.reflect.* import java.util.concurrent.* +import kotlin.test.* @Suppress("DEPRECATION") class CommonPoolTest { diff --git a/kotlinx-coroutines-core/jvm/test/DelayJvmTest.kt b/kotlinx-coroutines-core/jvm/test/DelayJvmTest.kt index 642e504967..2906c316ef 100644 --- a/kotlinx-coroutines-core/jvm/test/DelayJvmTest.kt +++ b/kotlinx-coroutines-core/jvm/test/DelayJvmTest.kt @@ -4,11 +4,10 @@ package kotlinx.coroutines -import org.hamcrest.MatcherAssert.* -import org.hamcrest.core.* import org.junit.* import java.util.concurrent.* import kotlin.coroutines.* +import kotlin.test.assertEquals class DelayJvmTest : TestBase() { /** @@ -22,12 +21,12 @@ class DelayJvmTest : TestBase() { } val context = CustomInterceptor(pool) val c = async(context) { - assertThat(Thread.currentThread(), IsEqual(thread)) + assertEquals(thread, Thread.currentThread()) delay(100) - assertThat(Thread.currentThread(), IsEqual(thread)) + assertEquals(thread, Thread.currentThread()) 42 } - assertThat(c.await(), IsEqual(42)) + assertEquals(42, c.await()) pool.shutdown() } @@ -38,7 +37,7 @@ class DelayJvmTest : TestBase() { delay(100) 42 } - assertThat(c.await(), IsEqual(42)) + assertEquals(42, c.await()) } @Test diff --git a/kotlinx-coroutines-core/jvm/test/DispatcherKeyTest.kt b/kotlinx-coroutines-core/jvm/test/DispatcherKeyTest.kt new file mode 100644 index 0000000000..e2d8ffaa47 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/DispatcherKeyTest.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2016-2020 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 kotlin.coroutines.* +import kotlin.test.* + +@UseExperimental(ExperimentalStdlibApi::class) +class DispatcherKeyTest : TestBase() { + + companion object CustomInterceptor : AbstractCoroutineContextElement(ContinuationInterceptor), + ContinuationInterceptor { + override fun interceptContinuation(continuation: Continuation): Continuation { + return continuation + } + } + + private val name = CoroutineName("test") + + @Test + fun testDispatcher() { + val context = name + CustomInterceptor + assertNull(context[CoroutineDispatcher]) + assertSame(CustomInterceptor, context[ContinuationInterceptor]) + + val updated = context + Dispatchers.Main + val result: CoroutineDispatcher? = updated[CoroutineDispatcher] + assertSame(Dispatchers.Main, result) + assertSame(Dispatchers.Main, updated[ContinuationInterceptor]) + assertEquals(name, updated.minusKey(CoroutineDispatcher)) + assertEquals(name, updated.minusKey(ContinuationInterceptor)) + } + + @Test + fun testExecutorCoroutineDispatcher() { + val context = name + CustomInterceptor + assertNull(context[ExecutorCoroutineDispatcher]) + val updated = context + Dispatchers.Main + assertNull(updated[ExecutorCoroutineDispatcher]) + val executor = Dispatchers.Default + val updated2 = updated + executor + assertSame(Dispatchers.Default, updated2[ContinuationInterceptor]) + assertSame(Dispatchers.Default, updated2[CoroutineDispatcher]) + assertSame(Dispatchers.Default as ExecutorCoroutineDispatcher, updated2[ExecutorCoroutineDispatcher]) + assertEquals(name, updated2.minusKey(ContinuationInterceptor)) + assertEquals(name, updated2.minusKey(CoroutineDispatcher)) + assertEquals(name, updated2.minusKey(ExecutorCoroutineDispatcher)) + } +} diff --git a/kotlinx-coroutines-core/jvm/test/ExecutorRule.kt b/kotlinx-coroutines-core/jvm/test/ExecutorRule.kt new file mode 100644 index 0000000000..e9cf6bcfc3 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/ExecutorRule.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines + +import org.junit.rules.* +import org.junit.runner.* +import org.junit.runners.model.* +import java.util.concurrent.* +import kotlin.coroutines.* + +class ExecutorRule(private val numberOfThreads: Int) : TestRule, ExecutorCoroutineDispatcher() { + + private var _executor: ExecutorCoroutineDispatcher? = null + override val executor: Executor + get() = _executor?.executor ?: error("Executor is not initialized") + + override fun apply(base: Statement, description: Description): Statement { + return object : Statement() { + override fun evaluate() { + val threadPrefix = description.className.substringAfterLast(".") + "#" + description.methodName + _executor = newFixedThreadPoolContext(numberOfThreads, threadPrefix) + ignoreLostThreads(threadPrefix) + try { + return base.evaluate() + } finally { + val service = executor as ExecutorService + service.shutdown() + if (!service.awaitTermination(10, TimeUnit.SECONDS)) { + error("Test $description timed out") + } + } + } + } + } + + override fun dispatch(context: CoroutineContext, block: Runnable) { + _executor?.dispatch(context, block) ?: error("Executor is not initialized") + } + + override fun close() { + error("Cannot be closed manually") + } +} diff --git a/kotlinx-coroutines-core/jvm/test/ExecutorsTest.kt b/kotlinx-coroutines-core/jvm/test/ExecutorsTest.kt index 2cf4361867..033b9b7bc9 100644 --- a/kotlinx-coroutines-core/jvm/test/ExecutorsTest.kt +++ b/kotlinx-coroutines-core/jvm/test/ExecutorsTest.kt @@ -4,10 +4,10 @@ package kotlinx.coroutines -import org.junit.* -import org.junit.Assert.* +import org.junit.Test import java.util.concurrent.* import kotlin.coroutines.* +import kotlin.test.* class ExecutorsTest : TestBase() { private fun checkThreadName(prefix: String) { diff --git a/kotlinx-coroutines-core/jvm/test/FieldWalker.kt b/kotlinx-coroutines-core/jvm/test/FieldWalker.kt index bb8b855498..12fd4dea04 100644 --- a/kotlinx-coroutines-core/jvm/test/FieldWalker.kt +++ b/kotlinx-coroutines-core/jvm/test/FieldWalker.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines @@ -7,109 +7,148 @@ package kotlinx.coroutines import java.lang.reflect.* import java.util.* import java.util.Collections.* +import java.util.concurrent.atomic.* import kotlin.collections.ArrayList +import kotlin.test.* object FieldWalker { + sealed class Ref { + object RootRef : Ref() + class FieldRef(val parent: Any, val name: String) : Ref() + class ArrayRef(val parent: Any, val index: Int) : Ref() + } + + private val fieldsCache = HashMap, List>() + + init { + // excluded/terminal classes (don't walk them) + fieldsCache += listOf(Any::class, String::class, Thread::class, Throwable::class) + .map { it.java } + .associateWith { emptyList() } + } /* * Reflectively starts to walk through object graph and returns identity set of all reachable objects. + * Use [walkRefs] if you need a path from root for debugging. + */ + public fun walk(root: Any?): Set = walkRefs(root).keys + + public fun assertReachableCount(expected: Int, root: Any?, predicate: (Any) -> Boolean) { + val visited = walkRefs(root) + val actual = visited.keys.filter(predicate) + if (actual.size != expected) { + val textDump = actual.joinToString("") { "\n\t" + showPath(it, visited) } + assertEquals( + expected, actual.size, + "Unexpected number objects. Expected $expected, found ${actual.size}$textDump" + ) + } + } + + /* + * Reflectively starts to walk through object graph and map to all the reached object to their path + * in from root. Use [showPath] do display a path if needed. */ - public fun walk(root: Any): Set { - val result = newSetFromMap(IdentityHashMap()) - result.add(root) + private fun walkRefs(root: Any?): Map { + val visited = IdentityHashMap() + if (root == null) return visited + visited[root] = Ref.RootRef val stack = ArrayDeque() stack.addLast(root) while (stack.isNotEmpty()) { val element = stack.removeLast() - val type = element.javaClass - type.visit(element, result, stack) + try { + visit(element, visited, stack) + } catch (e: Exception) { + error("Failed to visit element ${showPath(element, visited)}: $e") + } } - return result + return visited } - private fun Class<*>.visit( - element: Any, - result: MutableSet, - stack: ArrayDeque - ) { - val fields = fields() - fields.forEach { - it.isAccessible = true - val value = it.get(element) ?: return@forEach - if (result.add(value)) { - stack.addLast(value) + private fun showPath(element: Any, visited: Map): String { + val path = ArrayList() + var cur = element + while (true) { + val ref = visited.getValue(cur) + if (ref is Ref.RootRef) break + when (ref) { + is Ref.FieldRef -> { + cur = ref.parent + path += ".${ref.name}" + } + is Ref.ArrayRef -> { + cur = ref.parent + path += "[${ref.index}]" + } } } + path.reverse() + return path.joinToString("") + } - if (isArray && !componentType.isPrimitive) { - val array = element as Array - array.filterNotNull().forEach { - if (result.add(it)) { - stack.addLast(it) + private fun visit(element: Any, visited: IdentityHashMap, stack: ArrayDeque) { + val type = element.javaClass + when { + // Special code for arrays + type.isArray && !type.componentType.isPrimitive -> { + @Suppress("UNCHECKED_CAST") + val array = element as Array + array.forEachIndexed { index, value -> + push(value, visited, stack) { Ref.ArrayRef(element, index) } + } + } + // Special code for platform types that cannot be reflectively accessed on modern JDKs + type.name.startsWith("java.") && element is Collection<*> -> { + element.forEachIndexed { index, value -> + push(value, visited, stack) { Ref.ArrayRef(element, index) } + } + } + type.name.startsWith("java.") && element is Map<*, *> -> { + push(element.keys, visited, stack) { Ref.FieldRef(element, "keys") } + push(element.values, visited, stack) { Ref.FieldRef(element, "values") } + } + element is AtomicReference<*> -> { + push(element.get(), visited, stack) { Ref.FieldRef(element, "value") } + } + // All the other classes are reflectively scanned + else -> fields(type).forEach { field -> + push(field.get(element), visited, stack) { Ref.FieldRef(element, field.name) } + // special case to scan Throwable cause (cannot get it reflectively) + if (element is Throwable) { + push(element.cause, visited, stack) { Ref.FieldRef(element, "cause") } } } } } - private fun Class<*>.fields(): List { + private inline fun push(value: Any?, visited: IdentityHashMap, stack: ArrayDeque, ref: () -> Ref) { + if (value != null && !visited.containsKey(value)) { + visited[value] = ref() + stack.addLast(value) + } + } + + private fun fields(type0: Class<*>): List { + fieldsCache[type0]?.let { return it } val result = ArrayList() - var type = this - while (type != Any::class.java) { + var type = type0 + while (true) { val fields = type.declaredFields.filter { !it.type.isPrimitive && !Modifier.isStatic(it.modifiers) && !(it.type.isArray && it.type.componentType.isPrimitive) } + fields.forEach { it.isAccessible = true } // make them all accessible result.addAll(fields) type = type.superclass - } - - return result - } - - // Debugging-only - @Suppress("UNUSED") - fun printPath(from: Any, to: Any) { - val pathNodes = ArrayList() - val visited = newSetFromMap(IdentityHashMap()) - visited.add(from) - if (findPath(from, to, visited, pathNodes)) { - pathNodes.reverse() - println(pathNodes.joinToString(" -> ", from.javaClass.simpleName + " -> ", "-> " + to.javaClass.simpleName)) - } else { - println("Path from $from to $to not found") - } - } - - private fun findPath(from: Any, to: Any, visited: MutableSet, pathNodes: MutableList): Boolean { - if (from === to) { - return true - } - - val type = from.javaClass - if (type.isArray) { - if (type.componentType.isPrimitive) return false - val array = from as Array - array.filterNotNull().forEach { - if (findPath(it, to, visited, pathNodes)) { - return true - } + val superFields = fieldsCache[type] // will stop at Any anyway + if (superFields != null) { + result.addAll(superFields) + break } - return false } - - val fields = type.fields() - fields.forEach { - it.isAccessible = true - val value = it.get(from) ?: return@forEach - if (!visited.add(value)) return@forEach - val found = findPath(value, to, visited, pathNodes) - if (found) { - pathNodes += from.javaClass.simpleName + ":" + it.name - return true - } - } - - return false + fieldsCache[type0] = result + return result } } diff --git a/kotlinx-coroutines-core/jvm/test/LCStressOptionsDefault.kt b/kotlinx-coroutines-core/jvm/test/LCStressOptionsDefault.kt new file mode 100644 index 0000000000..62ded9f969 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/LCStressOptionsDefault.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ +package kotlinx.coroutines + +import org.jetbrains.kotlinx.lincheck.* +import org.jetbrains.kotlinx.lincheck.strategy.stress.* +import kotlin.reflect.* + +class LCStressOptionsDefault : StressOptions() { + init { + iterations(100 * stressTestMultiplierCbrt) + invocationsPerIteration(1000 * stressTestMultiplierCbrt) + actorsBefore(if (isStressTest) 3 else 0) + threads(3) + actorsPerThread(if (isStressTest) 3 else 2) + } +} + +fun Options<*,*>.check(testClass: KClass<*>) = LinChecker.check(testClass.java, this) \ No newline at end of file diff --git a/kotlinx-coroutines-core/jvm/test/ReusableCancellableContinuationTest.kt b/kotlinx-coroutines-core/jvm/test/ReusableCancellableContinuationTest.kt index b324e7ed3a..997f746118 100644 --- a/kotlinx-coroutines-core/jvm/test/ReusableCancellableContinuationTest.kt +++ b/kotlinx-coroutines-core/jvm/test/ReusableCancellableContinuationTest.kt @@ -76,7 +76,7 @@ class ReusableCancellableContinuationTest : TestBase() { expect(4) ensureActive() // Verify child was bound - assertNotNull(FieldWalker.walk(coroutineContext[Job]!!).single { it === continuation }) + FieldWalker.assertReachableCount(1, coroutineContext[Job]) { it === continuation } suspendAtomicCancellableCoroutineReusable { expect(5) coroutineContext[Job]!!.cancel() @@ -97,7 +97,7 @@ class ReusableCancellableContinuationTest : TestBase() { cont = it } ensureActive() - assertTrue { FieldWalker.walk(coroutineContext[Job]!!).contains(cont!!) } + assertTrue { FieldWalker.walk(coroutineContext[Job]).contains(cont!!) } finish(2) } @@ -112,7 +112,7 @@ class ReusableCancellableContinuationTest : TestBase() { cont = it } ensureActive() - assertFalse { FieldWalker.walk(coroutineContext[Job]!!).contains(cont!!) } + FieldWalker.assertReachableCount(0, coroutineContext[Job]) { it === cont } finish(2) } @@ -127,7 +127,7 @@ class ReusableCancellableContinuationTest : TestBase() { } expectUnreached() } catch (e: CancellationException) { - assertFalse { FieldWalker.walk(coroutineContext[Job]!!).contains(cont!!) } + FieldWalker.assertReachableCount(0, coroutineContext[Job]) { it === cont } finish(2) } } @@ -148,19 +148,19 @@ class ReusableCancellableContinuationTest : TestBase() { expect(4) ensureActive() // Verify child was bound - assertEquals(1, FieldWalker.walk(currentJob).count { it is CancellableContinuation<*> }) + FieldWalker.assertReachableCount(1, currentJob) { it is CancellableContinuation<*> } currentJob.cancel() assertFalse(isActive) // Child detached - assertEquals(0, FieldWalker.walk(currentJob).count { it is CancellableContinuation<*> }) + FieldWalker.assertReachableCount(0, currentJob) { it is CancellableContinuation<*> } suspendAtomicCancellableCoroutineReusable { it.resume(Unit) } suspendAtomicCancellableCoroutineReusable { it.resume(Unit) } - assertEquals(0, FieldWalker.walk(currentJob).count { it is CancellableContinuation<*> }) + FieldWalker.assertReachableCount(0, currentJob) { it is CancellableContinuation<*> } try { suspendAtomicCancellableCoroutineReusable {} } catch (e: CancellationException) { - assertEquals(0, FieldWalker.walk(currentJob).count { it is CancellableContinuation<*> }) + FieldWalker.assertReachableCount(0, currentJob) { it is CancellableContinuation<*> } finish(5) } } @@ -184,12 +184,12 @@ class ReusableCancellableContinuationTest : TestBase() { expect(2) val job = coroutineContext[Job]!! // 1 for reusable CC, another one for outer joiner - assertEquals(2, FieldWalker.walk(job).count { it is CancellableContinuation<*> }) + FieldWalker.assertReachableCount(2, job) { it is CancellableContinuation<*> } } expect(1) receiver.join() // Reference should be claimed at this point - assertEquals(0, FieldWalker.walk(receiver).count { it is CancellableContinuation<*> }) + FieldWalker.assertReachableCount(0, receiver) { it is CancellableContinuation<*> } finish(3) } } diff --git a/kotlinx-coroutines-core/jvm/test/TestBase.kt b/kotlinx-coroutines-core/jvm/test/TestBase.kt index cc41ff3f62..bf462cc78f 100644 --- a/kotlinx-coroutines-core/jvm/test/TestBase.kt +++ b/kotlinx-coroutines-core/jvm/test/TestBase.kt @@ -7,9 +7,11 @@ package kotlinx.coroutines import kotlinx.coroutines.internal.* import kotlinx.coroutines.scheduling.* import org.junit.* +import java.lang.Math.* import java.util.* import java.util.concurrent.atomic.* import kotlin.coroutines.* +import kotlin.math.* import kotlin.test.* private val VERBOSE = systemProp("test.verbose", false) @@ -26,19 +28,21 @@ public val stressTestMultiplierSqrt = if (isStressTest) 5 else 1 */ public actual val stressTestMultiplier = stressTestMultiplierSqrt * stressTestMultiplierSqrt +public val stressTestMultiplierCbrt = cbrt(stressTestMultiplier.toDouble()).roundToInt() + /** * Base class for tests, so that tests for predictable scheduling of actions in multiple coroutines sharing a single * thread can be written. Use it like this: * * ``` - * class MyTest { + * class MyTest : TestBase() { * @Test - * fun testSomething() = runBlocking { // run in the context of the main thread + * fun testSomething() = runBlocking { // run in the context of the main thread * expect(1) // initiate action counter - * val job = launch(context) { // use the context of the main thread + * launch { // use the context of the main thread * expect(3) // the body of this coroutine in going to be executed in the 3rd step * } - * expect(2) // launch just scheduled coroutine for exectuion later, so this line is executed second + * expect(2) // launch just scheduled coroutine for execution later, so this line is executed second * yield() // yield main thread to the launched job * finish(4) // fourth step is the last one. `finish` must be invoked or test fails * } diff --git a/kotlinx-coroutines-core/jvm/test/WithTimeoutOrNullJvmTest.kt b/kotlinx-coroutines-core/jvm/test/WithTimeoutOrNullJvmTest.kt index 5c55bd0aa6..377fcf462e 100644 --- a/kotlinx-coroutines-core/jvm/test/WithTimeoutOrNullJvmTest.kt +++ b/kotlinx-coroutines-core/jvm/test/WithTimeoutOrNullJvmTest.kt @@ -19,7 +19,7 @@ class WithTimeoutOrNullJvmTest : TestBase() { expectUnreached() // should not be reached, because it is outer timeout } // outer timeout results in null - assertEquals(null, result) + assertNull(result) } @Test diff --git a/kotlinx-coroutines-core/jvm/test/WithTimeoutOrNullThreadDispatchTest.kt b/kotlinx-coroutines-core/jvm/test/WithTimeoutOrNullThreadDispatchTest.kt index 5d8c0221a7..ea1ba1a9bb 100644 --- a/kotlinx-coroutines-core/jvm/test/WithTimeoutOrNullThreadDispatchTest.kt +++ b/kotlinx-coroutines-core/jvm/test/WithTimeoutOrNullThreadDispatchTest.kt @@ -74,7 +74,7 @@ class WithTimeoutOrNullThreadDispatchTest : TestBase() { } } assertEquals(thread, Thread.currentThread()) - assertEquals(null, result) + assertNull(result) expect(5) } finish(6) diff --git a/kotlinx-coroutines-core/jvm/test/channels/ActorLazyTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ActorLazyTest.kt index 1ec96ee5ab..ae95e694cd 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ActorLazyTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/ActorLazyTest.kt @@ -5,9 +5,8 @@ package kotlinx.coroutines.channels import kotlinx.coroutines.* -import org.hamcrest.core.* -import org.junit.* -import org.junit.Assert.* +import org.junit.Test +import kotlin.test.* class ActorLazyTest : TestBase() { @Test @@ -17,22 +16,22 @@ class ActorLazyTest : TestBase() { expect(5) } actor as Job // type assertion - assertThat(actor.isActive, IsEqual(false)) - assertThat(actor.isCompleted, IsEqual(false)) - assertThat(actor.isClosedForSend, IsEqual(false)) + assertFalse(actor.isActive) + assertFalse(actor.isCompleted) + assertFalse(actor.isClosedForSend) expect(2) yield() // to actor code --> nothing happens (not started!) - assertThat(actor.isActive, IsEqual(false)) - assertThat(actor.isCompleted, IsEqual(false)) - assertThat(actor.isClosedForSend, IsEqual(false)) + assertFalse(actor.isActive) + assertFalse(actor.isCompleted) + assertFalse(actor.isClosedForSend) expect(3) // start actor explicitly actor.start() expect(4) yield() // to started actor - assertThat(actor.isActive, IsEqual(false)) - assertThat(actor.isCompleted, IsEqual(true)) - assertThat(actor.isClosedForSend, IsEqual(true)) + assertFalse(actor.isActive) + assertTrue(actor.isCompleted) + assertTrue(actor.isClosedForSend) finish(6) } @@ -41,24 +40,24 @@ class ActorLazyTest : TestBase() { expect(1) val actor = actor(start = CoroutineStart.LAZY) { expect(4) - assertThat(receive(), IsEqual("OK")) + assertEquals("OK", receive()) expect(5) } actor as Job // type assertion - assertThat(actor.isActive, IsEqual(false)) - assertThat(actor.isCompleted, IsEqual(false)) - assertThat(actor.isClosedForSend, IsEqual(false)) + assertFalse(actor.isActive) + assertFalse(actor.isCompleted) + assertFalse(actor.isClosedForSend) expect(2) yield() // to actor code --> nothing happens (not started!) - assertThat(actor.isActive, IsEqual(false)) - assertThat(actor.isCompleted, IsEqual(false)) - assertThat(actor.isClosedForSend, IsEqual(false)) + assertFalse(actor.isActive) + assertFalse(actor.isCompleted) + assertFalse(actor.isClosedForSend) expect(3) // send message to actor --> should start it actor.send("OK") - assertThat(actor.isActive, IsEqual(false)) - assertThat(actor.isCompleted, IsEqual(true)) - assertThat(actor.isClosedForSend, IsEqual(true)) + assertFalse(actor.isActive) + assertTrue(actor.isCompleted) + assertTrue(actor.isClosedForSend) finish(6) } diff --git a/kotlinx-coroutines-core/jvm/test/channels/ActorTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ActorTest.kt index 18349dddb6..bdca5039d2 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ActorTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/ActorTest.kt @@ -5,12 +5,11 @@ package kotlinx.coroutines.channels import kotlinx.coroutines.* -import org.hamcrest.core.* -import org.junit.* -import org.junit.Assert.* +import org.junit.Test import org.junit.runner.* import org.junit.runners.* import java.io.* +import kotlin.test.* @RunWith(Parameterized::class) class ActorTest(private val capacity: Int) : TestBase() { @@ -28,14 +27,14 @@ class ActorTest(private val capacity: Int) : TestBase() { expect(3) } actor as Job // type assertion - assertThat(actor.isActive, IsEqual(true)) - assertThat(actor.isCompleted, IsEqual(false)) - assertThat(actor.isClosedForSend, IsEqual(false)) + assertTrue(actor.isActive) + assertFalse(actor.isCompleted) + assertFalse(actor.isClosedForSend) expect(2) yield() // to actor code - assertThat(actor.isActive, IsEqual(false)) - assertThat(actor.isCompleted, IsEqual(true)) - assertThat(actor.isClosedForSend, IsEqual(true)) + assertFalse(actor.isActive) + assertTrue(actor.isCompleted) + assertTrue(actor.isClosedForSend) finish(4) } @@ -44,26 +43,26 @@ class ActorTest(private val capacity: Int) : TestBase() { expect(1) val actor = actor(capacity = capacity) { expect(3) - assertThat(receive(), IsEqual("OK")) + assertEquals("OK", receive()) expect(6) } actor as Job // type assertion - assertThat(actor.isActive, IsEqual(true)) - assertThat(actor.isCompleted, IsEqual(false)) - assertThat(actor.isClosedForSend, IsEqual(false)) + assertTrue(actor.isActive) + assertFalse(actor.isCompleted) + assertFalse(actor.isClosedForSend) expect(2) yield() // to actor code - assertThat(actor.isActive, IsEqual(true)) - assertThat(actor.isCompleted, IsEqual(false)) - assertThat(actor.isClosedForSend, IsEqual(false)) + assertTrue(actor.isActive) + assertFalse(actor.isCompleted) + assertFalse(actor.isClosedForSend) expect(4) // send message to actor actor.send("OK") expect(5) yield() // to actor code - assertThat(actor.isActive, IsEqual(false)) - assertThat(actor.isCompleted, IsEqual(true)) - assertThat(actor.isClosedForSend, IsEqual(true)) + assertFalse(actor.isActive) + assertTrue(actor.isCompleted) + assertTrue(actor.isClosedForSend) finish(7) } diff --git a/kotlinx-coroutines-core/jvm/test/channels/ChannelAtomicCancelStressTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ChannelAtomicCancelStressTest.kt index 5afac37c9c..6556888a0f 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ChannelAtomicCancelStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/ChannelAtomicCancelStressTest.kt @@ -6,12 +6,13 @@ package kotlinx.coroutines.channels import kotlinx.coroutines.* import kotlinx.coroutines.selects.* -import org.junit.* -import org.junit.Assert.* +import org.junit.After +import org.junit.Test import org.junit.runner.* import org.junit.runners.* import kotlin.random.Random import java.util.concurrent.atomic.* +import kotlin.test.* /** * Tests cancel atomicity for channel send & receive operations, including their select versions. diff --git a/kotlinx-coroutines-core/jvm/test/channels/ChannelLFStressTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ChannelLFStressTest.kt index 75e34e5a5e..256ef62132 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ChannelLFStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/ChannelLFStressTest.kt @@ -41,14 +41,6 @@ class ChannelLFStressTest : TestBase() { checkAllReceived() } - @Test - fun testConflatedLockFreedom() { - // This test does not really verify that all sent elements were received - // and checks only LF property - channel = Channel(Channel.CONFLATED) - performLockFreedomTest() - } - private fun performLockFreedomTest() { env.onCompletion { // We must cancel the channel to abort both senders & receivers diff --git a/kotlinx-coroutines-core/jvm/test/channels/ChannelSelectStressTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ChannelSelectStressTest.kt index 0fa64276df..a44ff6cdc6 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ChannelSelectStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/ChannelSelectStressTest.kt @@ -7,14 +7,14 @@ package kotlinx.coroutines.channels import kotlinx.atomicfu.* import kotlinx.coroutines.* import kotlinx.coroutines.selects.* -import org.junit.* -import org.junit.Assert.* +import org.junit.After +import org.junit.Test import java.util.concurrent.atomic.AtomicLongArray +import kotlin.test.* class ChannelSelectStressTest : TestBase() { private val pairedCoroutines = 3 private val dispatcher = newFixedThreadPoolContext(pairedCoroutines * 2, "ChannelSelectStressTest") - private val scope = CoroutineScope(dispatcher) private val elementsToSend = 20_000 * Long.SIZE_BITS * stressTestMultiplier private val sent = atomic(0) private val received = atomic(0) @@ -28,19 +28,27 @@ class ChannelSelectStressTest : TestBase() { @Test fun testAtomicCancelStress() = runTest { - repeat(pairedCoroutines) { launchSender() } - repeat(pairedCoroutines) { launchReceiver() } - val job = scope.coroutineContext[Job] as CompletableJob - job.complete() - job.join() - + withContext(dispatcher) { + repeat(pairedCoroutines) { launchSender() } + repeat(pairedCoroutines) { launchReceiver() } + } + val missing = ArrayList() for (i in 0 until receivedArray.length()) { - assertEquals("Missing element detected", 0L.inv(), receivedArray[i]) + val bits = receivedArray[i] + if (bits != 0L.inv()) { + for (j in 0 until Long.SIZE_BITS) { + val mask = 1L shl j + if (bits and mask == 0L) missing += i * Long.SIZE_BITS + j + } + } + } + if (missing.isNotEmpty()) { + fail("Missed ${missing.size} out of $elementsToSend: $missing") } } - private fun launchSender() { - scope.launch { + private fun CoroutineScope.launchSender() { + launch { while (sent.value < elementsToSend) { val element = sent.getAndIncrement() if (element >= elementsToSend) break @@ -50,8 +58,8 @@ class ChannelSelectStressTest : TestBase() { } } - private fun launchReceiver() { - scope.launch { + private fun CoroutineScope.launchReceiver() { + launch { while (received.value != elementsToSend) { val element = select { channel.onReceive { it } } received.incrementAndGet() diff --git a/kotlinx-coroutines-core/jvm/test/channels/ChannelSendReceiveStressTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ChannelSendReceiveStressTest.kt index 1bd6060a8c..00c5a6090f 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ChannelSendReceiveStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/ChannelSendReceiveStressTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels @@ -7,10 +7,11 @@ package kotlinx.coroutines.channels import kotlinx.coroutines.* import kotlinx.coroutines.selects.* import org.junit.* -import org.junit.Assert.* +import org.junit.Test import org.junit.runner.* import org.junit.runners.* import java.util.concurrent.atomic.* +import kotlin.test.* @RunWith(Parameterized::class) class ChannelSendReceiveStressTest( @@ -43,12 +44,20 @@ class ChannelSendReceiveStressTest( private val receivedTotal = AtomicInteger() private val receivedBy = IntArray(nReceivers) + private val pool = + newFixedThreadPoolContext(nSenders + nReceivers, "ChannelSendReceiveStressTest") + + @After + fun tearDown() { + pool.close() + } + @Test fun testSendReceiveStress() = runBlocking { println("--- ChannelSendReceiveStressTest $kind with nSenders=$nSenders, nReceivers=$nReceivers") val receivers = List(nReceivers) { receiverIndex -> // different event receivers use different code - launch(Dispatchers.Default + CoroutineName("receiver$receiverIndex")) { + launch(pool + CoroutineName("receiver$receiverIndex")) { when (receiverIndex % 5) { 0 -> doReceive(receiverIndex) 1 -> doReceiveOrNull(receiverIndex) @@ -60,7 +69,7 @@ class ChannelSendReceiveStressTest( } } val senders = List(nSenders) { senderIndex -> - launch(Dispatchers.Default + CoroutineName("sender$senderIndex")) { + launch(pool + CoroutineName("sender$senderIndex")) { when (senderIndex % 2) { 0 -> doSend(senderIndex) 1 -> doSendSelect(senderIndex) @@ -101,7 +110,7 @@ class ChannelSendReceiveStressTest( assertEquals(nEvents, sentTotal.get()) if (!kind.isConflated) assertEquals(nEvents, receivedTotal.get()) repeat(nReceivers) { receiveIndex -> - assertTrue("Each receiver should have received something", receivedBy[receiveIndex] > 0) + assertTrue(receivedBy[receiveIndex] > 0, "Each receiver should have received something") } } diff --git a/kotlinx-coroutines-core/jvm/test/channels/ChannelsConsumeTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ChannelsConsumeTest.kt index d9ef22b10e..cb19b36a13 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ChannelsConsumeTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/ChannelsConsumeTest.kt @@ -76,7 +76,7 @@ class ChannelsConsumeTest : TestBase() { assertEquals(4, elementAtOrNull(3)) } checkTerminal { - assertEquals(null, elementAtOrNull(10)) + assertNull(elementAtOrNull(10)) } } @@ -124,7 +124,7 @@ class ChannelsConsumeTest : TestBase() { assertEquals(3, firstOrNull { it % 3 == 0 }) } checkTerminal { - assertEquals(null, firstOrNull { it > 10 }) + assertNull(firstOrNull { it > 10 }) } } @@ -195,7 +195,7 @@ class ChannelsConsumeTest : TestBase() { assertEquals(9, lastOrNull { it % 3 == 0 }) } checkTerminal { - assertEquals(null, lastOrNull { it > 10 }) + assertNull(lastOrNull { it > 10 }) } } @@ -222,7 +222,7 @@ class ChannelsConsumeTest : TestBase() { @Test fun testSingleOrNull() { checkTerminal { - assertEquals(null, singleOrNull()) + assertNull(singleOrNull()) } } @@ -232,10 +232,10 @@ class ChannelsConsumeTest : TestBase() { assertEquals(7, singleOrNull { it % 7 == 0 }) } checkTerminal { - assertEquals(null, singleOrNull { it % 3 == 0 }) + assertNull(singleOrNull { it % 3 == 0 }) } checkTerminal { - assertEquals(null, singleOrNull { it > 10 }) + assertNull(singleOrNull { it > 10 }) } } @@ -898,7 +898,7 @@ class ChannelsConsumeTest : TestBase() { res.cancel() } else { // then check that result is closed - assertEquals(null, res.receiveOrNull(), "Result has unexpected values") + assertNull(res.receiveOrNull(), "Result has unexpected values") } src } diff --git a/kotlinx-coroutines-core/jvm/test/channels/ConflatedBroadcastChannelNotifyStressTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ConflatedBroadcastChannelNotifyStressTest.kt index 4d09e3751e..eb7be57500 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ConflatedBroadcastChannelNotifyStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/ConflatedBroadcastChannelNotifyStressTest.kt @@ -5,10 +5,9 @@ package kotlinx.coroutines.channels import kotlinx.coroutines.* -import org.hamcrest.MatcherAssert.* -import org.hamcrest.core.* -import org.junit.* +import org.junit.Test import java.util.concurrent.atomic.* +import kotlin.test.* class ConflatedBroadcastChannelNotifyStressTest : TestBase() { private val nSenders = 2 @@ -76,9 +75,9 @@ class ConflatedBroadcastChannelNotifyStressTest : TestBase() { println("Completed successfully ${receiversCompleted.get()} receiver coroutines") println(" Sent ${sentTotal.get()} events") println(" Received ${receivedTotal.get()} events") - assertThat(sendersCompleted.get(), IsEqual(nSenders)) - assertThat(receiversCompleted.get(), IsEqual(nReceivers)) - assertThat(sentTotal.get(), IsEqual(nEvents)) + assertEquals(nSenders, sendersCompleted.get()) + assertEquals(nReceivers, receiversCompleted.get()) + assertEquals(nEvents, sentTotal.get()) } private suspend fun waitForEvent(): Int = diff --git a/kotlinx-coroutines-core/jvm/test/channels/ProduceConsumeJvmTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ProduceConsumeJvmTest.kt index 1ae62d811b..61c6635e8a 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ProduceConsumeJvmTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/ProduceConsumeJvmTest.kt @@ -5,10 +5,10 @@ package kotlinx.coroutines.channels import kotlinx.coroutines.* -import org.junit.* -import org.junit.Assert.* +import org.junit.Test import org.junit.runner.* import org.junit.runners.* +import kotlin.test.* @RunWith(Parameterized::class) class ProduceConsumeJvmTest( diff --git a/kotlinx-coroutines-core/jvm/test/channels/SimpleSendReceiveJvmTest.kt b/kotlinx-coroutines-core/jvm/test/channels/SimpleSendReceiveJvmTest.kt index 54023a3e23..07c431bb4d 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/SimpleSendReceiveJvmTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/SimpleSendReceiveJvmTest.kt @@ -5,12 +5,10 @@ package kotlinx.coroutines.channels import kotlinx.coroutines.* -import org.hamcrest.core.* -import org.junit.* -import org.junit.Assert.* +import org.junit.Test import org.junit.runner.* import org.junit.runners.* -import kotlin.coroutines.* +import kotlin.test.* @RunWith(Parameterized::class) class SimpleSendReceiveJvmTest( @@ -42,14 +40,14 @@ class SimpleSendReceiveJvmTest( var expected = 0 for (x in channel) { if (!kind.isConflated) { - assertThat(x, IsEqual(expected++)) + assertEquals(expected++, x) } else { assertTrue(x >= expected) expected = x + 1 } } if (!kind.isConflated) { - assertThat(expected, IsEqual(n)) + assertEquals(n, expected) } } } diff --git a/kotlinx-coroutines-core/jvm/test/flow/CallbackFlowTest.kt b/kotlinx-coroutines-core/jvm/test/flow/CallbackFlowTest.kt index f71040343d..e3db2626ce 100644 --- a/kotlinx-coroutines-core/jvm/test/flow/CallbackFlowTest.kt +++ b/kotlinx-coroutines-core/jvm/test/flow/CallbackFlowTest.kt @@ -39,7 +39,7 @@ class CallbackFlowTest : TestBase() { runCatching { it.offer(++i) } } - val flow = channelFlow { + val flow = callbackFlow { api.start(channel) awaitClose { api.stop() @@ -118,7 +118,7 @@ class CallbackFlowTest : TestBase() { } } - private fun Flow.merge(other: Flow): Flow = callbackFlow { + private fun Flow.merge(other: Flow): Flow = channelFlow { launch { collect { send(it) } } diff --git a/kotlinx-coroutines-core/jvm/test/flow/ConsumeAsFlowLeakTest.kt b/kotlinx-coroutines-core/jvm/test/flow/ConsumeAsFlowLeakTest.kt index 3fcceaf1fb..c037be1e6d 100644 --- a/kotlinx-coroutines-core/jvm/test/flow/ConsumeAsFlowLeakTest.kt +++ b/kotlinx-coroutines-core/jvm/test/flow/ConsumeAsFlowLeakTest.kt @@ -41,7 +41,7 @@ class ConsumeAsFlowLeakTest : TestBase() { if (shouldSuspendOnSend) yield() channel.send(second) yield() - assertEquals(0, FieldWalker.walk(channel).count { it === second }) + FieldWalker.assertReachableCount(0, channel) { it === second } finish(6) job.cancelAndJoin() } diff --git a/kotlinx-coroutines-core/jvm/test/flow/ExceptionTransparencyTest.kt b/kotlinx-coroutines-core/jvm/test/flow/ExceptionTransparencyTest.kt new file mode 100644 index 0000000000..aca9eb9a1f --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/flow/ExceptionTransparencyTest.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2016-2020 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 kotlin.test.* + +class ExceptionTransparencyTest : TestBase() { + + @Test + fun testViolation() = runTest { + val flow = flow { + try { + expect(1) + emit(1) + expectUnreached() + } catch (e: CancellationException) { + expect(3) + emit(2) + } + }.take(1) + + assertFailsWith { flow.collect { expect(2) } } + finish(4) + } + + @Test + fun testViolationResumeWith() = runTest { + val flow = flow { + try { + expect(1) + emit(1) + yield() + expectUnreached() + } catch (e: CancellationException) { + expect(3) + emit(2) + } + }.take(1) + + assertFailsWith { + flow.collect { + yield() + expect(2) + } + } + finish(4) + } + + @Test + fun testViolationAfterInvariantVariation() = runTest { + val flow = flow { + coroutineScope { + try { + expect(1) + launch { + expect(2) + emit(1) + }.join() + expectUnreached() + } catch (e: Throwable) { + try { + emit(2) + } catch (e: IllegalStateException) { + assertTrue { e.message!!.contains("exception transparency") } + emit(3) + } + } + } + } + val e = assertFailsWith { flow.collect { expectUnreached() } } + assertTrue { e.message!!.contains("channelFlow") } + finish(3) + } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-basic-01.kt b/kotlinx-coroutines-core/jvm/test/guide/example-basic-01.kt index dcb36af536..15e126149b 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-basic-01.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-basic-01.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.basic01 +// This file was automatically generated from basics.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleBasic01 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-basic-02.kt b/kotlinx-coroutines-core/jvm/test/guide/example-basic-02.kt index 4f1277d4a4..4f178ca6b3 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-basic-02.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-basic-02.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.basic02 +// This file was automatically generated from basics.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleBasic02 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-basic-02b.kt b/kotlinx-coroutines-core/jvm/test/guide/example-basic-02b.kt deleted file mode 100644 index a78840d5e1..0000000000 --- a/kotlinx-coroutines-core/jvm/test/guide/example-basic-02b.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.basic02b - -import kotlinx.coroutines.* - -fun main() = runBlocking { // start main coroutine - GlobalScope.launch { // launch a new coroutine in background and continue - delay(1000L) - println("World!") - } - println("Hello,") // main coroutine continues here immediately - delay(2000L) // delaying for 2 seconds to keep JVM alive -} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-basic-03.kt b/kotlinx-coroutines-core/jvm/test/guide/example-basic-03.kt index a35e848196..f80113c57f 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-basic-03.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-basic-03.kt @@ -1,17 +1,17 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.basic03 +// This file was automatically generated from basics.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleBasic03 import kotlinx.coroutines.* -fun main() = runBlocking { - val job = GlobalScope.launch { // launch a new coroutine and keep a reference to its Job +fun main() = runBlocking { // start main coroutine + GlobalScope.launch { // launch a new coroutine in background and continue delay(1000L) println("World!") } - println("Hello,") - job.join() // wait until child coroutine completes + println("Hello,") // main coroutine continues here immediately + delay(2000L) // delaying for 2 seconds to keep JVM alive } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-basic-03s.kt b/kotlinx-coroutines-core/jvm/test/guide/example-basic-03s.kt deleted file mode 100644 index 13cf679ab9..0000000000 --- a/kotlinx-coroutines-core/jvm/test/guide/example-basic-03s.kt +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.basic03s - -import kotlinx.coroutines.* - -fun main() = runBlocking { // this: CoroutineScope - launch { // launch a new coroutine in the scope of runBlocking - delay(1000L) - println("World!") - } - println("Hello,") -} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-basic-04.kt b/kotlinx-coroutines-core/jvm/test/guide/example-basic-04.kt index 3afa0fe5c7..33c928a008 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-basic-04.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-basic-04.kt @@ -1,27 +1,17 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.basic04 +// This file was automatically generated from basics.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleBasic04 import kotlinx.coroutines.* -fun main() = runBlocking { // this: CoroutineScope - launch { - delay(200L) - println("Task from runBlocking") +fun main() = runBlocking { + val job = GlobalScope.launch { // launch a new coroutine and keep a reference to its Job + delay(1000L) + println("World!") } - - coroutineScope { // Creates a coroutine scope - launch { - delay(500L) - println("Task from nested launch") - } - - delay(100L) - println("Task from coroutine scope") // This line will be printed before the nested launch - } - - println("Coroutine scope is over") // This line is not printed until the nested launch completes + println("Hello,") + job.join() // wait until child coroutine completes } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-basic-05.kt b/kotlinx-coroutines-core/jvm/test/guide/example-basic-05.kt index e6a911291d..52b490d2b7 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-basic-05.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-basic-05.kt @@ -1,19 +1,16 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.basic05 +// This file was automatically generated from basics.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleBasic05 import kotlinx.coroutines.* -fun main() = runBlocking { - launch { doWorld() } +fun main() = runBlocking { // this: CoroutineScope + launch { // launch a new coroutine in the scope of runBlocking + delay(1000L) + println("World!") + } println("Hello,") } - -// this is your first suspending function -suspend fun doWorld() { - delay(1000L) - println("World!") -} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-basic-05s.kt b/kotlinx-coroutines-core/jvm/test/guide/example-basic-05s.kt deleted file mode 100644 index 876f9a1aeb..0000000000 --- a/kotlinx-coroutines-core/jvm/test/guide/example-basic-05s.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.basic05s - -import kotlinx.coroutines.* - -fun main(args: Array) = runBlocking { - launchDoWorld() - println("Hello,") -} - -// this is your first suspending function -suspend fun launchDoWorld() = coroutineScope { - launch { - println("World!") - } -} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-basic-06.kt b/kotlinx-coroutines-core/jvm/test/guide/example-basic-06.kt index 60de941d49..a1b5bc5f23 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-basic-06.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-basic-06.kt @@ -1,17 +1,27 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.basic06 +// This file was automatically generated from basics.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleBasic06 import kotlinx.coroutines.* -fun main() = runBlocking { - repeat(100_000) { // launch a lot of coroutines +fun main() = runBlocking { // this: CoroutineScope + launch { + delay(200L) + println("Task from runBlocking") + } + + coroutineScope { // Creates a coroutine scope launch { - delay(1000L) - print(".") + delay(500L) + println("Task from nested launch") } + + delay(100L) + println("Task from coroutine scope") // This line will be printed before the nested launch } + + println("Coroutine scope is over") // This line is not printed until the nested launch completes } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-basic-07.kt b/kotlinx-coroutines-core/jvm/test/guide/example-basic-07.kt index 56e785fb7f..32c02b86e5 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-basic-07.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-basic-07.kt @@ -1,18 +1,19 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.basic07 +// This file was automatically generated from basics.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleBasic07 import kotlinx.coroutines.* fun main() = runBlocking { - GlobalScope.launch { - repeat(1000) { i -> - println("I'm sleeping $i ...") - delay(500L) - } - } - delay(1300L) // just quit after delay + launch { doWorld() } + println("Hello,") +} + +// this is your first suspending function +suspend fun doWorld() { + delay(1000L) + println("World!") } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-basic-08.kt b/kotlinx-coroutines-core/jvm/test/guide/example-basic-08.kt new file mode 100644 index 0000000000..ff11eb70d7 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-basic-08.kt @@ -0,0 +1,17 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from basics.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleBasic08 + +import kotlinx.coroutines.* + +fun main() = runBlocking { + repeat(100_000) { // launch a lot of coroutines + launch { + delay(1000L) + print(".") + } + } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-basic-09.kt b/kotlinx-coroutines-core/jvm/test/guide/example-basic-09.kt new file mode 100644 index 0000000000..9f998b5244 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-basic-09.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from basics.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleBasic09 + +import kotlinx.coroutines.* + +fun main() = runBlocking { + GlobalScope.launch { + repeat(1000) { i -> + println("I'm sleeping $i ...") + delay(500L) + } + } + delay(1300L) // just quit after delay +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-01.kt b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-01.kt index ebf5171e2e..a1b036d91f 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-01.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-01.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.cancel01 +// This file was automatically generated from cancellation-and-timeouts.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleCancel01 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-02.kt b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-02.kt index e3127b41ba..f6dec6e8f4 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-02.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-02.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.cancel02 +// This file was automatically generated from cancellation-and-timeouts.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleCancel02 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-03.kt b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-03.kt index d47ecd9dda..3daaf49b36 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-03.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-03.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.cancel03 +// This file was automatically generated from cancellation-and-timeouts.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleCancel03 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-04.kt b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-04.kt index 45c97851aa..b1b9a9bfd5 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-04.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-04.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.cancel04 +// This file was automatically generated from cancellation-and-timeouts.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleCancel04 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-05.kt b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-05.kt index 9f2cac1c23..9772ae5739 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-05.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-05.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.cancel05 +// This file was automatically generated from cancellation-and-timeouts.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleCancel05 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-06.kt b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-06.kt index f06d1187e4..e1afd057c4 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-06.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-06.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.cancel06 +// This file was automatically generated from cancellation-and-timeouts.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleCancel06 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-07.kt b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-07.kt index e2880c9129..8c57b42903 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-07.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-07.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.cancel07 +// This file was automatically generated from cancellation-and-timeouts.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleCancel07 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-channel-01.kt b/kotlinx-coroutines-core/jvm/test/guide/example-channel-01.kt index 36c6db316b..0af1ebd311 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-01.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-01.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.channel01 +// This file was automatically generated from channels.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleChannel01 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-channel-02.kt b/kotlinx-coroutines-core/jvm/test/guide/example-channel-02.kt index 59f5a76807..33eb7b33dd 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-02.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-02.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.channel02 +// This file was automatically generated from channels.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleChannel02 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-channel-03.kt b/kotlinx-coroutines-core/jvm/test/guide/example-channel-03.kt index 5c9cfb181f..4a2613be6a 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-03.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-03.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.channel03 +// This file was automatically generated from channels.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleChannel03 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-channel-04.kt b/kotlinx-coroutines-core/jvm/test/guide/example-channel-04.kt index 81fdd37e9b..73e8b6ce5f 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-04.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-04.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.channel04 +// This file was automatically generated from channels.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleChannel04 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-channel-05.kt b/kotlinx-coroutines-core/jvm/test/guide/example-channel-05.kt index d6ad2e5113..2143df579c 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-05.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-05.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.channel05 +// This file was automatically generated from channels.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleChannel05 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-channel-06.kt b/kotlinx-coroutines-core/jvm/test/guide/example-channel-06.kt index 452e056d34..b78fcc90ef 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-06.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-06.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.channel06 +// This file was automatically generated from channels.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleChannel06 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-channel-07.kt b/kotlinx-coroutines-core/jvm/test/guide/example-channel-07.kt index 9fc852e5c0..84de403dc9 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-07.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-07.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.channel07 +// This file was automatically generated from channels.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleChannel07 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-channel-08.kt b/kotlinx-coroutines-core/jvm/test/guide/example-channel-08.kt index c9916d4184..eedf1c7a89 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-08.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-08.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.channel08 +// This file was automatically generated from channels.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleChannel08 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-channel-09.kt b/kotlinx-coroutines-core/jvm/test/guide/example-channel-09.kt index fb293257e8..259f52eb8a 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-09.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-09.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.channel09 +// This file was automatically generated from channels.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleChannel09 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-channel-10.kt b/kotlinx-coroutines-core/jvm/test/guide/example-channel-10.kt index 43ceea50b3..ce0424471a 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-10.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-10.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.channel10 +// This file was automatically generated from channels.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleChannel10 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-compose-01.kt b/kotlinx-coroutines-core/jvm/test/guide/example-compose-01.kt index ab9ef608f4..ec20f1e40d 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-compose-01.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-compose-01.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.compose01 +// This file was automatically generated from composing-suspending-functions.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleCompose01 import kotlinx.coroutines.* import kotlin.system.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-compose-02.kt b/kotlinx-coroutines-core/jvm/test/guide/example-compose-02.kt index 9e46c6c48f..9531b5c893 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-compose-02.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-compose-02.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.compose02 +// This file was automatically generated from composing-suspending-functions.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleCompose02 import kotlinx.coroutines.* import kotlin.system.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-compose-03.kt b/kotlinx-coroutines-core/jvm/test/guide/example-compose-03.kt index 1dc2fd9bb2..a61afeab55 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-compose-03.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-compose-03.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.compose03 +// This file was automatically generated from composing-suspending-functions.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleCompose03 import kotlinx.coroutines.* import kotlin.system.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-compose-04.kt b/kotlinx-coroutines-core/jvm/test/guide/example-compose-04.kt index ad0b021488..312dc72b55 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-compose-04.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-compose-04.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.compose04 +// This file was automatically generated from composing-suspending-functions.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleCompose04 import kotlinx.coroutines.* import kotlin.system.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-compose-05.kt b/kotlinx-coroutines-core/jvm/test/guide/example-compose-05.kt index e02f33e0bd..37c4f99e6e 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-compose-05.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-compose-05.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.compose05 +// This file was automatically generated from composing-suspending-functions.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleCompose05 import kotlinx.coroutines.* import kotlin.system.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-compose-06.kt b/kotlinx-coroutines-core/jvm/test/guide/example-compose-06.kt index 1df506e4e8..5ee9e451fb 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-compose-06.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-compose-06.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.compose06 +// This file was automatically generated from composing-suspending-functions.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleCompose06 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-context-01.kt b/kotlinx-coroutines-core/jvm/test/guide/example-context-01.kt index c3a9f5afeb..0a9546057a 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-context-01.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-01.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.context01 +// This file was automatically generated from coroutine-context-and-dispatchers.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleContext01 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-context-02.kt b/kotlinx-coroutines-core/jvm/test/guide/example-context-02.kt index d1ec85fa9b..49bfe7efa3 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-context-02.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-02.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.context02 +// This file was automatically generated from coroutine-context-and-dispatchers.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleContext02 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-context-03.kt b/kotlinx-coroutines-core/jvm/test/guide/example-context-03.kt index e52976d095..3aaf0b30ad 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-context-03.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-03.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.context03 +// This file was automatically generated from coroutine-context-and-dispatchers.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleContext03 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-context-04.kt b/kotlinx-coroutines-core/jvm/test/guide/example-context-04.kt index b4a8a3f821..c5ce5eba7b 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-context-04.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-04.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.context04 +// This file was automatically generated from coroutine-context-and-dispatchers.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleContext04 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-context-05.kt b/kotlinx-coroutines-core/jvm/test/guide/example-context-05.kt index 338e3c9d88..3017b04996 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-context-05.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-05.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.context05 +// This file was automatically generated from coroutine-context-and-dispatchers.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleContext05 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-context-06.kt b/kotlinx-coroutines-core/jvm/test/guide/example-context-06.kt index b37b06b85c..e23eaf2542 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-context-06.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-06.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.context06 +// This file was automatically generated from coroutine-context-and-dispatchers.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleContext06 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-context-07.kt b/kotlinx-coroutines-core/jvm/test/guide/example-context-07.kt index 825f572a34..8037a559c5 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-context-07.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-07.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.context07 +// This file was automatically generated from coroutine-context-and-dispatchers.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleContext07 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-context-08.kt b/kotlinx-coroutines-core/jvm/test/guide/example-context-08.kt index 1083d77da4..a34e1aea5c 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-context-08.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-08.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.context08 +// This file was automatically generated from coroutine-context-and-dispatchers.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleContext08 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-context-09.kt b/kotlinx-coroutines-core/jvm/test/guide/example-context-09.kt index 386e52544f..6c1121b2a3 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-context-09.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-09.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.context09 +// This file was automatically generated from coroutine-context-and-dispatchers.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleContext09 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-context-10.kt b/kotlinx-coroutines-core/jvm/test/guide/example-context-10.kt index 3dde44670e..0256004405 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-context-10.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-10.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.context10 +// This file was automatically generated from coroutine-context-and-dispatchers.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleContext10 import kotlin.coroutines.* import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-context-11.kt b/kotlinx-coroutines-core/jvm/test/guide/example-context-11.kt index 4a50d86c0f..e3563171f5 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-context-11.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-11.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.context11 +// This file was automatically generated from coroutine-context-and-dispatchers.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleContext11 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-01.kt b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-01.kt index 4bec14fca3..34d7b68c82 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-01.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-01.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.exceptions01 +// This file was automatically generated from exception-handling.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleExceptions01 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-02.kt b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-02.kt index 818ab285c8..359eff60e4 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-02.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-02.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.exceptions02 +// This file was automatically generated from exception-handling.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleExceptions02 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-03.kt b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-03.kt index 2b1e8e62b1..1ad5aef719 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-03.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-03.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.exceptions03 +// This file was automatically generated from exception-handling.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleExceptions03 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-04.kt b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-04.kt index 02024ce206..e1fc22d725 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-04.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-04.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.exceptions04 +// This file was automatically generated from exception-handling.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleExceptions04 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-05.kt b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-05.kt index e90606ff2a..e97572aba8 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-05.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-05.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.exceptions05 +// This file was automatically generated from exception-handling.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleExceptions05 import kotlinx.coroutines.exceptions.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-06.kt b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-06.kt index 636c4a1f5d..eec27840e5 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-06.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-06.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.exceptions06 +// This file was automatically generated from exception-handling.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleExceptions06 import kotlinx.coroutines.* import java.io.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-01.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-01.kt index 020f458b44..df14603db0 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-01.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-01.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow01 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow01 fun foo(): List = listOf(1, 2, 3) diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-02.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-02.kt index 66fc1639b5..fcb61b9d1d 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-02.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-02.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow02 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow02 fun foo(): Sequence = sequence { // sequence builder for (i in 1..3) { diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-03.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-03.kt index 393a0fa3a0..ba94b2f8f6 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-03.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-03.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow03 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow03 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-04.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-04.kt index 7bde16cf29..3e3aea0f55 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-04.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-04.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow04 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow04 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-05.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-05.kt index c1e05e2e3c..6d0e451923 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-05.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-05.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow05 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow05 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-06.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-06.kt index 1926983d67..9d9348ea5c 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-06.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-06.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow06 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow06 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-07.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-07.kt index 47ecf20bae..73b1734208 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-07.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-07.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow07 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow07 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-08.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-08.kt index 96aa19c1d5..1e8580c81f 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-08.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-08.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow08 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow08 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-09.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-09.kt index 4af29d93d3..7e2113c401 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-09.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-09.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow09 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow09 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-10.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-10.kt index 47602dab09..61e74a1aa1 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-10.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-10.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow10 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow10 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-11.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-11.kt index a97400626c..513057d10b 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-11.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-11.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow11 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow11 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-12.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-12.kt index 24dc4267ad..54ddf659f3 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-12.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-12.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow12 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow12 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-13.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-13.kt index 5d45946cb9..4feacc6d25 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-13.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-13.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow13 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow13 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-14.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-14.kt index 6628d12601..c0f2320490 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-14.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-14.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow14 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow14 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-15.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-15.kt index 3c1b10a94b..8f0e395ce4 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-15.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-15.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow15 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow15 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-16.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-16.kt index 0698e1bf84..d2f41ff6cc 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-16.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-16.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow16 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow16 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-17.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-17.kt index 86de59a2d0..5db79df185 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-17.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-17.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow17 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow17 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-18.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-18.kt index 597ff78326..3c1a8a1b7c 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-18.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-18.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow18 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow18 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-19.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-19.kt index eff3d8c054..1725276bf8 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-19.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-19.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow19 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow19 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-20.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-20.kt index 0cc3df45ab..bb829d11a0 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-20.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-20.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow20 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow20 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-21.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-21.kt index 5bf0e87074..e0adea60b5 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-21.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-21.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow21 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow21 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-22.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-22.kt index cd8f8b0c5c..9450ce59a9 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-22.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-22.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow22 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow22 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-23.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-23.kt index 742452e414..513fa30450 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-23.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-23.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow23 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow23 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-24.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-24.kt index 32047a93c4..7da20e01a6 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-24.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-24.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow24 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow24 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-25.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-25.kt index 094553095a..e3c2b812cd 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-25.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-25.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow25 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow25 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-26.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-26.kt index 037e253094..e489c3f2ed 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-26.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-26.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow26 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow26 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-27.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-27.kt index 6117cf5c62..f9ef9793cf 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-27.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-27.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow27 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow27 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-28.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-28.kt index 15acb79614..84fc69fd7b 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-28.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-28.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow28 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow28 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-29.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-29.kt index c0497df73c..6c60c5d9d2 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-29.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-29.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow29 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow29 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-30.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-30.kt index 5035efe23c..e21c77fcf3 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-30.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-30.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow30 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow30 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-31.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-31.kt index dfa43db53e..9b2855ef09 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-31.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-31.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow31 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow31 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-32.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-32.kt index f541ab57d9..3ad74ae5f1 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-32.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-32.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow32 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow32 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-33.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-33.kt index 1e291412c3..c0e0ab3d5b 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-33.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-33.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow33 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow33 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-34.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-34.kt index df2cad2025..4b79a73683 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-34.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-34.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow34 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow34 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-35.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-35.kt index a7c6bd2100..928c8bf2e6 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-35.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-35.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow35 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow35 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-36.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-36.kt index 9c5a57bf8f..3138464ced 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-flow-36.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-36.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.flow36 +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleFlow36 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-select-01.kt b/kotlinx-coroutines-core/jvm/test/guide/example-select-01.kt index 1939e72089..c1a962e60d 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-select-01.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-select-01.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.select01 +// This file was automatically generated from select-expression.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleSelect01 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-select-02.kt b/kotlinx-coroutines-core/jvm/test/guide/example-select-02.kt index 0e51015105..57fe638297 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-select-02.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-select-02.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.select02 +// This file was automatically generated from select-expression.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleSelect02 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-select-03.kt b/kotlinx-coroutines-core/jvm/test/guide/example-select-03.kt index 42422378f8..36597dd637 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-select-03.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-select-03.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.select03 +// This file was automatically generated from select-expression.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleSelect03 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-select-04.kt b/kotlinx-coroutines-core/jvm/test/guide/example-select-04.kt index 2db5170206..4cd9f6d93f 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-select-04.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-select-04.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.select04 +// This file was automatically generated from select-expression.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleSelect04 import kotlinx.coroutines.* import kotlinx.coroutines.selects.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-select-05.kt b/kotlinx-coroutines-core/jvm/test/guide/example-select-05.kt index e03be9da75..464e9b20f3 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-select-05.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-select-05.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.select05 +// This file was automatically generated from select-expression.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleSelect05 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-supervision-01.kt b/kotlinx-coroutines-core/jvm/test/guide/example-supervision-01.kt index d70b6c9f69..ffd56a8f85 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-supervision-01.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-supervision-01.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.supervision01 +// This file was automatically generated from exception-handling.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleSupervision01 import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-supervision-02.kt b/kotlinx-coroutines-core/jvm/test/guide/example-supervision-02.kt index facc2e0881..dc3a0f2844 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-supervision-02.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-supervision-02.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.supervision02 +// This file was automatically generated from exception-handling.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleSupervision02 import kotlin.coroutines.* import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-supervision-03.kt b/kotlinx-coroutines-core/jvm/test/guide/example-supervision-03.kt index 47c31b9e75..b32a004639 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-supervision-03.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-supervision-03.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.supervision03 +// This file was automatically generated from exception-handling.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleSupervision03 import kotlin.coroutines.* import kotlinx.coroutines.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-sync-01.kt b/kotlinx-coroutines-core/jvm/test/guide/example-sync-01.kt index bd710a49cd..32d4ac4fe9 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-sync-01.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-sync-01.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.sync01 +// This file was automatically generated from shared-mutable-state-and-concurrency.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleSync01 import kotlinx.coroutines.* import kotlin.system.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-sync-02.kt b/kotlinx-coroutines-core/jvm/test/guide/example-sync-02.kt index 813123723a..fb551f8953 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-sync-02.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-sync-02.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.sync02 +// This file was automatically generated from shared-mutable-state-and-concurrency.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleSync02 import kotlinx.coroutines.* import kotlin.system.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-sync-03.kt b/kotlinx-coroutines-core/jvm/test/guide/example-sync-03.kt index 1baf849acd..6c34149c93 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-sync-03.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-sync-03.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.sync03 +// This file was automatically generated from shared-mutable-state-and-concurrency.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleSync03 import kotlinx.coroutines.* import java.util.concurrent.atomic.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-sync-04.kt b/kotlinx-coroutines-core/jvm/test/guide/example-sync-04.kt index e16a8113f0..f94c4a05da 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-sync-04.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-sync-04.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.sync04 +// This file was automatically generated from shared-mutable-state-and-concurrency.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleSync04 import kotlinx.coroutines.* import kotlin.system.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-sync-05.kt b/kotlinx-coroutines-core/jvm/test/guide/example-sync-05.kt index d022961b31..a6a1cdd2a6 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-sync-05.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-sync-05.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.sync05 +// This file was automatically generated from shared-mutable-state-and-concurrency.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleSync05 import kotlinx.coroutines.* import kotlin.system.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-sync-06.kt b/kotlinx-coroutines-core/jvm/test/guide/example-sync-06.kt index fe08f049fd..e6c5f1bad3 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-sync-06.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-sync-06.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.sync06 +// This file was automatically generated from shared-mutable-state-and-concurrency.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleSync06 import kotlinx.coroutines.* import kotlinx.coroutines.sync.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-sync-07.kt b/kotlinx-coroutines-core/jvm/test/guide/example-sync-07.kt index 65e2050021..a84e02c849 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-sync-07.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-sync-07.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.sync07 +// This file was automatically generated from shared-mutable-state-and-concurrency.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.exampleSync07 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/kotlinx-coroutines-core/jvm/test/guide/test/BasicsGuideTest.kt b/kotlinx-coroutines-core/jvm/test/guide/test/BasicsGuideTest.kt index 93b49a6013..7fc57c2ee3 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/test/BasicsGuideTest.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/test/BasicsGuideTest.kt @@ -1,53 +1,56 @@ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from basics.md by Knit tool. Do not edit. package kotlinx.coroutines.guide.test import org.junit.Test class BasicsGuideTest { - @Test - fun testKotlinxCoroutinesGuideBasic01() { - test("KotlinxCoroutinesGuideBasic01") { kotlinx.coroutines.guide.basic01.main() }.verifyLines( + fun testExampleBasic01() { + test("ExampleBasic01") { kotlinx.coroutines.guide.exampleBasic01.main() }.verifyLines( "Hello,", "World!" ) } @Test - fun testKotlinxCoroutinesGuideBasic02() { - test("KotlinxCoroutinesGuideBasic02") { kotlinx.coroutines.guide.basic02.main() }.verifyLines( + fun testExampleBasic02() { + test("ExampleBasic02") { kotlinx.coroutines.guide.exampleBasic02.main() }.verifyLines( "Hello,", "World!" ) } @Test - fun testKotlinxCoroutinesGuideBasic02b() { - test("KotlinxCoroutinesGuideBasic02b") { kotlinx.coroutines.guide.basic02b.main() }.verifyLines( + fun testExampleBasic03() { + test("ExampleBasic03") { kotlinx.coroutines.guide.exampleBasic03.main() }.verifyLines( "Hello,", "World!" ) } @Test - fun testKotlinxCoroutinesGuideBasic03() { - test("KotlinxCoroutinesGuideBasic03") { kotlinx.coroutines.guide.basic03.main() }.verifyLines( + fun testExampleBasic04() { + test("ExampleBasic04") { kotlinx.coroutines.guide.exampleBasic04.main() }.verifyLines( "Hello,", "World!" ) } @Test - fun testKotlinxCoroutinesGuideBasic03s() { - test("KotlinxCoroutinesGuideBasic03s") { kotlinx.coroutines.guide.basic03s.main() }.verifyLines( + fun testExampleBasic05() { + test("ExampleBasic05") { kotlinx.coroutines.guide.exampleBasic05.main() }.verifyLines( "Hello,", "World!" ) } @Test - fun testKotlinxCoroutinesGuideBasic04() { - test("KotlinxCoroutinesGuideBasic04") { kotlinx.coroutines.guide.basic04.main() }.verifyLines( + fun testExampleBasic06() { + test("ExampleBasic06") { kotlinx.coroutines.guide.exampleBasic06.main() }.verifyLines( "Task from coroutine scope", "Task from runBlocking", "Task from nested launch", @@ -56,23 +59,23 @@ class BasicsGuideTest { } @Test - fun testKotlinxCoroutinesGuideBasic05() { - test("KotlinxCoroutinesGuideBasic05") { kotlinx.coroutines.guide.basic05.main() }.verifyLines( + fun testExampleBasic07() { + test("ExampleBasic07") { kotlinx.coroutines.guide.exampleBasic07.main() }.verifyLines( "Hello,", "World!" ) } @Test - fun testKotlinxCoroutinesGuideBasic06() { - test("KotlinxCoroutinesGuideBasic06") { kotlinx.coroutines.guide.basic06.main() }.also { lines -> + fun testExampleBasic08() { + test("ExampleBasic08") { kotlinx.coroutines.guide.exampleBasic08.main() }.also { lines -> check(lines.size == 1 && lines[0] == ".".repeat(100_000)) } } @Test - fun testKotlinxCoroutinesGuideBasic07() { - test("KotlinxCoroutinesGuideBasic07") { kotlinx.coroutines.guide.basic07.main() }.verifyLines( + fun testExampleBasic09() { + test("ExampleBasic09") { kotlinx.coroutines.guide.exampleBasic09.main() }.verifyLines( "I'm sleeping 0 ...", "I'm sleeping 1 ...", "I'm sleeping 2 ..." diff --git a/kotlinx-coroutines-core/jvm/test/guide/test/CancellationTimeOutsGuideTest.kt b/kotlinx-coroutines-core/jvm/test/guide/test/CancellationGuideTest.kt similarity index 60% rename from kotlinx-coroutines-core/jvm/test/guide/test/CancellationTimeOutsGuideTest.kt rename to kotlinx-coroutines-core/jvm/test/guide/test/CancellationGuideTest.kt index 83bca486cd..a2e91de82d 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/test/CancellationTimeOutsGuideTest.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/test/CancellationGuideTest.kt @@ -1,13 +1,16 @@ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from cancellation-and-timeouts.md by Knit tool. Do not edit. package kotlinx.coroutines.guide.test import org.junit.Test -class CancellationTimeOutsGuideTest { - +class CancellationGuideTest { @Test - fun testKotlinxCoroutinesGuideCancel01() { - test("KotlinxCoroutinesGuideCancel01") { kotlinx.coroutines.guide.cancel01.main() }.verifyLines( + fun testExampleCancel01() { + test("ExampleCancel01") { kotlinx.coroutines.guide.exampleCancel01.main() }.verifyLines( "job: I'm sleeping 0 ...", "job: I'm sleeping 1 ...", "job: I'm sleeping 2 ...", @@ -17,8 +20,8 @@ class CancellationTimeOutsGuideTest { } @Test - fun testKotlinxCoroutinesGuideCancel02() { - test("KotlinxCoroutinesGuideCancel02") { kotlinx.coroutines.guide.cancel02.main() }.verifyLines( + fun testExampleCancel02() { + test("ExampleCancel02") { kotlinx.coroutines.guide.exampleCancel02.main() }.verifyLines( "job: I'm sleeping 0 ...", "job: I'm sleeping 1 ...", "job: I'm sleeping 2 ...", @@ -30,8 +33,8 @@ class CancellationTimeOutsGuideTest { } @Test - fun testKotlinxCoroutinesGuideCancel03() { - test("KotlinxCoroutinesGuideCancel03") { kotlinx.coroutines.guide.cancel03.main() }.verifyLines( + fun testExampleCancel03() { + test("ExampleCancel03") { kotlinx.coroutines.guide.exampleCancel03.main() }.verifyLines( "job: I'm sleeping 0 ...", "job: I'm sleeping 1 ...", "job: I'm sleeping 2 ...", @@ -41,8 +44,8 @@ class CancellationTimeOutsGuideTest { } @Test - fun testKotlinxCoroutinesGuideCancel04() { - test("KotlinxCoroutinesGuideCancel04") { kotlinx.coroutines.guide.cancel04.main() }.verifyLines( + fun testExampleCancel04() { + test("ExampleCancel04") { kotlinx.coroutines.guide.exampleCancel04.main() }.verifyLines( "job: I'm sleeping 0 ...", "job: I'm sleeping 1 ...", "job: I'm sleeping 2 ...", @@ -53,8 +56,8 @@ class CancellationTimeOutsGuideTest { } @Test - fun testKotlinxCoroutinesGuideCancel05() { - test("KotlinxCoroutinesGuideCancel05") { kotlinx.coroutines.guide.cancel05.main() }.verifyLines( + fun testExampleCancel05() { + test("ExampleCancel05") { kotlinx.coroutines.guide.exampleCancel05.main() }.verifyLines( "job: I'm sleeping 0 ...", "job: I'm sleeping 1 ...", "job: I'm sleeping 2 ...", @@ -66,8 +69,8 @@ class CancellationTimeOutsGuideTest { } @Test - fun testKotlinxCoroutinesGuideCancel06() { - test("KotlinxCoroutinesGuideCancel06") { kotlinx.coroutines.guide.cancel06.main() }.verifyLinesStartWith( + fun testExampleCancel06() { + test("ExampleCancel06") { kotlinx.coroutines.guide.exampleCancel06.main() }.verifyLinesStartWith( "I'm sleeping 0 ...", "I'm sleeping 1 ...", "I'm sleeping 2 ...", @@ -76,8 +79,8 @@ class CancellationTimeOutsGuideTest { } @Test - fun testKotlinxCoroutinesGuideCancel07() { - test("KotlinxCoroutinesGuideCancel07") { kotlinx.coroutines.guide.cancel07.main() }.verifyLines( + fun testExampleCancel07() { + test("ExampleCancel07") { kotlinx.coroutines.guide.exampleCancel07.main() }.verifyLines( "I'm sleeping 0 ...", "I'm sleeping 1 ...", "I'm sleeping 2 ...", diff --git a/kotlinx-coroutines-core/jvm/test/guide/test/ChannelsGuideTest.kt b/kotlinx-coroutines-core/jvm/test/guide/test/ChannelsGuideTest.kt index a747c98487..209d439663 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/test/ChannelsGuideTest.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/test/ChannelsGuideTest.kt @@ -1,13 +1,16 @@ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from channels.md by Knit tool. Do not edit. package kotlinx.coroutines.guide.test import org.junit.Test class ChannelsGuideTest { - @Test - fun testKotlinxCoroutinesGuideChannel01() { - test("KotlinxCoroutinesGuideChannel01") { kotlinx.coroutines.guide.channel01.main() }.verifyLines( + fun testExampleChannel01() { + test("ExampleChannel01") { kotlinx.coroutines.guide.exampleChannel01.main() }.verifyLines( "1", "4", "9", @@ -18,8 +21,8 @@ class ChannelsGuideTest { } @Test - fun testKotlinxCoroutinesGuideChannel02() { - test("KotlinxCoroutinesGuideChannel02") { kotlinx.coroutines.guide.channel02.main() }.verifyLines( + fun testExampleChannel02() { + test("ExampleChannel02") { kotlinx.coroutines.guide.exampleChannel02.main() }.verifyLines( "1", "4", "9", @@ -30,8 +33,8 @@ class ChannelsGuideTest { } @Test - fun testKotlinxCoroutinesGuideChannel03() { - test("KotlinxCoroutinesGuideChannel03") { kotlinx.coroutines.guide.channel03.main() }.verifyLines( + fun testExampleChannel03() { + test("ExampleChannel03") { kotlinx.coroutines.guide.exampleChannel03.main() }.verifyLines( "1", "4", "9", @@ -42,8 +45,8 @@ class ChannelsGuideTest { } @Test - fun testKotlinxCoroutinesGuideChannel04() { - test("KotlinxCoroutinesGuideChannel04") { kotlinx.coroutines.guide.channel04.main() }.verifyLines( + fun testExampleChannel04() { + test("ExampleChannel04") { kotlinx.coroutines.guide.exampleChannel04.main() }.verifyLines( "1", "4", "9", @@ -54,8 +57,8 @@ class ChannelsGuideTest { } @Test - fun testKotlinxCoroutinesGuideChannel05() { - test("KotlinxCoroutinesGuideChannel05") { kotlinx.coroutines.guide.channel05.main() }.verifyLines( + fun testExampleChannel05() { + test("ExampleChannel05") { kotlinx.coroutines.guide.exampleChannel05.main() }.verifyLines( "2", "3", "5", @@ -70,15 +73,15 @@ class ChannelsGuideTest { } @Test - fun testKotlinxCoroutinesGuideChannel06() { - test("KotlinxCoroutinesGuideChannel06") { kotlinx.coroutines.guide.channel06.main() }.also { lines -> + fun testExampleChannel06() { + test("ExampleChannel06") { kotlinx.coroutines.guide.exampleChannel06.main() }.also { lines -> check(lines.size == 10 && lines.withIndex().all { (i, line) -> line.startsWith("Processor #") && line.endsWith(" received ${i + 1}") }) } } @Test - fun testKotlinxCoroutinesGuideChannel07() { - test("KotlinxCoroutinesGuideChannel07") { kotlinx.coroutines.guide.channel07.main() }.verifyLines( + fun testExampleChannel07() { + test("ExampleChannel07") { kotlinx.coroutines.guide.exampleChannel07.main() }.verifyLines( "foo", "foo", "BAR!", @@ -89,8 +92,8 @@ class ChannelsGuideTest { } @Test - fun testKotlinxCoroutinesGuideChannel08() { - test("KotlinxCoroutinesGuideChannel08") { kotlinx.coroutines.guide.channel08.main() }.verifyLines( + fun testExampleChannel08() { + test("ExampleChannel08") { kotlinx.coroutines.guide.exampleChannel08.main() }.verifyLines( "Sending 0", "Sending 1", "Sending 2", @@ -100,8 +103,8 @@ class ChannelsGuideTest { } @Test - fun testKotlinxCoroutinesGuideChannel09() { - test("KotlinxCoroutinesGuideChannel09") { kotlinx.coroutines.guide.channel09.main() }.verifyLines( + fun testExampleChannel09() { + test("ExampleChannel09") { kotlinx.coroutines.guide.exampleChannel09.main() }.verifyLines( "ping Ball(hits=1)", "pong Ball(hits=2)", "ping Ball(hits=3)", @@ -110,8 +113,8 @@ class ChannelsGuideTest { } @Test - fun testKotlinxCoroutinesGuideChannel10() { - test("KotlinxCoroutinesGuideChannel10") { kotlinx.coroutines.guide.channel10.main() }.verifyLines( + fun testExampleChannel10() { + test("ExampleChannel10") { kotlinx.coroutines.guide.exampleChannel10.main() }.verifyLines( "Initial element is available immediately: kotlin.Unit", "Next element is not ready in 50 ms: null", "Next element is ready in 100 ms: kotlin.Unit", diff --git a/kotlinx-coroutines-core/jvm/test/guide/test/ComposingGuideTest.kt b/kotlinx-coroutines-core/jvm/test/guide/test/ComposingGuideTest.kt index de4cba44c6..50c3fd7e62 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/test/ComposingGuideTest.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/test/ComposingGuideTest.kt @@ -1,53 +1,56 @@ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from composing-suspending-functions.md by Knit tool. Do not edit. package kotlinx.coroutines.guide.test import org.junit.Test class ComposingGuideTest { - @Test - fun testKotlinxCoroutinesGuideCompose01() { - test("KotlinxCoroutinesGuideCompose01") { kotlinx.coroutines.guide.compose01.main() }.verifyLinesArbitraryTime( + fun testExampleCompose01() { + test("ExampleCompose01") { kotlinx.coroutines.guide.exampleCompose01.main() }.verifyLinesArbitraryTime( "The answer is 42", "Completed in 2017 ms" ) } @Test - fun testKotlinxCoroutinesGuideCompose02() { - test("KotlinxCoroutinesGuideCompose02") { kotlinx.coroutines.guide.compose02.main() }.verifyLinesArbitraryTime( + fun testExampleCompose02() { + test("ExampleCompose02") { kotlinx.coroutines.guide.exampleCompose02.main() }.verifyLinesArbitraryTime( "The answer is 42", "Completed in 1017 ms" ) } @Test - fun testKotlinxCoroutinesGuideCompose03() { - test("KotlinxCoroutinesGuideCompose03") { kotlinx.coroutines.guide.compose03.main() }.verifyLinesArbitraryTime( + fun testExampleCompose03() { + test("ExampleCompose03") { kotlinx.coroutines.guide.exampleCompose03.main() }.verifyLinesArbitraryTime( "The answer is 42", "Completed in 1017 ms" ) } @Test - fun testKotlinxCoroutinesGuideCompose04() { - test("KotlinxCoroutinesGuideCompose04") { kotlinx.coroutines.guide.compose04.main() }.verifyLinesArbitraryTime( + fun testExampleCompose04() { + test("ExampleCompose04") { kotlinx.coroutines.guide.exampleCompose04.main() }.verifyLinesArbitraryTime( "The answer is 42", "Completed in 1085 ms" ) } @Test - fun testKotlinxCoroutinesGuideCompose05() { - test("KotlinxCoroutinesGuideCompose05") { kotlinx.coroutines.guide.compose05.main() }.verifyLinesArbitraryTime( + fun testExampleCompose05() { + test("ExampleCompose05") { kotlinx.coroutines.guide.exampleCompose05.main() }.verifyLinesArbitraryTime( "The answer is 42", "Completed in 1017 ms" ) } @Test - fun testKotlinxCoroutinesGuideCompose06() { - test("KotlinxCoroutinesGuideCompose06") { kotlinx.coroutines.guide.compose06.main() }.verifyLines( + fun testExampleCompose06() { + test("ExampleCompose06") { kotlinx.coroutines.guide.exampleCompose06.main() }.verifyLines( "Second child throws an exception", "First child was cancelled", "Computation failed with ArithmeticException" diff --git a/kotlinx-coroutines-core/jvm/test/guide/test/DispatcherGuideTest.kt b/kotlinx-coroutines-core/jvm/test/guide/test/DispatcherGuideTest.kt index 180738f8b3..c0c32410d5 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/test/DispatcherGuideTest.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/test/DispatcherGuideTest.kt @@ -1,13 +1,16 @@ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutine-context-and-dispatchers.md by Knit tool. Do not edit. package kotlinx.coroutines.guide.test import org.junit.Test -class DispatchersGuideTest { - +class DispatcherGuideTest { @Test - fun testKotlinxCoroutinesGuideContext01() { - test("KotlinxCoroutinesGuideContext01") { kotlinx.coroutines.guide.context01.main() }.verifyLinesStartUnordered( + fun testExampleContext01() { + test("ExampleContext01") { kotlinx.coroutines.guide.exampleContext01.main() }.verifyLinesStartUnordered( "Unconfined : I'm working in thread main", "Default : I'm working in thread DefaultDispatcher-worker-1", "newSingleThreadContext: I'm working in thread MyOwnThread", @@ -16,8 +19,8 @@ class DispatchersGuideTest { } @Test - fun testKotlinxCoroutinesGuideContext02() { - test("KotlinxCoroutinesGuideContext02") { kotlinx.coroutines.guide.context02.main() }.verifyLinesStart( + fun testExampleContext02() { + test("ExampleContext02") { kotlinx.coroutines.guide.exampleContext02.main() }.verifyLinesStart( "Unconfined : I'm working in thread main", "main runBlocking: I'm working in thread main", "Unconfined : After delay in thread kotlinx.coroutines.DefaultExecutor", @@ -26,8 +29,8 @@ class DispatchersGuideTest { } @Test - fun testKotlinxCoroutinesGuideContext03() { - test("KotlinxCoroutinesGuideContext03") { kotlinx.coroutines.guide.context03.main() }.verifyLinesFlexibleThread( + fun testExampleContext03() { + test("ExampleContext03") { kotlinx.coroutines.guide.exampleContext03.main() }.verifyLinesFlexibleThread( "[main @coroutine#2] I'm computing a piece of the answer", "[main @coroutine#3] I'm computing another piece of the answer", "[main @coroutine#1] The answer is 42" @@ -35,8 +38,8 @@ class DispatchersGuideTest { } @Test - fun testKotlinxCoroutinesGuideContext04() { - test("KotlinxCoroutinesGuideContext04") { kotlinx.coroutines.guide.context04.main() }.verifyLines( + fun testExampleContext04() { + test("ExampleContext04") { kotlinx.coroutines.guide.exampleContext04.main() }.verifyLines( "[Ctx1 @coroutine#1] Started in ctx1", "[Ctx2 @coroutine#1] Working in ctx2", "[Ctx1 @coroutine#1] Back to ctx1" @@ -44,15 +47,15 @@ class DispatchersGuideTest { } @Test - fun testKotlinxCoroutinesGuideContext05() { - test("KotlinxCoroutinesGuideContext05") { kotlinx.coroutines.guide.context05.main() }.also { lines -> + fun testExampleContext05() { + test("ExampleContext05") { kotlinx.coroutines.guide.exampleContext05.main() }.also { lines -> check(lines.size == 1 && lines[0].startsWith("My job is \"coroutine#1\":BlockingCoroutine{Active}@")) } } @Test - fun testKotlinxCoroutinesGuideContext06() { - test("KotlinxCoroutinesGuideContext06") { kotlinx.coroutines.guide.context06.main() }.verifyLines( + fun testExampleContext06() { + test("ExampleContext06") { kotlinx.coroutines.guide.exampleContext06.main() }.verifyLines( "job1: I run in GlobalScope and execute independently!", "job2: I am a child of the request coroutine", "job1: I am not affected by cancellation of the request", @@ -61,8 +64,8 @@ class DispatchersGuideTest { } @Test - fun testKotlinxCoroutinesGuideContext07() { - test("KotlinxCoroutinesGuideContext07") { kotlinx.coroutines.guide.context07.main() }.verifyLines( + fun testExampleContext07() { + test("ExampleContext07") { kotlinx.coroutines.guide.exampleContext07.main() }.verifyLines( "request: I'm done and I don't explicitly join my children that are still active", "Coroutine 0 is done", "Coroutine 1 is done", @@ -72,8 +75,8 @@ class DispatchersGuideTest { } @Test - fun testKotlinxCoroutinesGuideContext08() { - test("KotlinxCoroutinesGuideContext08") { kotlinx.coroutines.guide.context08.main() }.verifyLinesFlexibleThread( + fun testExampleContext08() { + test("ExampleContext08") { kotlinx.coroutines.guide.exampleContext08.main() }.verifyLinesFlexibleThread( "[main @main#1] Started main coroutine", "[main @v1coroutine#2] Computing v1", "[main @v2coroutine#3] Computing v2", @@ -82,15 +85,15 @@ class DispatchersGuideTest { } @Test - fun testKotlinxCoroutinesGuideContext09() { - test("KotlinxCoroutinesGuideContext09") { kotlinx.coroutines.guide.context09.main() }.verifyLinesFlexibleThread( + fun testExampleContext09() { + test("ExampleContext09") { kotlinx.coroutines.guide.exampleContext09.main() }.verifyLinesFlexibleThread( "I'm working in thread DefaultDispatcher-worker-1 @test#2" ) } @Test - fun testKotlinxCoroutinesGuideContext10() { - test("KotlinxCoroutinesGuideContext10") { kotlinx.coroutines.guide.context10.main() }.verifyLines( + fun testExampleContext10() { + test("ExampleContext10") { kotlinx.coroutines.guide.exampleContext10.main() }.verifyLines( "Launched coroutines", "Coroutine 0 is done", "Coroutine 1 is done", @@ -99,8 +102,8 @@ class DispatchersGuideTest { } @Test - fun testKotlinxCoroutinesGuideContext11() { - test("KotlinxCoroutinesGuideContext11") { kotlinx.coroutines.guide.context11.main() }.verifyLinesFlexibleThread( + fun testExampleContext11() { + test("ExampleContext11") { kotlinx.coroutines.guide.exampleContext11.main() }.verifyLinesFlexibleThread( "Pre-main, current thread: Thread[main @coroutine#1,5,main], thread local value: 'main'", "Launch start, current thread: Thread[DefaultDispatcher-worker-1 @coroutine#2,5,main], thread local value: 'launch'", "After yield, current thread: Thread[DefaultDispatcher-worker-2 @coroutine#2,5,main], thread local value: 'launch'", diff --git a/kotlinx-coroutines-core/jvm/test/guide/test/ExceptionsGuideTest.kt b/kotlinx-coroutines-core/jvm/test/guide/test/ExceptionsGuideTest.kt index 7fa692b2a0..4a140208f9 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/test/ExceptionsGuideTest.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/test/ExceptionsGuideTest.kt @@ -1,13 +1,16 @@ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from exception-handling.md by Knit tool. Do not edit. package kotlinx.coroutines.guide.test import org.junit.Test class ExceptionsGuideTest { - @Test - fun testKotlinxCoroutinesGuideExceptions01() { - test("KotlinxCoroutinesGuideExceptions01") { kotlinx.coroutines.guide.exceptions01.main() }.verifyExceptions( + fun testExampleExceptions01() { + test("ExampleExceptions01") { kotlinx.coroutines.guide.exampleExceptions01.main() }.verifyExceptions( "Throwing exception from launch", "Exception in thread \"DefaultDispatcher-worker-2 @coroutine#2\" java.lang.IndexOutOfBoundsException", "Joined failed job", @@ -17,15 +20,15 @@ class ExceptionsGuideTest { } @Test - fun testKotlinxCoroutinesGuideExceptions02() { - test("KotlinxCoroutinesGuideExceptions02") { kotlinx.coroutines.guide.exceptions02.main() }.verifyLines( + fun testExampleExceptions02() { + test("ExampleExceptions02") { kotlinx.coroutines.guide.exampleExceptions02.main() }.verifyLines( "Caught java.lang.AssertionError" ) } @Test - fun testKotlinxCoroutinesGuideExceptions03() { - test("KotlinxCoroutinesGuideExceptions03") { kotlinx.coroutines.guide.exceptions03.main() }.verifyLines( + fun testExampleExceptions03() { + test("ExampleExceptions03") { kotlinx.coroutines.guide.exampleExceptions03.main() }.verifyLines( "Cancelling child", "Child is cancelled", "Parent is not cancelled" @@ -33,8 +36,8 @@ class ExceptionsGuideTest { } @Test - fun testKotlinxCoroutinesGuideExceptions04() { - test("KotlinxCoroutinesGuideExceptions04") { kotlinx.coroutines.guide.exceptions04.main() }.verifyLines( + fun testExampleExceptions04() { + test("ExampleExceptions04") { kotlinx.coroutines.guide.exampleExceptions04.main() }.verifyLines( "Second child throws an exception", "Children are cancelled, but exception is not handled until all children terminate", "The first child finished its non cancellable block", @@ -43,23 +46,23 @@ class ExceptionsGuideTest { } @Test - fun testKotlinxCoroutinesGuideExceptions05() { - test("KotlinxCoroutinesGuideExceptions05") { kotlinx.coroutines.guide.exceptions05.main() }.verifyLines( + fun testExampleExceptions05() { + test("ExampleExceptions05") { kotlinx.coroutines.guide.exampleExceptions05.main() }.verifyLines( "Caught java.io.IOException with suppressed [java.lang.ArithmeticException]" ) } @Test - fun testKotlinxCoroutinesGuideExceptions06() { - test("KotlinxCoroutinesGuideExceptions06") { kotlinx.coroutines.guide.exceptions06.main() }.verifyLines( + fun testExampleExceptions06() { + test("ExampleExceptions06") { kotlinx.coroutines.guide.exampleExceptions06.main() }.verifyLines( "Rethrowing CancellationException with original cause", "Caught original java.io.IOException" ) } @Test - fun testKotlinxCoroutinesGuideSupervision01() { - test("KotlinxCoroutinesGuideSupervision01") { kotlinx.coroutines.guide.supervision01.main() }.verifyLines( + fun testExampleSupervision01() { + test("ExampleSupervision01") { kotlinx.coroutines.guide.exampleSupervision01.main() }.verifyLines( "First child is failing", "First child is cancelled: true, but second one is still active", "Cancelling supervisor", @@ -68,8 +71,8 @@ class ExceptionsGuideTest { } @Test - fun testKotlinxCoroutinesGuideSupervision02() { - test("KotlinxCoroutinesGuideSupervision02") { kotlinx.coroutines.guide.supervision02.main() }.verifyLines( + fun testExampleSupervision02() { + test("ExampleSupervision02") { kotlinx.coroutines.guide.exampleSupervision02.main() }.verifyLines( "Child is sleeping", "Throwing exception from scope", "Child is cancelled", @@ -78,8 +81,8 @@ class ExceptionsGuideTest { } @Test - fun testKotlinxCoroutinesGuideSupervision03() { - test("KotlinxCoroutinesGuideSupervision03") { kotlinx.coroutines.guide.supervision03.main() }.verifyLines( + fun testExampleSupervision03() { + test("ExampleSupervision03") { kotlinx.coroutines.guide.exampleSupervision03.main() }.verifyLines( "Scope is completing", "Child throws an exception", "Caught java.lang.AssertionError", diff --git a/kotlinx-coroutines-core/jvm/test/guide/test/FlowGuideTest.kt b/kotlinx-coroutines-core/jvm/test/guide/test/FlowGuideTest.kt index 0353c54e7c..5d320f2124 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/test/FlowGuideTest.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/test/FlowGuideTest.kt @@ -1,13 +1,16 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + // This file was automatically generated from flow.md by Knit tool. Do not edit. package kotlinx.coroutines.guide.test import org.junit.Test class FlowGuideTest { - @Test - fun testKotlinxCoroutinesGuideFlow01() { - test("KotlinxCoroutinesGuideFlow01") { kotlinx.coroutines.guide.flow01.main() }.verifyLines( + fun testExampleFlow01() { + test("ExampleFlow01") { kotlinx.coroutines.guide.exampleFlow01.main() }.verifyLines( "1", "2", "3" @@ -15,8 +18,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow02() { - test("KotlinxCoroutinesGuideFlow02") { kotlinx.coroutines.guide.flow02.main() }.verifyLines( + fun testExampleFlow02() { + test("ExampleFlow02") { kotlinx.coroutines.guide.exampleFlow02.main() }.verifyLines( "1", "2", "3" @@ -24,8 +27,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow03() { - test("KotlinxCoroutinesGuideFlow03") { kotlinx.coroutines.guide.flow03.main() }.verifyLines( + fun testExampleFlow03() { + test("ExampleFlow03") { kotlinx.coroutines.guide.exampleFlow03.main() }.verifyLines( "1", "2", "3" @@ -33,8 +36,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow04() { - test("KotlinxCoroutinesGuideFlow04") { kotlinx.coroutines.guide.flow04.main() }.verifyLines( + fun testExampleFlow04() { + test("ExampleFlow04") { kotlinx.coroutines.guide.exampleFlow04.main() }.verifyLines( "I'm not blocked 1", "1", "I'm not blocked 2", @@ -45,8 +48,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow05() { - test("KotlinxCoroutinesGuideFlow05") { kotlinx.coroutines.guide.flow05.main() }.verifyLines( + fun testExampleFlow05() { + test("ExampleFlow05") { kotlinx.coroutines.guide.exampleFlow05.main() }.verifyLines( "Calling foo...", "Calling collect...", "Flow started", @@ -62,8 +65,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow06() { - test("KotlinxCoroutinesGuideFlow06") { kotlinx.coroutines.guide.flow06.main() }.verifyLines( + fun testExampleFlow06() { + test("ExampleFlow06") { kotlinx.coroutines.guide.exampleFlow06.main() }.verifyLines( "Emitting 1", "1", "Emitting 2", @@ -73,8 +76,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow07() { - test("KotlinxCoroutinesGuideFlow07") { kotlinx.coroutines.guide.flow07.main() }.verifyLines( + fun testExampleFlow07() { + test("ExampleFlow07") { kotlinx.coroutines.guide.exampleFlow07.main() }.verifyLines( "1", "2", "3" @@ -82,8 +85,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow08() { - test("KotlinxCoroutinesGuideFlow08") { kotlinx.coroutines.guide.flow08.main() }.verifyLines( + fun testExampleFlow08() { + test("ExampleFlow08") { kotlinx.coroutines.guide.exampleFlow08.main() }.verifyLines( "response 1", "response 2", "response 3" @@ -91,8 +94,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow09() { - test("KotlinxCoroutinesGuideFlow09") { kotlinx.coroutines.guide.flow09.main() }.verifyLines( + fun testExampleFlow09() { + test("ExampleFlow09") { kotlinx.coroutines.guide.exampleFlow09.main() }.verifyLines( "Making request 1", "response 1", "Making request 2", @@ -103,8 +106,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow10() { - test("KotlinxCoroutinesGuideFlow10") { kotlinx.coroutines.guide.flow10.main() }.verifyLines( + fun testExampleFlow10() { + test("ExampleFlow10") { kotlinx.coroutines.guide.exampleFlow10.main() }.verifyLines( "1", "2", "Finally in numbers" @@ -112,15 +115,15 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow11() { - test("KotlinxCoroutinesGuideFlow11") { kotlinx.coroutines.guide.flow11.main() }.verifyLines( + fun testExampleFlow11() { + test("ExampleFlow11") { kotlinx.coroutines.guide.exampleFlow11.main() }.verifyLines( "55" ) } @Test - fun testKotlinxCoroutinesGuideFlow12() { - test("KotlinxCoroutinesGuideFlow12") { kotlinx.coroutines.guide.flow12.main() }.verifyLines( + fun testExampleFlow12() { + test("ExampleFlow12") { kotlinx.coroutines.guide.exampleFlow12.main() }.verifyLines( "Filter 1", "Filter 2", "Map 2", @@ -134,8 +137,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow13() { - test("KotlinxCoroutinesGuideFlow13") { kotlinx.coroutines.guide.flow13.main() }.verifyLinesFlexibleThread( + fun testExampleFlow13() { + test("ExampleFlow13") { kotlinx.coroutines.guide.exampleFlow13.main() }.verifyLinesFlexibleThread( "[main @coroutine#1] Started foo flow", "[main @coroutine#1] Collected 1", "[main @coroutine#1] Collected 2", @@ -144,19 +147,19 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow14() { - test("KotlinxCoroutinesGuideFlow14") { kotlinx.coroutines.guide.flow14.main() }.verifyExceptions( + fun testExampleFlow14() { + test("ExampleFlow14") { kotlinx.coroutines.guide.exampleFlow14.main() }.verifyExceptions( "Exception in thread \"main\" java.lang.IllegalStateException: Flow invariant is violated:", - " Flow was collected in [CoroutineId(1), \"coroutine#1\":BlockingCoroutine{Active}@5511c7f8, BlockingEventLoop@2eac3323],", - " but emission happened in [CoroutineId(1), \"coroutine#1\":DispatchedCoroutine{Active}@2dae0000, DefaultDispatcher].", - " Please refer to 'flow' documentation or use 'flowOn' instead", - " at ..." + "\t\tFlow was collected in [CoroutineId(1), \"coroutine#1\":BlockingCoroutine{Active}@5511c7f8, BlockingEventLoop@2eac3323],", + "\t\tbut emission happened in [CoroutineId(1), \"coroutine#1\":DispatchedCoroutine{Active}@2dae0000, DefaultDispatcher].", + "\t\tPlease refer to 'flow' documentation or use 'flowOn' instead", + "\tat ..." ) } @Test - fun testKotlinxCoroutinesGuideFlow15() { - test("KotlinxCoroutinesGuideFlow15") { kotlinx.coroutines.guide.flow15.main() }.verifyLinesFlexibleThread( + fun testExampleFlow15() { + test("ExampleFlow15") { kotlinx.coroutines.guide.exampleFlow15.main() }.verifyLinesFlexibleThread( "[DefaultDispatcher-worker-1 @coroutine#2] Emitting 1", "[main @coroutine#1] Collected 1", "[DefaultDispatcher-worker-1 @coroutine#2] Emitting 2", @@ -167,8 +170,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow16() { - test("KotlinxCoroutinesGuideFlow16") { kotlinx.coroutines.guide.flow16.main() }.verifyLinesArbitraryTime( + fun testExampleFlow16() { + test("ExampleFlow16") { kotlinx.coroutines.guide.exampleFlow16.main() }.verifyLinesArbitraryTime( "1", "2", "3", @@ -177,8 +180,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow17() { - test("KotlinxCoroutinesGuideFlow17") { kotlinx.coroutines.guide.flow17.main() }.verifyLinesArbitraryTime( + fun testExampleFlow17() { + test("ExampleFlow17") { kotlinx.coroutines.guide.exampleFlow17.main() }.verifyLinesArbitraryTime( "1", "2", "3", @@ -187,8 +190,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow18() { - test("KotlinxCoroutinesGuideFlow18") { kotlinx.coroutines.guide.flow18.main() }.verifyLinesArbitraryTime( + fun testExampleFlow18() { + test("ExampleFlow18") { kotlinx.coroutines.guide.exampleFlow18.main() }.verifyLinesArbitraryTime( "1", "3", "Collected in 758 ms" @@ -196,8 +199,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow19() { - test("KotlinxCoroutinesGuideFlow19") { kotlinx.coroutines.guide.flow19.main() }.verifyLinesArbitraryTime( + fun testExampleFlow19() { + test("ExampleFlow19") { kotlinx.coroutines.guide.exampleFlow19.main() }.verifyLinesArbitraryTime( "Collecting 1", "Collecting 2", "Collecting 3", @@ -207,8 +210,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow20() { - test("KotlinxCoroutinesGuideFlow20") { kotlinx.coroutines.guide.flow20.main() }.verifyLines( + fun testExampleFlow20() { + test("ExampleFlow20") { kotlinx.coroutines.guide.exampleFlow20.main() }.verifyLines( "1 -> one", "2 -> two", "3 -> three" @@ -216,8 +219,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow21() { - test("KotlinxCoroutinesGuideFlow21") { kotlinx.coroutines.guide.flow21.main() }.verifyLinesArbitraryTime( + fun testExampleFlow21() { + test("ExampleFlow21") { kotlinx.coroutines.guide.exampleFlow21.main() }.verifyLinesArbitraryTime( "1 -> one at 437 ms from start", "2 -> two at 837 ms from start", "3 -> three at 1243 ms from start" @@ -225,8 +228,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow22() { - test("KotlinxCoroutinesGuideFlow22") { kotlinx.coroutines.guide.flow22.main() }.verifyLinesArbitraryTime( + fun testExampleFlow22() { + test("ExampleFlow22") { kotlinx.coroutines.guide.exampleFlow22.main() }.verifyLinesArbitraryTime( "1 -> one at 452 ms from start", "2 -> one at 651 ms from start", "2 -> two at 854 ms from start", @@ -236,8 +239,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow23() { - test("KotlinxCoroutinesGuideFlow23") { kotlinx.coroutines.guide.flow23.main() }.verifyLinesArbitraryTime( + fun testExampleFlow23() { + test("ExampleFlow23") { kotlinx.coroutines.guide.exampleFlow23.main() }.verifyLinesArbitraryTime( "1: First at 121 ms from start", "1: Second at 622 ms from start", "2: First at 727 ms from start", @@ -248,8 +251,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow24() { - test("KotlinxCoroutinesGuideFlow24") { kotlinx.coroutines.guide.flow24.main() }.verifyLinesArbitraryTime( + fun testExampleFlow24() { + test("ExampleFlow24") { kotlinx.coroutines.guide.exampleFlow24.main() }.verifyLinesArbitraryTime( "1: First at 136 ms from start", "2: First at 231 ms from start", "3: First at 333 ms from start", @@ -260,8 +263,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow25() { - test("KotlinxCoroutinesGuideFlow25") { kotlinx.coroutines.guide.flow25.main() }.verifyLinesArbitraryTime( + fun testExampleFlow25() { + test("ExampleFlow25") { kotlinx.coroutines.guide.exampleFlow25.main() }.verifyLinesArbitraryTime( "1: First at 142 ms from start", "2: First at 322 ms from start", "3: First at 425 ms from start", @@ -270,8 +273,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow26() { - test("KotlinxCoroutinesGuideFlow26") { kotlinx.coroutines.guide.flow26.main() }.verifyLines( + fun testExampleFlow26() { + test("ExampleFlow26") { kotlinx.coroutines.guide.exampleFlow26.main() }.verifyLines( "Emitting 1", "1", "Emitting 2", @@ -281,8 +284,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow27() { - test("KotlinxCoroutinesGuideFlow27") { kotlinx.coroutines.guide.flow27.main() }.verifyLines( + fun testExampleFlow27() { + test("ExampleFlow27") { kotlinx.coroutines.guide.exampleFlow27.main() }.verifyLines( "Emitting 1", "string 1", "Emitting 2", @@ -291,8 +294,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow28() { - test("KotlinxCoroutinesGuideFlow28") { kotlinx.coroutines.guide.flow28.main() }.verifyLines( + fun testExampleFlow28() { + test("ExampleFlow28") { kotlinx.coroutines.guide.exampleFlow28.main() }.verifyLines( "Emitting 1", "string 1", "Emitting 2", @@ -301,19 +304,19 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow29() { - test("KotlinxCoroutinesGuideFlow29") { kotlinx.coroutines.guide.flow29.main() }.verifyExceptions( + fun testExampleFlow29() { + test("ExampleFlow29") { kotlinx.coroutines.guide.exampleFlow29.main() }.verifyExceptions( "Emitting 1", "1", "Emitting 2", "Exception in thread \"main\" java.lang.IllegalStateException: Collected 2", - " at ..." + "\tat ..." ) } @Test - fun testKotlinxCoroutinesGuideFlow30() { - test("KotlinxCoroutinesGuideFlow30") { kotlinx.coroutines.guide.flow30.main() }.verifyExceptions( + fun testExampleFlow30() { + test("ExampleFlow30") { kotlinx.coroutines.guide.exampleFlow30.main() }.verifyExceptions( "Emitting 1", "1", "Emitting 2", @@ -322,8 +325,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow31() { - test("KotlinxCoroutinesGuideFlow31") { kotlinx.coroutines.guide.flow31.main() }.verifyLines( + fun testExampleFlow31() { + test("ExampleFlow31") { kotlinx.coroutines.guide.exampleFlow31.main() }.verifyLines( "1", "2", "3", @@ -332,8 +335,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow32() { - test("KotlinxCoroutinesGuideFlow32") { kotlinx.coroutines.guide.flow32.main() }.verifyLines( + fun testExampleFlow32() { + test("ExampleFlow32") { kotlinx.coroutines.guide.exampleFlow32.main() }.verifyLines( "1", "2", "3", @@ -342,8 +345,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow33() { - test("KotlinxCoroutinesGuideFlow33") { kotlinx.coroutines.guide.flow33.main() }.verifyLines( + fun testExampleFlow33() { + test("ExampleFlow33") { kotlinx.coroutines.guide.exampleFlow33.main() }.verifyLines( "1", "Flow completed exceptionally", "Caught exception" @@ -351,8 +354,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow34() { - test("KotlinxCoroutinesGuideFlow34") { kotlinx.coroutines.guide.flow34.main() }.verifyExceptions( + fun testExampleFlow34() { + test("ExampleFlow34") { kotlinx.coroutines.guide.exampleFlow34.main() }.verifyExceptions( "1", "Flow completed with null", "Exception in thread \"main\" java.lang.IllegalStateException: Collected 2" @@ -360,8 +363,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow35() { - test("KotlinxCoroutinesGuideFlow35") { kotlinx.coroutines.guide.flow35.main() }.verifyLines( + fun testExampleFlow35() { + test("ExampleFlow35") { kotlinx.coroutines.guide.exampleFlow35.main() }.verifyLines( "Event: 1", "Event: 2", "Event: 3", @@ -370,8 +373,8 @@ class FlowGuideTest { } @Test - fun testKotlinxCoroutinesGuideFlow36() { - test("KotlinxCoroutinesGuideFlow36") { kotlinx.coroutines.guide.flow36.main() }.verifyLines( + fun testExampleFlow36() { + test("ExampleFlow36") { kotlinx.coroutines.guide.exampleFlow36.main() }.verifyLines( "Done", "Event: 1", "Event: 2", diff --git a/kotlinx-coroutines-core/jvm/test/guide/test/SelectGuideTest.kt b/kotlinx-coroutines-core/jvm/test/guide/test/SelectGuideTest.kt index b5246ff4be..e3f47b9648 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/test/SelectGuideTest.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/test/SelectGuideTest.kt @@ -1,13 +1,16 @@ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from select-expression.md by Knit tool. Do not edit. package kotlinx.coroutines.guide.test import org.junit.Test class SelectGuideTest { - @Test - fun testKotlinxCoroutinesGuideSelect01() { - test("KotlinxCoroutinesGuideSelect01") { kotlinx.coroutines.guide.select01.main() }.verifyLines( + fun testExampleSelect01() { + test("ExampleSelect01") { kotlinx.coroutines.guide.exampleSelect01.main() }.verifyLines( "fizz -> 'Fizz'", "buzz -> 'Buzz!'", "fizz -> 'Fizz'", @@ -19,8 +22,8 @@ class SelectGuideTest { } @Test - fun testKotlinxCoroutinesGuideSelect02() { - test("KotlinxCoroutinesGuideSelect02") { kotlinx.coroutines.guide.select02.main() }.verifyLines( + fun testExampleSelect02() { + test("ExampleSelect02") { kotlinx.coroutines.guide.exampleSelect02.main() }.verifyLines( "a -> 'Hello 0'", "a -> 'Hello 1'", "b -> 'World 0'", @@ -33,8 +36,8 @@ class SelectGuideTest { } @Test - fun testKotlinxCoroutinesGuideSelect03() { - test("KotlinxCoroutinesGuideSelect03") { kotlinx.coroutines.guide.select03.main() }.verifyLines( + fun testExampleSelect03() { + test("ExampleSelect03") { kotlinx.coroutines.guide.exampleSelect03.main() }.verifyLines( "Consuming 1", "Side channel has 2", "Side channel has 3", @@ -50,16 +53,16 @@ class SelectGuideTest { } @Test - fun testKotlinxCoroutinesGuideSelect04() { - test("KotlinxCoroutinesGuideSelect04") { kotlinx.coroutines.guide.select04.main() }.verifyLines( + fun testExampleSelect04() { + test("ExampleSelect04") { kotlinx.coroutines.guide.exampleSelect04.main() }.verifyLines( "Deferred 4 produced answer 'Waited for 128 ms'", "11 coroutines are still active" ) } @Test - fun testKotlinxCoroutinesGuideSelect05() { - test("KotlinxCoroutinesGuideSelect05") { kotlinx.coroutines.guide.select05.main() }.verifyLines( + fun testExampleSelect05() { + test("ExampleSelect05") { kotlinx.coroutines.guide.exampleSelect05.main() }.verifyLines( "BEGIN", "Replace", "END", diff --git a/kotlinx-coroutines-core/jvm/test/guide/test/SharedStateGuideTest.kt b/kotlinx-coroutines-core/jvm/test/guide/test/SharedStateGuideTest.kt index 45988570ba..8d534a09ea 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/test/SharedStateGuideTest.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/test/SharedStateGuideTest.kt @@ -1,61 +1,64 @@ -// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from shared-mutable-state-and-concurrency.md by Knit tool. Do not edit. package kotlinx.coroutines.guide.test import org.junit.Test class SharedStateGuideTest { - @Test - fun testKotlinxCoroutinesGuideSync01() { - test("KotlinxCoroutinesGuideSync01") { kotlinx.coroutines.guide.sync01.main() }.verifyLinesStart( + fun testExampleSync01() { + test("ExampleSync01") { kotlinx.coroutines.guide.exampleSync01.main() }.verifyLinesStart( "Completed 100000 actions in", "Counter =" ) } @Test - fun testKotlinxCoroutinesGuideSync02() { - test("KotlinxCoroutinesGuideSync02") { kotlinx.coroutines.guide.sync02.main() }.verifyLinesStart( + fun testExampleSync02() { + test("ExampleSync02") { kotlinx.coroutines.guide.exampleSync02.main() }.verifyLinesStart( "Completed 100000 actions in", "Counter =" ) } @Test - fun testKotlinxCoroutinesGuideSync03() { - test("KotlinxCoroutinesGuideSync03") { kotlinx.coroutines.guide.sync03.main() }.verifyLinesArbitraryTime( + fun testExampleSync03() { + test("ExampleSync03") { kotlinx.coroutines.guide.exampleSync03.main() }.verifyLinesArbitraryTime( "Completed 100000 actions in xxx ms", "Counter = 100000" ) } @Test - fun testKotlinxCoroutinesGuideSync04() { - test("KotlinxCoroutinesGuideSync04") { kotlinx.coroutines.guide.sync04.main() }.verifyLinesArbitraryTime( + fun testExampleSync04() { + test("ExampleSync04") { kotlinx.coroutines.guide.exampleSync04.main() }.verifyLinesArbitraryTime( "Completed 100000 actions in xxx ms", "Counter = 100000" ) } @Test - fun testKotlinxCoroutinesGuideSync05() { - test("KotlinxCoroutinesGuideSync05") { kotlinx.coroutines.guide.sync05.main() }.verifyLinesArbitraryTime( + fun testExampleSync05() { + test("ExampleSync05") { kotlinx.coroutines.guide.exampleSync05.main() }.verifyLinesArbitraryTime( "Completed 100000 actions in xxx ms", "Counter = 100000" ) } @Test - fun testKotlinxCoroutinesGuideSync06() { - test("KotlinxCoroutinesGuideSync06") { kotlinx.coroutines.guide.sync06.main() }.verifyLinesArbitraryTime( + fun testExampleSync06() { + test("ExampleSync06") { kotlinx.coroutines.guide.exampleSync06.main() }.verifyLinesArbitraryTime( "Completed 100000 actions in xxx ms", "Counter = 100000" ) } @Test - fun testKotlinxCoroutinesGuideSync07() { - test("KotlinxCoroutinesGuideSync07") { kotlinx.coroutines.guide.sync07.main() }.verifyLinesArbitraryTime( + fun testExampleSync07() { + test("ExampleSync07") { kotlinx.coroutines.guide.exampleSync07.main() }.verifyLinesArbitraryTime( "Completed 100000 actions in xxx ms", "Counter = 100000" ) diff --git a/kotlinx-coroutines-core/jvm/test/guide/test/TestUtil.kt b/kotlinx-coroutines-core/jvm/test/guide/test/TestUtil.kt index bd7159fb08..fb1c85bce1 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/test/TestUtil.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/test/TestUtil.kt @@ -7,9 +7,9 @@ package kotlinx.coroutines.guide.test import kotlinx.coroutines.* import kotlinx.coroutines.internal.* import kotlinx.coroutines.scheduling.* -import org.junit.Assert.* -import java.io.* +import kotlinx.knit.test.* import java.util.concurrent.* +import kotlin.test.* fun wrapTask(block: Runnable) = kotlinx.coroutines.wrapTask(block) @@ -25,81 +25,35 @@ private inline fun outputException(name: String, block: () -> T): T = private const val SHUTDOWN_TIMEOUT = 5000L // 5 sec at most to wait private val OUT_ENABLED = systemProp("guide.tests.sout", false) -@Suppress("DEPRECATION") fun test(name: String, block: () -> R): List = outputException(name) { - val sout = System.out - val oldOut = if (OUT_ENABLED) System.out else NullOut - val oldErr = System.err - val bytesOut = ByteArrayOutputStream() - val tee = TeeOutput(bytesOut, oldOut) - val ps = PrintStream(tee) - - oldOut.println("--- Running test$name") - System.setErr(ps) - System.setOut(ps) - CommonPool.usePrivatePool() - DefaultScheduler.usePrivateScheduler() - DefaultExecutor.shutdown(SHUTDOWN_TIMEOUT) - resetCoroutineId() - val threadsBefore = currentThreads() - var bytes = ByteArray(0) - withVirtualTimeSource(oldOut) { - try { - val result = block() - require(result === Unit) { "Test 'main' shall return Unit" } - } catch (e: Throwable) { - System.err.print("Exception in thread \"main\" ") - e.printStackTrace() - } finally { - // capture output - bytes = bytesOut.toByteArray() - oldOut.println("--- shutting down") - // the shutdown - CommonPool.shutdown(SHUTDOWN_TIMEOUT) - DefaultScheduler.shutdown(SHUTDOWN_TIMEOUT) - shutdownDispatcherPools(SHUTDOWN_TIMEOUT) - DefaultExecutor.shutdown(SHUTDOWN_TIMEOUT) // the last man standing -- cleanup all pending tasks - if (tee.flushLine()) oldOut.println() - oldOut.println("--- done") - System.setOut(sout) - System.setErr(oldErr) - checkTestThreads(threadsBefore) - } - } - CommonPool.restore() - DefaultScheduler.restore() - return ByteArrayInputStream(bytes).bufferedReader().readLines() -} - -private class TeeOutput( - private val bytesOut: OutputStream, - private val oldOut: PrintStream -) : OutputStream() { - val limit = 200 - var lineLength = 0 - - fun flushLine(): Boolean { - if (lineLength > limit) - oldOut.print(" ($lineLength chars in total)") - val result = lineLength > 0 - lineLength = 0 - return result - } - - override fun write(b: Int) { - bytesOut.write(b) - if (b == 0x0d || b == 0x0a) { // new line - flushLine() - oldOut.write(b) - } else { - lineLength++ - if (lineLength <= limit) - oldOut.write(b) + try { + captureOutput(name, stdoutEnabled = OUT_ENABLED) { log -> + CommonPool.usePrivatePool() + DefaultScheduler.usePrivateScheduler() + DefaultExecutor.shutdown(SHUTDOWN_TIMEOUT) + resetCoroutineId() + val threadsBefore = currentThreads() + try { + withVirtualTimeSource(log) { + val result = block() + require(result === Unit) { "Test 'main' shall return Unit" } + } + } finally { + // the shutdown + log.println("--- shutting down") + CommonPool.shutdown(SHUTDOWN_TIMEOUT) + DefaultScheduler.shutdown(SHUTDOWN_TIMEOUT) + shutdownDispatcherPools(SHUTDOWN_TIMEOUT) + DefaultExecutor.shutdown(SHUTDOWN_TIMEOUT) // the last man standing -- cleanup all pending tasks + } + checkTestThreads(threadsBefore) // check thread if the main completed successfully } + } finally { + CommonPool.restore() + DefaultScheduler.restore() } } - private fun shutdownDispatcherPools(timeout: Long) { val threads = arrayOfNulls(Thread.activeCount()) val n = Thread.enumerate(threads) @@ -145,7 +99,7 @@ private fun List.verifyCommonLines(expected: Array, mode: Sa for (i in 0 until n) { val exp = sanitize(expected[i], mode) val act = sanitize(get(i), mode) - assertEquals("Line ${i + 1}", exp, act) + assertEquals(exp, act, "Line ${i + 1}") } } @@ -163,7 +117,7 @@ fun List.verifyLines(vararg expected: String) = verify { fun List.verifyLinesStartWith(vararg expected: String) = verify { verifyCommonLines(expected) - assertTrue("Number of lines", expected.size <= size) + assertTrue(expected.size <= size, "Number of lines") } fun List.verifyLinesArbitraryTime(vararg expected: String) = verify { @@ -197,7 +151,7 @@ fun List.verifyExceptions(vararg expected: String) { for (i in 0 until n) { val exp = sanitize(expected[i], SanitizeMode.FLEXIBLE_THREAD) val act = sanitize(actual[i], SanitizeMode.FLEXIBLE_THREAD) - assertEquals("Line ${i + 1}", exp, act) + assertEquals(exp, act, "Line ${i + 1}") } } @@ -207,17 +161,11 @@ fun List.verifyLinesStart(vararg expected: String) = verify { for (i in 0 until n) { val exp = sanitize(expected[i], SanitizeMode.FLEXIBLE_THREAD) val act = sanitize(get(i), SanitizeMode.FLEXIBLE_THREAD) - assertEquals("Line ${i + 1}", exp, act.substring(0, minOf(act.length, exp.length))) + assertEquals(exp, act.substring(0, minOf(act.length, exp.length)), "Line ${i + 1}") } checkEqualNumberOfLines(expected) } -private object NullOut : PrintStream(NullOutputStream()) - -private class NullOutputStream : OutputStream() { - override fun write(b: Int) = Unit -} - private inline fun List.verify(verification: () -> Unit) { try { verification() @@ -226,7 +174,6 @@ private inline fun List.verify(verification: () -> Unit) { println("Printing [delayed] test output") forEach { println(it) } } - throw t } } \ No newline at end of file diff --git a/kotlinx-coroutines-core/jvm/test/internal/LockFreeLinkedListAddRemoveStressTest.kt b/kotlinx-coroutines-core/jvm/test/internal/LockFreeLinkedListAddRemoveStressTest.kt new file mode 100644 index 0000000000..3229e664c1 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/internal/LockFreeLinkedListAddRemoveStressTest.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.internal + +import kotlinx.atomicfu.* +import kotlinx.coroutines.* +import java.util.concurrent.* +import kotlin.concurrent.* +import kotlin.test.* + +class LockFreeLinkedListAddRemoveStressTest : TestBase() { + private class Node : LockFreeLinkedListNode() + + private val nRepeat = 100_000 * stressTestMultiplier + private val list = LockFreeLinkedListHead() + private val barrier = CyclicBarrier(3) + private val done = atomic(false) + private val removed = atomic(0) + + @Test + fun testStressAddRemove() { + val threads = ArrayList() + threads += testThread("adder") { + val node = Node() + list.addLast(node) + if (node.remove()) removed.incrementAndGet() + } + threads += testThread("remover") { + val node = list.removeFirstOrNull() + if (node != null) removed.incrementAndGet() + } + try { + for (i in 1..nRepeat) { + barrier.await() + barrier.await() + assertEquals(i, removed.value) + list.validate() + } + } finally { + done.value = true + barrier.await() + threads.forEach { it.join() } + } + } + + private fun testThread(name: String, op: () -> Unit) = thread(name = name) { + while (true) { + barrier.await() + if (done.value) break + op() + barrier.await() + } + } +} \ No newline at end of file diff --git a/kotlinx-coroutines-core/jvm/test/internal/LockFreeLinkedListAtomicLFStressTest.kt b/kotlinx-coroutines-core/jvm/test/internal/LockFreeLinkedListAtomicLFStressTest.kt index e5c4c2c898..225b848186 100644 --- a/kotlinx-coroutines-core/jvm/test/internal/LockFreeLinkedListAtomicLFStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/internal/LockFreeLinkedListAtomicLFStressTest.kt @@ -1,16 +1,16 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal import kotlinx.atomicfu.LockFreedomTestEnvironment import kotlinx.coroutines.stressTestMultiplier -import org.junit.Assert.* import org.junit.Test import java.util.* import java.util.concurrent.atomic.AtomicLong import java.util.concurrent.atomic.AtomicReference +import kotlin.test.* /** * This stress test has 4 threads adding randomly to the list and them immediately undoing @@ -19,9 +19,9 @@ import java.util.concurrent.atomic.AtomicReference class LockFreeLinkedListAtomicLFStressTest { private val env = LockFreedomTestEnvironment("LockFreeLinkedListAtomicLFStressTest") - data class IntNode(val i: Int) : LockFreeLinkedListNode() + private data class Node(val i: Long) : LockFreeLinkedListNode() - private val TEST_DURATION_SEC = 5 * stressTestMultiplier + private val nSeconds = 5 * stressTestMultiplier private val nLists = 4 private val nAdderThreads = 4 @@ -32,7 +32,8 @@ class LockFreeLinkedListAtomicLFStressTest { private val undone = AtomicLong() private val missed = AtomicLong() private val removed = AtomicLong() - val error = AtomicReference() + private val error = AtomicReference() + private val index = AtomicLong() @Test fun testStress() { @@ -42,7 +43,7 @@ class LockFreeLinkedListAtomicLFStressTest { when (rnd.nextInt(4)) { 0 -> { val list = lists[rnd.nextInt(nLists)] - val node = IntNode(threadId) + val node = Node(index.incrementAndGet()) addLastOp(list, node) randomSpinWaitIntermission() tryRemoveOp(node) @@ -50,7 +51,7 @@ class LockFreeLinkedListAtomicLFStressTest { 1 -> { // just to test conditional add val list = lists[rnd.nextInt(nLists)] - val node = IntNode(threadId) + val node = Node(index.incrementAndGet()) addLastIfTrueOp(list, node) randomSpinWaitIntermission() tryRemoveOp(node) @@ -58,7 +59,7 @@ class LockFreeLinkedListAtomicLFStressTest { 2 -> { // just to test failed conditional add and burn some time val list = lists[rnd.nextInt(nLists)] - val node = IntNode(threadId) + val node = Node(index.incrementAndGet()) addLastIfFalseOp(list, node) } 3 -> { @@ -68,8 +69,8 @@ class LockFreeLinkedListAtomicLFStressTest { check(idx1 < idx2) // that is our global order val list1 = lists[idx1] val list2 = lists[idx2] - val node1 = IntNode(threadId) - val node2 = IntNode(-threadId - 1) + val node1 = Node(index.incrementAndGet()) + val node2 = Node(index.incrementAndGet()) addTwoOp(list1, node1, list2, node2) randomSpinWaitIntermission() tryRemoveOp(node1) @@ -91,13 +92,13 @@ class LockFreeLinkedListAtomicLFStressTest { removeTwoOp(list1, list2) } } - env.performTest(TEST_DURATION_SEC) { - val _undone = undone.get() - val _missed = missed.get() - val _removed = removed.get() - println(" Adders undone $_undone node additions") - println(" Adders missed $_missed nodes") - println("Remover removed $_removed nodes") + env.performTest(nSeconds) { + val undone = undone.get() + val missed = missed.get() + val removed = removed.get() + println(" Adders undone $undone node additions") + println(" Adders missed $missed nodes") + println("Remover removed $removed nodes") } error.get()?.let { throw it } assertEquals(missed.get(), removed.get()) @@ -106,19 +107,19 @@ class LockFreeLinkedListAtomicLFStressTest { lists.forEach { it.validate() } } - private fun addLastOp(list: LockFreeLinkedListHead, node: IntNode) { + private fun addLastOp(list: LockFreeLinkedListHead, node: Node) { list.addLast(node) } - private fun addLastIfTrueOp(list: LockFreeLinkedListHead, node: IntNode) { - assertTrue(list.addLastIf(node, { true })) + private fun addLastIfTrueOp(list: LockFreeLinkedListHead, node: Node) { + assertTrue(list.addLastIf(node) { true }) } - private fun addLastIfFalseOp(list: LockFreeLinkedListHead, node: IntNode) { - assertFalse(list.addLastIf(node, { false })) + private fun addLastIfFalseOp(list: LockFreeLinkedListHead, node: Node) { + assertFalse(list.addLastIf(node) { false }) } - private fun addTwoOp(list1: LockFreeLinkedListHead, node1: IntNode, list2: LockFreeLinkedListHead, node2: IntNode) { + private fun addTwoOp(list1: LockFreeLinkedListHead, node1: Node, list2: LockFreeLinkedListHead, node2: Node) { val add1 = list1.describeAddLast(node1) val add2 = list2.describeAddLast(node2) val op = object : AtomicOp() { @@ -138,7 +139,7 @@ class LockFreeLinkedListAtomicLFStressTest { assertTrue(op.perform(null) == null) } - private fun tryRemoveOp(node: IntNode) { + private fun tryRemoveOp(node: Node) { if (node.remove()) undone.incrementAndGet() else @@ -165,5 +166,4 @@ class LockFreeLinkedListAtomicLFStressTest { val success = op.perform(null) == null if (success) removed.addAndGet(2) } - } \ No newline at end of file diff --git a/kotlinx-coroutines-core/jvm/test/internal/LockFreeLinkedListShortStressTest.kt b/kotlinx-coroutines-core/jvm/test/internal/LockFreeLinkedListShortStressTest.kt index 54932ec1d5..2ac51b9b1d 100644 --- a/kotlinx-coroutines-core/jvm/test/internal/LockFreeLinkedListShortStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/internal/LockFreeLinkedListShortStressTest.kt @@ -5,11 +5,11 @@ package kotlinx.coroutines.internal import kotlinx.coroutines.* -import org.junit.* -import org.junit.Assert.* +import org.junit.Test import java.util.* import java.util.concurrent.atomic.* import kotlin.concurrent.* +import kotlin.test.* /** * This stress test has 6 threads adding randomly first to the list and them immediately undoing diff --git a/kotlinx-coroutines-core/jvm/test/internal/LockFreeLinkedListTest.kt b/kotlinx-coroutines-core/jvm/test/internal/LockFreeLinkedListTest.kt index 9de11f792e..b9011448cd 100644 --- a/kotlinx-coroutines-core/jvm/test/internal/LockFreeLinkedListTest.kt +++ b/kotlinx-coroutines-core/jvm/test/internal/LockFreeLinkedListTest.kt @@ -4,8 +4,8 @@ package kotlinx.coroutines.internal -import org.junit.Assert.* import org.junit.Test +import kotlin.test.* class LockFreeLinkedListTest { data class IntNode(val i: Int) : LockFreeLinkedListNode() @@ -79,7 +79,7 @@ class LockFreeLinkedListTest { var index = 0 list.forEach { actual[index++] = it.i } assertEquals(n, index) - for (i in 0 until n) assertEquals("item i", expected[i], actual[i]) + for (i in 0 until n) assertEquals(expected[i], actual[i], "item $i") assertEquals(expected.isEmpty(), list.isEmpty) } } \ No newline at end of file diff --git a/kotlinx-coroutines-core/jvm/test/internal/SegmentQueueLCStressTest.kt b/kotlinx-coroutines-core/jvm/test/internal/SegmentQueueLCStressTest.kt deleted file mode 100644 index c8493f6f30..0000000000 --- a/kotlinx-coroutines-core/jvm/test/internal/SegmentQueueLCStressTest.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.coroutines.internal - -import com.devexperts.dxlab.lincheck.LinChecker -import com.devexperts.dxlab.lincheck.annotations.Operation -import com.devexperts.dxlab.lincheck.annotations.Param -import com.devexperts.dxlab.lincheck.paramgen.IntGen -import com.devexperts.dxlab.lincheck.strategy.stress.StressCTest -import org.junit.Test - -@StressCTest -class SegmentQueueLCStressTest { - private val q = SegmentBasedQueue() - - @Operation - fun add(@Param(gen = IntGen::class) x: Int) { - q.enqueue(x) - } - - @Operation - fun poll(): Int? = q.dequeue() - - @Test - fun test() { - LinChecker.check(SegmentQueueLCStressTest::class.java) - } -} \ No newline at end of file diff --git a/kotlinx-coroutines-core/jvm/test/internal/SegmentQueueTest.kt b/kotlinx-coroutines-core/jvm/test/internal/SegmentQueueTest.kt index 89cd83dac8..b59a6488a0 100644 --- a/kotlinx-coroutines-core/jvm/test/internal/SegmentQueueTest.kt +++ b/kotlinx-coroutines-core/jvm/test/internal/SegmentQueueTest.kt @@ -7,14 +7,14 @@ import java.util.concurrent.CyclicBarrier import java.util.concurrent.atomic.AtomicInteger import kotlin.concurrent.thread import kotlin.random.Random -import kotlin.test.assertEquals +import kotlin.test.* class SegmentQueueTest : TestBase() { @Test fun testSimpleTest() { val q = SegmentBasedQueue() assertEquals(1, q.numberOfSegments) - assertEquals(null, q.dequeue()) + assertNull(q.dequeue()) q.enqueue(1) assertEquals(1, q.numberOfSegments) q.enqueue(2) @@ -23,7 +23,7 @@ class SegmentQueueTest : TestBase() { assertEquals(2, q.numberOfSegments) assertEquals(2, q.dequeue()) assertEquals(1, q.numberOfSegments) - assertEquals(null, q.dequeue()) + assertNull(q.dequeue()) } @Test @@ -37,7 +37,7 @@ class SegmentQueueTest : TestBase() { assertEquals(2, q.numberOfSegments) assertEquals(1, q.dequeue()) assertEquals(3, q.dequeue()) - assertEquals(null, q.dequeue()) + assertNull(q.dequeue()) } @Test @@ -49,7 +49,7 @@ class SegmentQueueTest : TestBase() { q.enqueue(3) s.removeSegment() assertEquals(3, q.dequeue()) - assertEquals(null, q.dequeue()) + assertNull(q.dequeue()) } @Test diff --git a/kotlinx-coroutines-core/jvm/test/internal/ThreadSafeHeapTest.kt b/kotlinx-coroutines-core/jvm/test/internal/ThreadSafeHeapTest.kt index 12b3540547..be7ed91a3f 100644 --- a/kotlinx-coroutines-core/jvm/test/internal/ThreadSafeHeapTest.kt +++ b/kotlinx-coroutines-core/jvm/test/internal/ThreadSafeHeapTest.kt @@ -21,7 +21,7 @@ class ThreadSafeHeapTest : TestBase() { @Test fun testBasic() { val h = ThreadSafeHeap() - assertEquals(null, h.peek()) + assertNull(h.peek()) val n1 = Node(1) h.addLast(n1) assertEquals(n1, h.peek()) @@ -47,7 +47,7 @@ class ThreadSafeHeapTest : TestBase() { h.remove(n3) assertEquals(n5, h.peek()) h.remove(n5) - assertEquals(null, h.peek()) + assertNull(h.peek()) } @Test @@ -59,7 +59,7 @@ class ThreadSafeHeapTest : TestBase() { repeat(n) { h.addLast(Node(a[it])) } a.sort() repeat(n) { assertEquals(Node(a[it]), h.removeFirstOrNull()) } - assertEquals(null, h.peek()) + assertNull(h.peek()) } @Test diff --git a/kotlinx-coroutines-core/jvm/test/linearizability/ChannelCloseLCStressTest.kt b/kotlinx-coroutines-core/jvm/test/linearizability/ChannelCloseLCStressTest.kt deleted file mode 100644 index 5bdc2841dc..0000000000 --- a/kotlinx-coroutines-core/jvm/test/linearizability/ChannelCloseLCStressTest.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ -@file:Suppress("unused") - -package kotlinx.coroutines.linearizability - -import com.devexperts.dxlab.lincheck.* -import com.devexperts.dxlab.lincheck.annotations.* -import com.devexperts.dxlab.lincheck.paramgen.* -import com.devexperts.dxlab.lincheck.strategy.stress.* -import kotlinx.coroutines.* -import kotlinx.coroutines.channels.* -import org.junit.* -import java.io.* - -/** - * This is stress test that is fine-tuned to catch the problem - * [#1419](https://github.com/Kotlin/kotlinx.coroutines/issues/1419) - */ -@Param(name = "value", gen = IntGen::class, conf = "2:2") -@OpGroupConfig.OpGroupConfigs( - OpGroupConfig(name = "send", nonParallel = true), - OpGroupConfig(name = "receive", nonParallel = true), - OpGroupConfig(name = "close", nonParallel = true) -) -class ChannelCloseLCStressTest : TestBase() { - - private companion object { - // Emulating ctor argument for lincheck - var capacity = 0 - } - - private val lt = LinTesting() - private var channel: Channel = Channel(capacity) - - @Operation(runOnce = true, group = "send") - fun send1(@Param(name = "value") value: Int) = lt.run("send1") { channel.send(value) } - - @Operation(runOnce = true, group = "send") - fun send2(@Param(name = "value") value: Int) = lt.run("send2") { channel.send(value) } - - @Operation(runOnce = true, group = "receive") - fun receive1() = lt.run("receive1") { channel.receive() } - - @Operation(runOnce = true, group = "receive") - fun receive2() = lt.run("receive2") { channel.receive() } - - @Operation(runOnce = true, group = "close") - fun close1() = lt.run("close1") { channel.close(IOException("close1")) } - - @Operation(runOnce = true, group = "close") - fun close2() = lt.run("close2") { channel.close(IOException("close2")) } - - @Test - fun testRendezvousChannelLinearizability() { - runTest(0) - } - - @Test - fun testArrayChannelLinearizability() { - for (i in listOf(1, 2, 16)) { - runTest(i) - } - } - - @Test - fun testConflatedChannelLinearizability() = runTest(Channel.CONFLATED) - - @Test - fun testUnlimitedChannelLinearizability() = runTest(Channel.UNLIMITED) - - private fun runTest(capacity: Int) { - ChannelCloseLCStressTest.capacity = capacity - val options = StressOptions() - .iterations(1) // only one iteration -- test scenario is fixed - .invocationsPerIteration(10_000 * stressTestMultiplierSqrt) - .threads(3) - .verifier(LinVerifier::class.java) - LinChecker.check(ChannelCloseLCStressTest::class.java, options) - } -} diff --git a/kotlinx-coroutines-core/jvm/test/linearizability/ChannelIsClosedLCStressTest.kt b/kotlinx-coroutines-core/jvm/test/linearizability/ChannelIsClosedLCStressTest.kt deleted file mode 100644 index 44ba182dd3..0000000000 --- a/kotlinx-coroutines-core/jvm/test/linearizability/ChannelIsClosedLCStressTest.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ -@file:Suppress("unused") - -package kotlinx.coroutines.linearizability - -import com.devexperts.dxlab.lincheck.* -import com.devexperts.dxlab.lincheck.annotations.* -import com.devexperts.dxlab.lincheck.paramgen.* -import com.devexperts.dxlab.lincheck.strategy.stress.* -import kotlinx.coroutines.* -import kotlinx.coroutines.channels.* -import org.junit.* -import java.io.* - -@Param(name = "value", gen = IntGen::class, conf = "1:3") -class ChannelIsClosedLCStressTest : TestBase() { - - private val lt = LinTesting() - private val channel = Channel() - - @Operation(runOnce = true) - fun send1(@Param(name = "value") value: Int) = lt.run("send1") { channel.send(value) } - - @Operation(runOnce = true) - fun send2(@Param(name = "value") value: Int) = lt.run("send2") { channel.send(value) } - - @Operation(runOnce = true) - fun receive1() = lt.run("receive1") { channel.receive() } - - @Operation(runOnce = true) - fun receive2() = lt.run("receive2") { channel.receive() } - - @Operation(runOnce = true) - fun close1() = lt.run("close1") { channel.close(IOException("close1")) } - - @Operation(runOnce = true) - fun isClosedForReceive() = lt.run("isClosedForReceive") { channel.isClosedForReceive } - - @Operation(runOnce = true) - fun isClosedForSend() = lt.run("isClosedForSend") { channel.isClosedForSend } - - @Test - fun testLinearizability() { - val options = StressOptions() - .iterations(100 * stressTestMultiplierSqrt) - .invocationsPerIteration(1000 * stressTestMultiplierSqrt) - .threads(3) - .verifier(LinVerifier::class.java) - - LinChecker.check(ChannelIsClosedLCStressTest::class.java, options) - } -} diff --git a/kotlinx-coroutines-core/jvm/test/linearizability/ChannelLCStressTest.kt b/kotlinx-coroutines-core/jvm/test/linearizability/ChannelLCStressTest.kt deleted file mode 100644 index f4b775631f..0000000000 --- a/kotlinx-coroutines-core/jvm/test/linearizability/ChannelLCStressTest.kt +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ -@file:Suppress("unused") - -package kotlinx.coroutines.linearizability - -import com.devexperts.dxlab.lincheck.* -import com.devexperts.dxlab.lincheck.annotations.* -import com.devexperts.dxlab.lincheck.paramgen.* -import com.devexperts.dxlab.lincheck.strategy.stress.* -import kotlinx.coroutines.* -import kotlinx.coroutines.channels.* -import org.junit.* -import java.io.* - -@Param(name = "value", gen = IntGen::class, conf = "1:3") -class ChannelLCStressTest : TestBase() { - - private companion object { - // Emulating ctor argument for lincheck - var capacity = 0 - } - - private val lt = LinTesting() - private var channel: Channel = Channel(capacity) - - @Operation(runOnce = true) - fun send1(@Param(name = "value") value: Int) = lt.run("send1") { channel.send(value) } - - @Operation(runOnce = true) - fun send2(@Param(name = "value") value: Int) = lt.run("send2") { channel.send(value) } - - @Operation(runOnce = true) - fun receive1() = lt.run("receive1") { channel.receive() } - - @Operation(runOnce = true) - fun receive2() = lt.run("receive2") { channel.receive() } - - @Operation(runOnce = true) - fun close1() = lt.run("close1") { channel.close(IOException("close1")) } - - @Operation(runOnce = true) - fun close2() = lt.run("close2") { channel.close(IOException("close2")) } - - @Test - fun testRendezvousChannelLinearizability() { - runTest(0) - } - - @Test - fun testArrayChannelLinearizability() { - for (i in listOf(1, 2, 16)) { - runTest(i) - } - } - - @Test - fun testConflatedChannelLinearizability() = runTest(Channel.CONFLATED) - - @Test - fun testUnlimitedChannelLinearizability() = runTest(Channel.UNLIMITED) - - private fun runTest(capacity: Int) { - ChannelLCStressTest.capacity = capacity - val options = StressOptions() - .iterations(50 * stressTestMultiplierSqrt) - .invocationsPerIteration(500 * stressTestMultiplierSqrt) - .threads(3) - .verifier(LinVerifier::class.java) - LinChecker.check(ChannelLCStressTest::class.java, options) - } -} diff --git a/kotlinx-coroutines-core/jvm/test/linearizability/ChannelsLCStressTest.kt b/kotlinx-coroutines-core/jvm/test/linearizability/ChannelsLCStressTest.kt new file mode 100644 index 0000000000..8836fdc7be --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/linearizability/ChannelsLCStressTest.kt @@ -0,0 +1,224 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ +@file:Suppress("unused") + +package kotlinx.coroutines.linearizability + +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.* +import kotlinx.coroutines.channels.Channel.Factory.CONFLATED +import kotlinx.coroutines.channels.Channel.Factory.RENDEZVOUS +import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED +import kotlinx.coroutines.selects.* +import org.jetbrains.kotlinx.lincheck.annotations.* +import org.jetbrains.kotlinx.lincheck.annotations.Operation +import org.jetbrains.kotlinx.lincheck.paramgen.* +import org.jetbrains.kotlinx.lincheck.verifier.* +import org.junit.* + +class RendezvousChannelLCStressTest : ChannelLCStressTestBase( + c = Channel(RENDEZVOUS), + sequentialSpecification = SequentialRendezvousChannel::class.java +) +class SequentialRendezvousChannel : SequentialIntChannelBase(RENDEZVOUS) + +class Array1ChannelLCStressTest : ChannelLCStressTestBase( + c = Channel(1), + sequentialSpecification = SequentialArray1RendezvousChannel::class.java +) +class SequentialArray1RendezvousChannel : SequentialIntChannelBase(1) + +class Array2ChannelLCStressTest : ChannelLCStressTestBase( + c = Channel(2), + sequentialSpecification = SequentialArray2RendezvousChannel::class.java +) +class SequentialArray2RendezvousChannel : SequentialIntChannelBase(2) + +class UnlimitedChannelLCStressTest : ChannelLCStressTestBase( + c = Channel(UNLIMITED), + sequentialSpecification = SequentialUnlimitedChannel::class.java +) +class SequentialUnlimitedChannel : SequentialIntChannelBase(UNLIMITED) + +class ConflatedChannelLCStressTest : ChannelLCStressTestBase( + c = Channel(CONFLATED), + sequentialSpecification = SequentialConflatedChannel::class.java +) +class SequentialConflatedChannel : SequentialIntChannelBase(CONFLATED) + +@Param.Params( + Param(name = "value", gen = IntGen::class, conf = "1:5"), + Param(name = "closeToken", gen = IntGen::class, conf = "1:3") +) +abstract class ChannelLCStressTestBase(private val c: Channel, private val sequentialSpecification: Class<*>) { + @Operation + suspend fun send(@Param(name = "value") value: Int): Any = try { + c.send(value) + } catch (e: NumberedCancellationException) { + e.testResult + } + + @Operation + fun offer(@Param(name = "value") value: Int): Any = try { + c.offer(value) + } catch (e: NumberedCancellationException) { + e.testResult + } + + // TODO: this operation should be (and can be!) linearizable, but is not + // @Operation + suspend fun sendViaSelect(@Param(name = "value") value: Int): Any = try { + select { c.onSend(value) {} } + } catch (e: NumberedCancellationException) { + e.testResult + } + + @Operation + suspend fun receive(): Any = try { + c.receive() + } catch (e: NumberedCancellationException) { + e.testResult + } + + @Operation + fun poll(): Any? = try { + c.poll() + } catch (e: NumberedCancellationException) { + e.testResult + } + + // TODO: this operation should be (and can be!) linearizable, but is not + // @Operation + suspend fun receiveViaSelect(): Any = try { + select { c.onReceive { it } } + } catch (e: NumberedCancellationException) { + e.testResult + } + + @Operation + fun close(@Param(name = "closeToken") token: Int): Boolean = c.close(NumberedCancellationException(token)) + + // TODO: this operation should be (and can be!) linearizable, but is not + // @Operation + fun cancel(@Param(name = "closeToken") token: Int) = c.cancel(NumberedCancellationException(token)) + + // @Operation + fun isClosedForReceive() = c.isClosedForReceive + + // @Operation + fun isClosedForSend() = c.isClosedForSend + + // TODO: this operation should be (and can be!) linearizable, but is not + // @Operation + fun isEmpty() = c.isEmpty + + @Test + fun test() = LCStressOptionsDefault() + .actorsBefore(0) + .sequentialSpecification(sequentialSpecification) + .check(this::class) +} + +private class NumberedCancellationException(number: Int) : CancellationException() { + val testResult = "Closed($number)" +} + + +abstract class SequentialIntChannelBase(private val capacity: Int) : VerifierState() { + private val senders = ArrayList, Int>>() + private val receivers = ArrayList>() + private val buffer = ArrayList() + private var closedMessage: String? = null + + suspend fun send(x: Int): Any = when (val offerRes = offer(x)) { + true -> Unit + false -> suspendCancellableCoroutine { cont -> + senders.add(cont to x) + } + else -> offerRes + } + + fun offer(element: Int): Any { + if (closedMessage !== null) return closedMessage!! + if (capacity == CONFLATED) { + if (resumeFirstReceiver(element)) return true + buffer.clear() + buffer.add(element) + return true + } + if (resumeFirstReceiver(element)) return true + if (buffer.size < capacity) { + buffer.add(element) + return true + } + return false + } + + private fun resumeFirstReceiver(element: Int): Boolean { + while (receivers.isNotEmpty()) { + val r = receivers.removeAt(0) + if (r.resume(element)) return true + } + return false + } + + suspend fun receive(): Any = poll() ?: suspendCancellableCoroutine { cont -> + receivers.add(cont) + } + + fun poll(): Any? { + if (buffer.isNotEmpty()) { + val el = buffer.removeAt(0) + resumeFirstSender().also { + if (it !== null) buffer.add(it) + } + return el + } + resumeFirstSender()?.also { return it } + if (closedMessage !== null) return closedMessage + return null + } + + private fun resumeFirstSender(): Int? { + while (senders.isNotEmpty()) { + val (s, el) = senders.removeAt(0) + if (s.resume(Unit)) return el + } + return null + } + + suspend fun sendViaSelect(element: Int) = send(element) + suspend fun receiveViaSelect() = receive() + + fun close(token: Int): Boolean { + if (closedMessage !== null) return false + closedMessage = "Closed($token)" + for (r in receivers) r.resume(closedMessage!!) + receivers.clear() + return true + } + + fun cancel(token: Int) { + if (!close(token)) return + for ((s, _) in senders) s.resume(closedMessage!!) + senders.clear() + buffer.clear() + } + + fun isClosedForSend(): Boolean = closedMessage !== null + fun isClosedForReceive(): Boolean = isClosedForSend() && buffer.isEmpty() && senders.isEmpty() + + fun isEmpty(): Boolean { + if (closedMessage !== null) return false + return buffer.isEmpty() && senders.isEmpty() + } + + override fun extractState() = buffer to closedMessage +} + +private fun CancellableContinuation.resume(res: T): Boolean { + val token = tryResume(res) ?: return false + completeResume(token) + return true +} \ No newline at end of file diff --git a/kotlinx-coroutines-core/jvm/test/linearizability/LinTesting.kt b/kotlinx-coroutines-core/jvm/test/linearizability/LinTesting.kt deleted file mode 100644 index 14cf2a7039..0000000000 --- a/kotlinx-coroutines-core/jvm/test/linearizability/LinTesting.kt +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.coroutines - -import com.devexperts.dxlab.lincheck.Actor -import com.devexperts.dxlab.lincheck.Result -import com.devexperts.dxlab.lincheck.Utils.* -import com.devexperts.dxlab.lincheck.execution.* -import com.devexperts.dxlab.lincheck.verifier.Verifier -import java.lang.reflect.Method -import java.util.* -import kotlin.coroutines.Continuation -import kotlin.coroutines.CoroutineContext -import kotlin.coroutines.EmptyCoroutineContext -import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED -import kotlin.coroutines.intrinsics.startCoroutineUninterceptedOrReturn - -data class OpResult(val name: String, val value: Any?) { - override fun toString(): String = "$name=$value" -} - -private const val CS_STR = "COROUTINE_SUSPENDED" - -class LinTesting { - private val resumed = object : ThreadLocal>() { - override fun initialValue() = arrayListOf() - } - - private inline fun wrap(block: () -> Any?): Any? = - try { repr(block()) } - catch(e: Throwable) { repr(e) } - - private fun repr(e: Any?): Any? = - when { - e === COROUTINE_SUSPENDED -> CS_STR - e is Throwable -> e.toString() - else -> e - } - - fun run(name: String, block: suspend () -> T): List { - val list = resumed.get() - list.clear() - val result = arrayListOf(OpResult(name, wrap { - block.startCoroutineUninterceptedOrReturn(completion = object : Continuation { - override val context: CoroutineContext - get() = EmptyCoroutineContext - - override fun resumeWith(result: kotlin.Result) { - val value = if (result.isSuccess) result.getOrNull() else result.exceptionOrNull() - resumed.get() += OpResult(name, repr(value)) - } - } - ) - })) - result.addAll(list) - return result - } -} - -class LinVerifier(scenario: ExecutionScenario, - testClass: Class<*>) : Verifier { - private val possibleResultsSet: Set>> = - generateAllLinearizableExecutions(scenario.parallelExecution) - .asSequence() - .map { linEx: List -> - val res: List = executeActors(testClass.newInstance(), linEx) - val actorIds = linEx.asSequence().withIndex().associateBy({ it.value}, { it.index }) - scenario.parallelExecution.map { actors -> actors.map { actor -> res[actorIds[actor]!!] } } - }.toSet() - - override fun verifyResults(results: ExecutionResult): Boolean { - if (!valid(results.parallelResults)) { - println("\nNon-linearizable execution:") - printResults(results.parallelResults) - println("\nPossible linearizable executions:") - possibleResultsSet.forEach { possibleResults -> - printResults(possibleResults) - println() - } - throw AssertionError("Non-linearizable execution detected, see log for details") - } - - return true - } - - private fun printResults(results: List>) { - results.forEachIndexed { index, res -> - println("Thread $index: $res") - } - println("Op map: ${results.toOpMap()}") - } - - private fun valid(results: List>): Boolean = - (results in possibleResultsSet) || possibleResultsSet.any { matches(results, it) } - - private fun matches(results: List>, possible: List>): Boolean = - results.toOpMap() == possible.toOpMap() - - private fun List>.toOpMap(): Map> { - val filtered = flatMap { it }.flatMap { it.resultValue }.filter { it.value != CS_STR } - return filtered.groupBy({ it.name }, { it.value }) - } - - private fun generateAllLinearizableExecutions(actorsPerThread: List>): List> { - val executions = ArrayList>() - generateLinearizableExecutions0( - executions, actorsPerThread, ArrayList(), IntArray(actorsPerThread.size), - actorsPerThread.sumBy { it.size }) - return executions - } - - @Suppress("UNCHECKED_CAST") - private fun generateLinearizableExecutions0(executions: MutableList>, actorsPerThread: List>, - currentExecution: ArrayList, indexes: IntArray, length: Int) { - if (currentExecution.size == length) { - executions.add(currentExecution.clone() as List) - return - } - for (i in indexes.indices) { - val actors = actorsPerThread[i] - if (indexes[i] == actors.size) - continue - currentExecution.add(actors[indexes[i]]) - indexes[i]++ - generateLinearizableExecutions0(executions, actorsPerThread, currentExecution, indexes, length) - indexes[i]-- - currentExecution.removeAt(currentExecution.size - 1) - } - } -} - -private val VALUE = Result::class.java.getDeclaredField("value").apply { isAccessible = true } - -@Suppress("UNCHECKED_CAST") -private val Result.resultValue: List - get() = VALUE.get(this) as List diff --git a/kotlinx-coroutines-core/jvm/test/linearizability/LockFreeListLCStressTest.kt b/kotlinx-coroutines-core/jvm/test/linearizability/LockFreeListLCStressTest.kt index 546615489b..5f91c640a6 100644 --- a/kotlinx-coroutines-core/jvm/test/linearizability/LockFreeListLCStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/linearizability/LockFreeListLCStressTest.kt @@ -5,16 +5,16 @@ package kotlinx.coroutines.linearizability -import com.devexperts.dxlab.lincheck.* -import com.devexperts.dxlab.lincheck.annotations.* -import com.devexperts.dxlab.lincheck.paramgen.* -import com.devexperts.dxlab.lincheck.strategy.stress.* import kotlinx.coroutines.* import kotlinx.coroutines.internal.* +import org.jetbrains.kotlinx.lincheck.annotations.* +import org.jetbrains.kotlinx.lincheck.annotations.Operation +import org.jetbrains.kotlinx.lincheck.paramgen.* +import org.jetbrains.kotlinx.lincheck.verifier.* import kotlin.test.* -@Param(name = "value", gen = IntGen::class, conf = "1:3") -class LockFreeListLCStressTest : TestBase() { +@Param(name = "value", gen = IntGen::class, conf = "1:5") +class LockFreeListLCStressTest : VerifierState() { class Node(val value: Int): LockFreeLinkedListNode() private val q: LockFreeLinkedListHead = LockFreeLinkedListHead() @@ -44,29 +44,11 @@ class LockFreeListLCStressTest : TestBase() { private fun Any.isSame(value: Int) = this is Node && this.value == value @Test - fun testAddRemoveLinearizability() { - val options = StressOptions() - .iterations(100 * stressTestMultiplierSqrt) - .invocationsPerIteration(1000 * stressTestMultiplierSqrt) - .threads(3) - LinChecker.check(LockFreeListLCStressTest::class.java, options) - } - - private var _curElements: ArrayList? = null - private val curElements: ArrayList get() { - if (_curElements == null) { - _curElements = ArrayList() - q.forEach { _curElements!!.add(it.value) } - } - return _curElements!! - } - - override fun equals(other: Any?): Boolean { - other as LockFreeListLCStressTest - return curElements == other.curElements - } + fun testAddRemoveLinearizability() = LCStressOptionsDefault().check(this::class) - override fun hashCode(): Int { - return curElements.hashCode() + override fun extractState(): Any { + val elements = ArrayList() + q.forEach { elements.add(it.value) } + return elements } } \ No newline at end of file diff --git a/kotlinx-coroutines-core/jvm/test/linearizability/LockFreeTaskQueueLCStressTest.kt b/kotlinx-coroutines-core/jvm/test/linearizability/LockFreeTaskQueueLCStressTest.kt index ea2afa1019..de494cc1e6 100644 --- a/kotlinx-coroutines-core/jvm/test/linearizability/LockFreeTaskQueueLCStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/linearizability/LockFreeTaskQueueLCStressTest.kt @@ -5,22 +5,19 @@ package kotlinx.coroutines.linearizability -import com.devexperts.dxlab.lincheck.* -import com.devexperts.dxlab.lincheck.annotations.* -import com.devexperts.dxlab.lincheck.paramgen.* -import com.devexperts.dxlab.lincheck.strategy.stress.* import kotlinx.coroutines.* import kotlinx.coroutines.internal.* +import org.jetbrains.kotlinx.lincheck.annotations.* +import org.jetbrains.kotlinx.lincheck.annotations.Operation +import org.jetbrains.kotlinx.lincheck.paramgen.* +import org.jetbrains.kotlinx.lincheck.verifier.* +import org.jetbrains.kotlinx.lincheck.verifier.quiescent.* import kotlin.test.* -internal data class Snapshot(val elements: List, val isClosed: Boolean) { - constructor(q: LockFreeTaskQueue) : this(q.map { it }, q.isClosed()) -} - -@OpGroupConfig.OpGroupConfigs(OpGroupConfig(name = "consumer", nonParallel = true)) @Param(name = "value", gen = IntGen::class, conf = "1:3") -class SCLockFreeTaskQueueLCStressTest : LockFreeTaskQueueLCTestBase() { - private val q: LockFreeTaskQueue = LockFreeTaskQueue(singleConsumer = true) +internal abstract class AbstractLockFreeTaskQueueWithoutRemoveLCStressTest protected constructor(singleConsumer: Boolean) : VerifierState() { + @JvmField + protected val q = LockFreeTaskQueue(singleConsumer = singleConsumer) @Operation fun close() = q.close() @@ -28,66 +25,20 @@ class SCLockFreeTaskQueueLCStressTest : LockFreeTaskQueueLCTestBase() { @Operation fun addLast(@Param(name = "value") value: Int) = q.addLast(value) - /** - * Note that removeFirstOrNull is not linearizable w.r.t. to addLast, so here - * we test only linearizability of close. - */ -// @Operation(group = "consumer") -// fun removeFirstOrNull() = q.removeFirstOrNull() - - @Test - fun testSC() = linTest() - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as SCLockFreeTaskQueueLCStressTest - - return Snapshot(q) == Snapshot(other.q) - } - - override fun hashCode(): Int = Snapshot(q).hashCode() -} - -@Param(name = "value", gen = IntGen::class, conf = "1:3") -class MCLockFreeTaskQueueLCStressTest : LockFreeTaskQueueLCTestBase() { - private val q: LockFreeTaskQueue = LockFreeTaskQueue(singleConsumer = false) - - @Operation - fun close() = q.close() - - @Operation - fun addLast(@Param(name = "value") value: Int) = q.addLast(value) + @QuiescentConsistent + @Operation(group = "consumer") + fun removeFirstOrNull() = q.removeFirstOrNull() - /** - * Note that removeFirstOrNull is not linearizable w.r.t. to addLast, so here - * we test only linearizability of close. - */ -// @Operation(group = "consumer") -// fun removeFirstOrNull() = q.removeFirstOrNull() + override fun extractState() = q.map { it } to q.isClosed() @Test - fun testMC() = linTest() - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as MCLockFreeTaskQueueLCStressTest - - return Snapshot(q) == Snapshot(other.q) - } - - override fun hashCode(): Int = Snapshot(q).hashCode() + fun testWithRemoveForQuiescentConsistency() = LCStressOptionsDefault() + .verifier(QuiescentConsistencyVerifier::class.java) + .check(this::class) } -open class LockFreeTaskQueueLCTestBase : TestBase() { - fun linTest() { - val options = StressOptions() - .iterations(100 * stressTestMultiplierSqrt) - .invocationsPerIteration(1000 * stressTestMultiplierSqrt) - .threads(2) - LinChecker.check(this::class.java, options) - } -} \ No newline at end of file +@OpGroupConfig(name = "consumer", nonParallel = false) +internal class MCLockFreeTaskQueueWithRemoveLCStressTest : AbstractLockFreeTaskQueueWithoutRemoveLCStressTest(singleConsumer = false) + +@OpGroupConfig(name = "consumer", nonParallel = true) +internal class SCLockFreeTaskQueueWithRemoveLCStressTest : AbstractLockFreeTaskQueueWithoutRemoveLCStressTest(singleConsumer = true) \ No newline at end of file diff --git a/kotlinx-coroutines-core/jvm/test/linearizability/SegmentQueueLCStressTest.kt b/kotlinx-coroutines-core/jvm/test/linearizability/SegmentQueueLCStressTest.kt new file mode 100644 index 0000000000..1bb51a568f --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/linearizability/SegmentQueueLCStressTest.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ +@file:Suppress("unused") + +package kotlinx.coroutines.linearizability + +import kotlinx.coroutines.* +import kotlinx.coroutines.internal.SegmentBasedQueue +import org.jetbrains.kotlinx.lincheck.annotations.* +import org.jetbrains.kotlinx.lincheck.annotations.Operation +import org.jetbrains.kotlinx.lincheck.paramgen.* +import org.jetbrains.kotlinx.lincheck.verifier.* +import org.junit.* + +@Param(name = "value", gen = IntGen::class, conf = "1:5") +class SegmentQueueLCStressTest : VerifierState() { + private val q = SegmentBasedQueue() + + @Operation + fun add(@Param(name = "value") value: Int) { + q.enqueue(value) + } + + @Operation + fun poll(): Int? = q.dequeue() + + override fun extractState(): Any { + val elements = ArrayList() + while (true) { + val x = q.dequeue() ?: break + elements.add(x) + } + + return elements + } + + @Test + fun test() = LCStressOptionsDefault().check(this::class) +} \ 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 66b93be9cf..f31752c8b5 100644 --- a/kotlinx-coroutines-core/jvm/test/scheduling/BlockingCoroutineDispatcherTest.kt +++ b/kotlinx-coroutines-core/jvm/test/scheduling/BlockingCoroutineDispatcherTest.kt @@ -194,10 +194,10 @@ class BlockingCoroutineDispatcherTest : SchedulerTestBase() { fun testYield() = runBlocking { corePoolSize = 1 maxPoolSize = 1 - val ds = blockingDispatcher(1) - val outerJob = launch(ds) { + val bd = blockingDispatcher(1) + val outerJob = launch(bd) { expect(1) - val innerJob = launch(ds) { + val innerJob = launch(bd) { // Do nothing expect(3) } @@ -215,6 +215,21 @@ class BlockingCoroutineDispatcherTest : SchedulerTestBase() { finish(5) } + @Test + fun testUndispatchedYield() = runTest { + expect(1) + corePoolSize = 1 + maxPoolSize = 1 + val blockingDispatcher = blockingDispatcher(1) + val job = launch(blockingDispatcher, CoroutineStart.UNDISPATCHED) { + expect(2) + yield() + } + expect(3) + job.join() + finish(4) + } + @Test(expected = IllegalArgumentException::class) fun testNegativeParallelism() { blockingDispatcher(-1) diff --git a/kotlinx-coroutines-core/jvm/test/scheduling/BlockingCoroutineDispatcherThreadLimitStressTest.kt b/kotlinx-coroutines-core/jvm/test/scheduling/BlockingCoroutineDispatcherThreadLimitStressTest.kt index 123fe3c9c4..c1fda44487 100644 --- a/kotlinx-coroutines-core/jvm/test/scheduling/BlockingCoroutineDispatcherThreadLimitStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/scheduling/BlockingCoroutineDispatcherThreadLimitStressTest.kt @@ -21,7 +21,6 @@ class BlockingCoroutineDispatcherThreadLimitStressTest : SchedulerTestBase() { private val concurrentWorkers = AtomicInteger(0) @Test - @Ignore fun testLimitParallelismToOne() = runTest { val limitingDispatcher = blockingDispatcher(1) // Do in bursts to avoid OOM diff --git a/kotlinx-coroutines-core/jvm/test/scheduling/CoroutineDispatcherTest.kt b/kotlinx-coroutines-core/jvm/test/scheduling/CoroutineDispatcherTest.kt index 062b849c0a..3cd77da74a 100644 --- a/kotlinx-coroutines-core/jvm/test/scheduling/CoroutineDispatcherTest.kt +++ b/kotlinx-coroutines-core/jvm/test/scheduling/CoroutineDispatcherTest.kt @@ -117,6 +117,18 @@ class CoroutineDispatcherTest : SchedulerTestBase() { finish(5) } + @Test + fun testUndispatchedYield() = runTest { + expect(1) + val job = launch(dispatcher, CoroutineStart.UNDISPATCHED) { + expect(2) + yield() + } + expect(3) + job.join() + finish(4) + } + @Test fun testThreadName() = runBlocking { val initialCount = Thread.getAllStackTraces().keys.asSequence() diff --git a/kotlinx-coroutines-core/jvm/test/scheduling/CoroutineSchedulerCloseStressTest.kt b/kotlinx-coroutines-core/jvm/test/scheduling/CoroutineSchedulerCloseStressTest.kt index f91b0a9131..473b429283 100644 --- a/kotlinx-coroutines-core/jvm/test/scheduling/CoroutineSchedulerCloseStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/scheduling/CoroutineSchedulerCloseStressTest.kt @@ -10,7 +10,6 @@ import org.junit.Test import org.junit.runner.* import org.junit.runners.* import java.util.* -import java.util.concurrent.* import kotlin.test.* @RunWith(Parameterized::class) @@ -79,6 +78,10 @@ class CoroutineSchedulerCloseStressTest(private val mode: Mode) : TestBase() { } else { if (rnd.nextBoolean()) { delay(1000) + val t = Thread.currentThread() + if (!t.name.contains("DefaultDispatcher-worker")) { + val a = 2 + } } else { yield() } diff --git a/kotlinx-coroutines-core/jvm/test/scheduling/CoroutineSchedulerTest.kt b/kotlinx-coroutines-core/jvm/test/scheduling/CoroutineSchedulerTest.kt index ff831950b5..b0a5954b70 100644 --- a/kotlinx-coroutines-core/jvm/test/scheduling/CoroutineSchedulerTest.kt +++ b/kotlinx-coroutines-core/jvm/test/scheduling/CoroutineSchedulerTest.kt @@ -12,11 +12,12 @@ import kotlin.coroutines.* import kotlin.test.* class CoroutineSchedulerTest : TestBase() { + private val taskModes = listOf(TASK_NON_BLOCKING, TASK_PROBABLY_BLOCKING) @Test fun testModesExternalSubmission() { // Smoke CoroutineScheduler(1, 1).use { - for (mode in TaskMode.values()) { + for (mode in taskModes) { val latch = CountDownLatch(1) it.dispatch(Runnable { latch.countDown() @@ -30,9 +31,9 @@ class CoroutineSchedulerTest : TestBase() { @Test fun testModesInternalSubmission() { // Smoke CoroutineScheduler(2, 2).use { - val latch = CountDownLatch(TaskMode.values().size) + val latch = CountDownLatch(taskModes.size) it.dispatch(Runnable { - for (mode in TaskMode.values()) { + for (mode in taskModes) { it.dispatch(Runnable { latch.countDown() }, TaskContextImpl(mode)) @@ -82,7 +83,7 @@ class CoroutineSchedulerTest : TestBase() { it.dispatch(Runnable { expect(2) finishLatch.countDown() - }, fair = true) + }, tailDispatch = true) }) startLatch.countDown() @@ -167,7 +168,7 @@ class CoroutineSchedulerTest : TestBase() { } } - private class TaskContextImpl(override val taskMode: TaskMode) : TaskContext { + private class TaskContextImpl(override val taskMode: Int) : TaskContext { override fun afterTask() {} } } \ No newline at end of file diff --git a/kotlinx-coroutines-core/jvm/test/selects/SelectPhilosophersStressTest.kt b/kotlinx-coroutines-core/jvm/test/selects/SelectPhilosophersStressTest.kt index eaff30c16d..22179f34ad 100644 --- a/kotlinx-coroutines-core/jvm/test/selects/SelectPhilosophersStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/selects/SelectPhilosophersStressTest.kt @@ -6,8 +6,8 @@ package kotlinx.coroutines.selects import kotlinx.coroutines.* import kotlinx.coroutines.sync.* -import org.junit.* -import org.junit.Assert.* +import org.junit.Test +import kotlin.test.* class SelectPhilosophersStressTest : TestBase() { private val TEST_DURATION = 3000L * stressTestMultiplier @@ -59,7 +59,7 @@ class SelectPhilosophersStressTest : TestBase() { val eats = withTimeout(5 * TEST_DURATION) { philosophers.map { it.await() } } debugJob.cancel() eats.withIndex().forEach { (id, eats) -> - assertTrue("$id shall not starve", eats > 0) + assertTrue(eats > 0, "$id shall not starve") } } } \ No newline at end of file diff --git a/kotlinx-coroutines-core/jvm/test/test/TestCoroutineContextTest.kt b/kotlinx-coroutines-core/jvm/test/test/TestCoroutineContextTest.kt index 25b909148d..4a6f4d24df 100644 --- a/kotlinx-coroutines-core/jvm/test/test/TestCoroutineContextTest.kt +++ b/kotlinx-coroutines-core/jvm/test/test/TestCoroutineContextTest.kt @@ -6,8 +6,9 @@ package kotlinx.coroutines.test import kotlinx.coroutines.* import org.junit.* -import org.junit.Assert.* +import org.junit.Test import kotlin.coroutines.* +import kotlin.test.* class TestCoroutineContextTest { private val injectedContext = TestCoroutineContext() @@ -309,7 +310,7 @@ class TestCoroutineContextTest { advanceTimeBy(delay) val e = result.getCompletionExceptionOrNull() - assertTrue("Expected to be thrown: '$expectedError' but was '$e'", expectedError === e) + assertTrue(expectedError === e, "Expected to be thrown: '$expectedError' but was '$e'") } @Test @@ -341,7 +342,7 @@ class TestCoroutineContextTest { } catch (e: AssertionError) { throw e } catch (e: Throwable) { - assertTrue("Expected to be thrown: '$expectedError' but was '$e'", expectedError === e) + assertTrue(expectedError === e, "Expected to be thrown: '$expectedError' but was '$e'") } } diff --git a/kotlinx-coroutines-core/native/src/Builders.kt b/kotlinx-coroutines-core/native/src/Builders.kt index 0dc90d556a..975fc98db7 100644 --- a/kotlinx-coroutines-core/native/src/Builders.kt +++ b/kotlinx-coroutines-core/native/src/Builders.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/native/src/CompletionHandler.kt b/kotlinx-coroutines-core/native/src/CompletionHandler.kt index ac833acb7d..706f6c498e 100644 --- a/kotlinx-coroutines-core/native/src/CompletionHandler.kt +++ b/kotlinx-coroutines-core/native/src/CompletionHandler.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/native/src/CoroutineContext.kt b/kotlinx-coroutines-core/native/src/CoroutineContext.kt index 41d2c88df5..bcc7f48963 100644 --- a/kotlinx-coroutines-core/native/src/CoroutineContext.kt +++ b/kotlinx-coroutines-core/native/src/CoroutineContext.kt @@ -1,10 +1,11 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines import kotlin.coroutines.* +import kotlin.native.concurrent.* private fun takeEventLoop(): EventLoopImpl = ThreadLocalEventLoop.currentOrNull() as? EventLoopImpl ?: @@ -26,6 +27,7 @@ internal fun loopWasShutDown(): Nothing = error("Cannot execute task because eve internal actual fun createDefaultDispatcher(): CoroutineDispatcher = DefaultExecutor +@SharedImmutable internal actual val DefaultDelay: Delay = DefaultExecutor public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext { @@ -37,4 +39,4 @@ public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): // No debugging facilities on native internal actual inline fun withCoroutineContext(context: CoroutineContext, countOrElement: Any?, block: () -> T): T = block() internal actual fun Continuation<*>.toDebugString(): String = toString() -internal actual val CoroutineContext.coroutineName: String? get() = null // not supported on native \ No newline at end of file +internal actual val CoroutineContext.coroutineName: String? get() = null // not supported on native diff --git a/kotlinx-coroutines-core/native/src/CoroutineExceptionHandlerImpl.kt b/kotlinx-coroutines-core/native/src/CoroutineExceptionHandlerImpl.kt index 5efa9249ec..dff845b27f 100644 --- a/kotlinx-coroutines-core/native/src/CoroutineExceptionHandlerImpl.kt +++ b/kotlinx-coroutines-core/native/src/CoroutineExceptionHandlerImpl.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/native/src/Debug.kt b/kotlinx-coroutines-core/native/src/Debug.kt index 653cb06978..26787f865a 100644 --- a/kotlinx-coroutines-core/native/src/Debug.kt +++ b/kotlinx-coroutines-core/native/src/Debug.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines @@ -15,4 +15,4 @@ internal actual val Any.classSimpleName: String get() = this::class.simpleName ? @SymbolName("Kotlin_Any_hashCode") external fun Any.id(): Int // Note: can return negative value on K/N -internal actual inline fun assert(value: () -> Boolean) {} \ No newline at end of file +internal actual inline fun assert(value: () -> Boolean) {} diff --git a/kotlinx-coroutines-core/native/src/Dispatchers.kt b/kotlinx-coroutines-core/native/src/Dispatchers.kt index 8aed212973..6c650046a0 100644 --- a/kotlinx-coroutines-core/native/src/Dispatchers.kt +++ b/kotlinx-coroutines-core/native/src/Dispatchers.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/native/src/EventLoop.kt b/kotlinx-coroutines-core/native/src/EventLoop.kt index 8f2dfeb339..d6c6525504 100644 --- a/kotlinx-coroutines-core/native/src/EventLoop.kt +++ b/kotlinx-coroutines-core/native/src/EventLoop.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines @@ -19,4 +19,4 @@ internal class EventLoopImpl: EventLoopImplBase() { internal actual fun createEventLoop(): EventLoop = EventLoopImpl() -internal actual fun nanoTime(): Long = getTimeNanos() \ No newline at end of file +internal actual fun nanoTime(): Long = getTimeNanos() diff --git a/kotlinx-coroutines-core/native/src/Exceptions.kt b/kotlinx-coroutines-core/native/src/Exceptions.kt index b7657da74c..39b3344ac1 100644 --- a/kotlinx-coroutines-core/native/src/Exceptions.kt +++ b/kotlinx-coroutines-core/native/src/Exceptions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/native/src/Runnable.kt b/kotlinx-coroutines-core/native/src/Runnable.kt index b240a943d0..19710f971b 100644 --- a/kotlinx-coroutines-core/native/src/Runnable.kt +++ b/kotlinx-coroutines-core/native/src/Runnable.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/native/src/SchedulerTask.kt b/kotlinx-coroutines-core/native/src/SchedulerTask.kt index 2da03c1bf1..4154d53e07 100644 --- a/kotlinx-coroutines-core/native/src/SchedulerTask.kt +++ b/kotlinx-coroutines-core/native/src/SchedulerTask.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/native/src/flow/internal/FlowExceptions.kt b/kotlinx-coroutines-core/native/src/flow/internal/FlowExceptions.kt index ed74d58313..4705471907 100644 --- a/kotlinx-coroutines-core/native/src/flow/internal/FlowExceptions.kt +++ b/kotlinx-coroutines-core/native/src/flow/internal/FlowExceptions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.flow.internal diff --git a/kotlinx-coroutines-core/native/src/flow/internal/SafeCollector.kt b/kotlinx-coroutines-core/native/src/flow/internal/SafeCollector.kt new file mode 100644 index 0000000000..78f1bdb2a9 --- /dev/null +++ b/kotlinx-coroutines-core/native/src/flow/internal/SafeCollector.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.flow.internal + +import kotlinx.coroutines.flow.* +import kotlin.coroutines.* + +internal actual class SafeCollector actual constructor( + internal actual val collector: FlowCollector, + internal actual val collectContext: CoroutineContext +) : FlowCollector { + + // Note, it is non-capturing lambda, so no extra allocation during init of SafeCollector + internal actual val collectContextSize = collectContext.fold(0) { count, _ -> count + 1 } + private var lastEmissionContext: CoroutineContext? = null + + override suspend fun emit(value: T) { + val currentContext = coroutineContext + if (lastEmissionContext !== currentContext) { + checkContext(currentContext) + lastEmissionContext = currentContext + } + collector.emit(value) + } + + public actual fun releaseIntercepted() { + } +} diff --git a/kotlinx-coroutines-core/native/src/internal/Concurrent.kt b/kotlinx-coroutines-core/native/src/internal/Concurrent.kt index 546d6af96b..486dc8f057 100644 --- a/kotlinx-coroutines-core/native/src/internal/Concurrent.kt +++ b/kotlinx-coroutines-core/native/src/internal/Concurrent.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal @@ -16,6 +16,3 @@ internal class NoOpLock { internal actual fun subscriberList(): MutableList = CopyOnWriteList() internal actual fun identitySet(expectedSize: Int): MutableSet = HashSet() - -@Suppress("ACTUAL_WITHOUT_EXPECT") -internal actual typealias SharedImmutable = kotlin.native.concurrent.SharedImmutable diff --git a/kotlinx-coroutines-core/native/src/internal/CopyOnWriteList.kt b/kotlinx-coroutines-core/native/src/internal/CopyOnWriteList.kt index c81f6530e0..b925317b3d 100644 --- a/kotlinx-coroutines-core/native/src/internal/CopyOnWriteList.kt +++ b/kotlinx-coroutines-core/native/src/internal/CopyOnWriteList.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal diff --git a/kotlinx-coroutines-core/native/src/internal/LinkedList.kt b/kotlinx-coroutines-core/native/src/internal/LinkedList.kt index bcdd0e8377..60d0857be5 100644 --- a/kotlinx-coroutines-core/native/src/internal/LinkedList.kt +++ b/kotlinx-coroutines-core/native/src/internal/LinkedList.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal diff --git a/kotlinx-coroutines-core/native/src/internal/ProbesSupport.kt b/kotlinx-coroutines-core/native/src/internal/ProbesSupport.kt index 81b6476bc2..a13a141f4d 100644 --- a/kotlinx-coroutines-core/native/src/internal/ProbesSupport.kt +++ b/kotlinx-coroutines-core/native/src/internal/ProbesSupport.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal diff --git a/kotlinx-coroutines-core/native/src/internal/StackTraceRecovery.kt b/kotlinx-coroutines-core/native/src/internal/StackTraceRecovery.kt index c27280072c..49f043da36 100644 --- a/kotlinx-coroutines-core/native/src/internal/StackTraceRecovery.kt +++ b/kotlinx-coroutines-core/native/src/internal/StackTraceRecovery.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal @@ -21,4 +21,4 @@ internal actual interface CoroutineStackFrame { internal actual typealias StackTraceElement = Any internal actual fun Throwable.initCause(cause: Throwable) { -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/native/src/internal/Synchronized.kt b/kotlinx-coroutines-core/native/src/internal/Synchronized.kt index fbed54622c..0911dbe115 100644 --- a/kotlinx-coroutines-core/native/src/internal/Synchronized.kt +++ b/kotlinx-coroutines-core/native/src/internal/Synchronized.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal @@ -17,4 +17,4 @@ public actual typealias SynchronizedObject = Any */ @InternalCoroutinesApi public actual inline fun synchronized(lock: SynchronizedObject, block: () -> T): T = - block() \ No newline at end of file + block() diff --git a/kotlinx-coroutines-core/native/src/internal/SystemProps.kt b/kotlinx-coroutines-core/native/src/internal/SystemProps.kt index 6779d4e5d8..564630f623 100644 --- a/kotlinx-coroutines-core/native/src/internal/SystemProps.kt +++ b/kotlinx-coroutines-core/native/src/internal/SystemProps.kt @@ -1,7 +1,7 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal -internal actual fun systemProp(propertyName: String): String? = null \ No newline at end of file +internal actual fun systemProp(propertyName: String): String? = null diff --git a/kotlinx-coroutines-core/native/src/internal/ThreadContext.kt b/kotlinx-coroutines-core/native/src/internal/ThreadContext.kt index d9daf256e3..4a9513ab9e 100644 --- a/kotlinx-coroutines-core/native/src/internal/ThreadContext.kt +++ b/kotlinx-coroutines-core/native/src/internal/ThreadContext.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal diff --git a/kotlinx-coroutines-core/native/src/internal/ThreadLocal.kt b/kotlinx-coroutines-core/native/src/internal/ThreadLocal.kt index c214a63bb6..db62371c1f 100644 --- a/kotlinx-coroutines-core/native/src/internal/ThreadLocal.kt +++ b/kotlinx-coroutines-core/native/src/internal/ThreadLocal.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.internal @@ -12,4 +12,4 @@ internal actual class CommonThreadLocal actual constructor() { @Suppress("UNCHECKED_CAST") actual fun get(): T = value as T actual fun set(value: T) { this.value = value } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-debug/README.md b/kotlinx-coroutines-debug/README.md index 21cc76e74f..2704859815 100644 --- a/kotlinx-coroutines-debug/README.md +++ b/kotlinx-coroutines-debug/README.md @@ -18,7 +18,7 @@ of coroutines hierarchy referenced by a [Job] or [CoroutineScope] instances usin Add `kotlinx-coroutines-debug` to your project test dependencies: ``` dependencies { - testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-debug:1.3.3' + testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-debug:1.3.4' } ``` @@ -56,7 +56,7 @@ stacktraces will be dumped to the console. ### Using as JVM agent It is possible to use this module 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.3.3.jar`. +You can run your application with an additional argument: `-javaagent:kotlinx-coroutines-debug-1.3.4.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. diff --git a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-debug.txt b/kotlinx-coroutines-debug/api/kotlinx-coroutines-debug.api similarity index 100% rename from binary-compatibility-validator/reference-public-api/kotlinx-coroutines-debug.txt rename to kotlinx-coroutines-debug/api/kotlinx-coroutines-debug.api diff --git a/kotlinx-coroutines-debug/build.gradle b/kotlinx-coroutines-debug/build.gradle index 420a88bd38..7fc2e22369 100644 --- a/kotlinx-coroutines-debug/build.gradle +++ b/kotlinx-coroutines-debug/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ apply plugin: "com.github.johnrengelman.shadow" diff --git a/kotlinx-coroutines-debug/src/AgentPremain.kt b/kotlinx-coroutines-debug/src/AgentPremain.kt index 1ff996e5aa..94ff0098b3 100644 --- a/kotlinx-coroutines-debug/src/AgentPremain.kt +++ b/kotlinx-coroutines-debug/src/AgentPremain.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.debug diff --git a/kotlinx-coroutines-debug/src/CoroutineInfo.kt b/kotlinx-coroutines-debug/src/CoroutineInfo.kt index 84cd9f370e..97bc2be595 100644 --- a/kotlinx-coroutines-debug/src/CoroutineInfo.kt +++ b/kotlinx-coroutines-debug/src/CoroutineInfo.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("PropertyName") diff --git a/kotlinx-coroutines-debug/src/DebugProbes.kt b/kotlinx-coroutines-debug/src/DebugProbes.kt index 7508954298..eb6a59b6c1 100644 --- a/kotlinx-coroutines-debug/src/DebugProbes.kt +++ b/kotlinx-coroutines-debug/src/DebugProbes.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("unused") diff --git a/kotlinx-coroutines-debug/src/internal/DebugProbesImpl.kt b/kotlinx-coroutines-debug/src/internal/DebugProbesImpl.kt index 72416ab1f2..a7ceb1468d 100644 --- a/kotlinx-coroutines-debug/src/internal/DebugProbesImpl.kt +++ b/kotlinx-coroutines-debug/src/internal/DebugProbesImpl.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.debug.internal diff --git a/kotlinx-coroutines-debug/src/internal/NoOpProbes.kt b/kotlinx-coroutines-debug/src/internal/NoOpProbes.kt index d32eeb674b..4854f5d7c6 100644 --- a/kotlinx-coroutines-debug/src/internal/NoOpProbes.kt +++ b/kotlinx-coroutines-debug/src/internal/NoOpProbes.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("unused", "UNUSED_PARAMETER") diff --git a/kotlinx-coroutines-debug/src/junit4/CoroutinesTimeout.kt b/kotlinx-coroutines-debug/src/junit4/CoroutinesTimeout.kt index 437ec6967e..16b5356721 100644 --- a/kotlinx-coroutines-debug/src/junit4/CoroutinesTimeout.kt +++ b/kotlinx-coroutines-debug/src/junit4/CoroutinesTimeout.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.debug.junit4 diff --git a/kotlinx-coroutines-debug/src/junit4/CoroutinesTimeoutStatement.kt b/kotlinx-coroutines-debug/src/junit4/CoroutinesTimeoutStatement.kt index 1fa4ab938a..72413b91f2 100644 --- a/kotlinx-coroutines-debug/src/junit4/CoroutinesTimeoutStatement.kt +++ b/kotlinx-coroutines-debug/src/junit4/CoroutinesTimeoutStatement.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.debug.junit4 diff --git a/kotlinx-coroutines-debug/test/CoroutinesDumpTest.kt b/kotlinx-coroutines-debug/test/CoroutinesDumpTest.kt index fa7353410b..91bd4f287d 100644 --- a/kotlinx-coroutines-debug/test/CoroutinesDumpTest.kt +++ b/kotlinx-coroutines-debug/test/CoroutinesDumpTest.kt @@ -11,41 +11,47 @@ import kotlin.test.* class CoroutinesDumpTest : DebugTestBase() { private val monitor = Any() - private var coroutineStarted = false // guarded by monitor + private var coroutineThread: Thread? = null // guarded by monitor @Test - fun testSuspendedCoroutine() = synchronized(monitor) { - val deferred = GlobalScope.async { + fun testSuspendedCoroutine() = runBlocking { + val deferred = async(Dispatchers.Default) { sleepingOuterMethod() } - awaitCoroutineStarted() - Thread.sleep(100) // Let delay be invoked + awaitCoroutine() + val found = DebugProbes.dumpCoroutinesInfo().single { it.job === deferred } verifyDump( "Coroutine \"coroutine#1\":DeferredCoroutine{Active}@1e4a7dd4, state: SUSPENDED\n" + - "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.sleepingNestedMethod(CoroutinesDumpTest.kt:95)\n" + - "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.sleepingOuterMethod(CoroutinesDumpTest.kt:88)\n" + - "\t(Coroutine creation stacktrace)\n" + - "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)\n" + - "\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:23)\n" + - "\tat kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:99)\n") - - val found = DebugProbes.dumpCoroutinesInfo().single { it.job === deferred } + "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.sleepingNestedMethod(CoroutinesDumpTest.kt:95)\n" + + "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.sleepingOuterMethod(CoroutinesDumpTest.kt:88)\n" + + "\t(Coroutine creation stacktrace)\n" + + "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)\n" + + "\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:23)\n" + + "\tat kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:99)\n", + ignoredCoroutine = "BlockingCoroutine" + ) { + deferred.cancel() + coroutineThread!!.interrupt() + } assertSame(deferred, found.job) - runBlocking { deferred.cancelAndJoin() } } @Test - fun testRunningCoroutine() = synchronized(monitor) { - val deferred = GlobalScope.async { + fun testRunningCoroutine() = runBlocking { + val deferred = async(Dispatchers.Default) { activeMethod(shouldSuspend = false) assertTrue(true) } - awaitCoroutineStarted() + awaitCoroutine() verifyDump( - "Coroutine \"coroutine#1\":DeferredCoroutine{Active}@227d9994, state: RUNNING (Last suspension stacktrace, not an actual stacktrace)\n" + - "\t(Coroutine creation stacktrace)\n" + + "Coroutine \"coroutine#1\":DeferredCoroutine{Active}@227d9994, state: RUNNING\n" + + "\tat java.lang.Thread.sleep(Native Method)\n" + + "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.nestedActiveMethod(CoroutinesDumpTest.kt:141)\n" + + "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.activeMethod(CoroutinesDumpTest.kt:133)\n" + + "\tat kotlinx.coroutines.debug.CoroutinesDumpTest\$testRunningCoroutine\$1$deferred\$1.invokeSuspend(CoroutinesDumpTest.kt:41)\n" + + "\t(Coroutine creation stacktrace)\n" + "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)\n" + "\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:23)\n" + "\tat kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:99)\n" + @@ -54,74 +60,84 @@ class CoroutinesDumpTest : DebugTestBase() { "\tat kotlinx.coroutines.BuildersKt.async(Unknown Source)\n" + "\tat kotlinx.coroutines.BuildersKt__Builders_commonKt.async\$default(Builders.common.kt)\n" + "\tat kotlinx.coroutines.BuildersKt.async\$default(Unknown Source)\n" + - "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.testRunningCoroutine(CoroutinesDumpTest.kt:49)") - runBlocking { deferred.cancelAndJoin() } + "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.testRunningCoroutine(CoroutinesDumpTest.kt:49)", + ignoredCoroutine = "BlockingCoroutine" + ) { + deferred.cancel() + coroutineThread?.interrupt() + } } @Test - fun testRunningCoroutineWithSuspensionPoint() = synchronized(monitor) { - val deferred = GlobalScope.async { + fun testRunningCoroutineWithSuspensionPoint() = runBlocking { + val deferred = async(Dispatchers.Default) { activeMethod(shouldSuspend = true) yield() // tail-call } - awaitCoroutineStarted() - Thread.sleep(10) + awaitCoroutine() verifyDump( "Coroutine \"coroutine#1\":DeferredCoroutine{Active}@1e4a7dd4, state: RUNNING\n" + - "\tat java.lang.Thread.sleep(Native Method)\n" + - "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.nestedActiveMethod(CoroutinesDumpTest.kt:111)\n" + - "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.activeMethod(CoroutinesDumpTest.kt:106)\n" + - "\t(Coroutine creation stacktrace)\n" + - "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)\n" + - "\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:23)\n" + - "\tat kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:99)\n" + - "\tat kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:148)\n" + - "\tat kotlinx.coroutines.BuildersKt__Builders_commonKt.async(Builders.common.kt)\n" + - "\tat kotlinx.coroutines.BuildersKt.async(Unknown Source)\n" + - "\tat kotlinx.coroutines.BuildersKt__Builders_commonKt.async\$default(Builders.common.kt)\n" + - "\tat kotlinx.coroutines.BuildersKt.async\$default(Unknown Source)\n" + - "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.testRunningCoroutineWithSuspensionPoint(CoroutinesDumpTest.kt:71)" - ) - runBlocking { deferred.cancelAndJoin() } + "\tat java.lang.Thread.sleep(Native Method)\n" + + "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.nestedActiveMethod(CoroutinesDumpTest.kt:111)\n" + + "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.activeMethod(CoroutinesDumpTest.kt:106)\n" + + "\t(Coroutine creation stacktrace)\n" + + "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)\n" + + "\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:23)\n" + + "\tat kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:99)\n" + + "\tat kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:148)\n" + + "\tat kotlinx.coroutines.BuildersKt__Builders_commonKt.async(Builders.common.kt)\n" + + "\tat kotlinx.coroutines.BuildersKt.async(Unknown Source)\n" + + "\tat kotlinx.coroutines.BuildersKt__Builders_commonKt.async\$default(Builders.common.kt)\n" + + "\tat kotlinx.coroutines.BuildersKt.async\$default(Unknown Source)\n" + + "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.testRunningCoroutineWithSuspensionPoint(CoroutinesDumpTest.kt:71)", + ignoredCoroutine = "BlockingCoroutine" + ) { + deferred.cancel() + coroutineThread!!.interrupt() + } } @Test - fun testCreationStackTrace() = synchronized(monitor) { - val deferred = GlobalScope.async { + fun testCreationStackTrace() = runBlocking { + val deferred = async(Dispatchers.Default) { activeMethod(shouldSuspend = true) } - awaitCoroutineStarted() - val coroutine = DebugProbes.dumpCoroutinesInfo().first() + awaitCoroutine() + val coroutine = DebugProbes.dumpCoroutinesInfo().first { it.job is Deferred<*> } val result = coroutine.creationStackTrace.fold(StringBuilder()) { acc, element -> acc.append(element.toString()) acc.append('\n') }.toString().trimStackTrace() - runBlocking { deferred.cancelAndJoin() } - - val expected = ("kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)\n" + - "kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:23)\n" + - "kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:109)\n" + - "kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:160)\n" + - "kotlinx.coroutines.BuildersKt__Builders_commonKt.async(Builders.common.kt:88)\n" + - "kotlinx.coroutines.BuildersKt.async(Unknown Source)\n" + - "kotlinx.coroutines.BuildersKt__Builders_commonKt.async\$default(Builders.common.kt:81)\n" + - "kotlinx.coroutines.BuildersKt.async\$default(Unknown Source)\n" + - "kotlinx.coroutines.debug.CoroutinesDumpTest.testCreationStackTrace(CoroutinesDumpTest.kt:109)").trimStackTrace() + deferred.cancel() + coroutineThread!!.interrupt() + + val expected = + ("kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)\n" + + "kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:23)\n" + + "kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:109)\n" + + "kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:160)\n" + + "kotlinx.coroutines.BuildersKt__Builders_commonKt.async(Builders.common.kt:88)\n" + + "kotlinx.coroutines.BuildersKt.async(Unknown Source)\n" + + "kotlinx.coroutines.BuildersKt__Builders_commonKt.async\$default(Builders.common.kt:81)\n" + + "kotlinx.coroutines.BuildersKt.async\$default(Unknown Source)\n" + + "kotlinx.coroutines.debug.CoroutinesDumpTest\$testCreationStackTrace\$1.invokeSuspend(CoroutinesDumpTest.kt)").trimStackTrace() assertTrue(result.startsWith(expected)) } @Test - fun testFinishedCoroutineRemoved() = synchronized(monitor) { - val deferred = GlobalScope.async { + fun testFinishedCoroutineRemoved() = runBlocking { + val deferred = async(Dispatchers.Default) { activeMethod(shouldSuspend = true) } - awaitCoroutineStarted() - runBlocking { deferred.cancelAndJoin() } - verifyDump() + awaitCoroutine() + deferred.cancel() + coroutineThread!!.interrupt() + deferred.join() + verifyDump(ignoredCoroutine = "BlockingCoroutine") } private suspend fun activeMethod(shouldSuspend: Boolean) { @@ -133,28 +149,31 @@ class CoroutinesDumpTest : DebugTestBase() { if (shouldSuspend) yield() notifyCoroutineStarted() while (coroutineContext[Job]!!.isActive) { - Thread.sleep(100) + runCatching { Thread.sleep(60_000) } } } private suspend fun sleepingOuterMethod() { sleepingNestedMethod() - yield() + yield() // TCE } private suspend fun sleepingNestedMethod() { - yield() + yield() // Suspension point notifyCoroutineStarted() delay(Long.MAX_VALUE) } - private fun awaitCoroutineStarted() { - while (!coroutineStarted) (monitor as Object).wait() + private fun awaitCoroutine() = synchronized(monitor) { + while (coroutineThread == null) (monitor as Object).wait() + while (coroutineThread!!.state != Thread.State.TIMED_WAITING) { + // Wait until thread sleeps to have a consistent stacktrace + } } private fun notifyCoroutineStarted() { synchronized(monitor) { - coroutineStarted = true + coroutineThread = Thread.currentThread() (monitor as Object).notifyAll() } } diff --git a/kotlinx-coroutines-debug/test/RunningThreadStackMergeTest.kt b/kotlinx-coroutines-debug/test/RunningThreadStackMergeTest.kt index 193bcce135..c0b7f50134 100644 --- a/kotlinx-coroutines-debug/test/RunningThreadStackMergeTest.kt +++ b/kotlinx-coroutines-debug/test/RunningThreadStackMergeTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.debug @@ -20,7 +20,7 @@ class RunningThreadStackMergeTest : DebugTestBase() { verifyDump( "Coroutine \"coroutine#1\":BlockingCoroutine{Active}@62230679", // <- this one is ignored "Coroutine \"coroutine#2\":StandaloneCoroutine{Active}@50284dc4, state: RUNNING\n" + - "\tat sun.misc.Unsafe.park(Native Method)\n" + + "\tat jdk.internal.misc.Unsafe.park(Native Method)\n" + "\tat java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)\n" + "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer\$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)\n" + "\tat java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:234)\n" + @@ -33,8 +33,9 @@ class RunningThreadStackMergeTest : DebugTestBase() { "\t(Coroutine creation stacktrace)\n" + "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)", ignoredCoroutine = ":BlockingCoroutine" - ) - coroutineBlocker.await() + ) { + coroutineBlocker.await() + } } private fun awaitCoroutineStarted() { @@ -74,7 +75,7 @@ class RunningThreadStackMergeTest : DebugTestBase() { verifyDump( "Coroutine \"coroutine#1\":BlockingCoroutine{Active}@62230679", // <- this one is ignored "Coroutine \"coroutine#2\":StandaloneCoroutine{Active}@3aea3c67, state: RUNNING\n" + - "\tat sun.misc.Unsafe.park(Native Method)\n" + + "\tat jdk.internal.misc.Unsafe.park(Native Method)\n" + "\tat java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)\n" + "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer\$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)\n" + "\tat java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:234)\n" + @@ -87,8 +88,9 @@ class RunningThreadStackMergeTest : DebugTestBase() { "\t(Coroutine creation stacktrace)\n" + "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)", ignoredCoroutine = ":BlockingCoroutine" - ) - coroutineBlocker.await() + ) { + coroutineBlocker.await() + } } private fun CoroutineScope.launchEscapingCoroutine() { @@ -114,7 +116,7 @@ class RunningThreadStackMergeTest : DebugTestBase() { verifyDump( "Coroutine \"coroutine#1\":BlockingCoroutine{Active}@62230679", // <- this one is ignored "Coroutine \"coroutine#2\":StandaloneCoroutine{Active}@3aea3c67, state: RUNNING\n" + - "\tat sun.misc.Unsafe.park(Native Method)\n" + + "\tat jdk.internal.misc.Unsafe.park(Native Method)\n" + "\tat java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)\n" + "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer\$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)\n" + "\tat java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:234)\n" + @@ -125,8 +127,9 @@ class RunningThreadStackMergeTest : DebugTestBase() { "\t(Coroutine creation stacktrace)\n" + "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)", ignoredCoroutine = ":BlockingCoroutine" - ) - coroutineBlocker.await() + ) { + coroutineBlocker.await() + } } private fun CoroutineScope.launchEscapingCoroutineWithoutContext() { diff --git a/kotlinx-coroutines-debug/test/StracktraceUtils.kt b/kotlinx-coroutines-debug/test/StracktraceUtils.kt index cab4ed86b3..de31ac10a4 100644 --- a/kotlinx-coroutines-debug/test/StracktraceUtils.kt +++ b/kotlinx-coroutines-debug/test/StracktraceUtils.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.debug @@ -11,6 +11,9 @@ public fun String.trimStackTrace(): String = trimIndent() .replace(Regex(":[0-9]+"), "") .replace(Regex("#[0-9]+"), "") + .replace(Regex("(?<=\tat )[^\n]*/"), "") + .replace(Regex("\t"), "") + .replace("sun.misc.Unsafe.park", "jdk.internal.misc.Unsafe.park") // JDK8->JDK11 .applyBackspace() public fun String.applyBackspace(): String { @@ -30,16 +33,12 @@ public fun String.applyBackspace(): String { public fun verifyStackTrace(e: Throwable, traces: List) { val stacktrace = toStackTrace(e) + val trimmedStackTrace = stacktrace.trimStackTrace() traces.forEach { - val expectedLines = it.trimStackTrace().split("\n") - for (i in 0 until expectedLines.size) { - traces.forEach { - assertTrue( - stacktrace.trimStackTrace().contains(it.trimStackTrace()), - "\nExpected trace element:\n$it\n\nActual stacktrace:\n$stacktrace" - ) - } - } + assertTrue( + trimmedStackTrace.contains(it.trimStackTrace()), + "\nExpected trace element:\n$it\n\nActual stacktrace:\n$stacktrace" + ) } val causes = stacktrace.count("Caused by") @@ -55,13 +54,22 @@ public fun toStackTrace(t: Throwable): String { public fun String.count(substring: String): Int = split(substring).size - 1 +public fun verifyDump(vararg traces: String, ignoredCoroutine: String? = null, finally: () -> Unit) { + try { + verifyDump(*traces, ignoredCoroutine = ignoredCoroutine) + } finally { + finally() + } +} + public fun verifyDump(vararg traces: String, ignoredCoroutine: String? = null) { val baos = ByteArrayOutputStream() DebugProbes.dumpCoroutines(PrintStream(baos)) val trace = baos.toString().split("\n\n") if (traces.isEmpty()) { - assertEquals(1, trace.size) - assertTrue(trace[0].startsWith("Coroutines dump")) + val filtered = trace.filter { ignoredCoroutine == null || !it.contains(ignoredCoroutine) } + assertEquals(1, filtered.count()) + assertTrue(filtered[0].startsWith("Coroutines dump")) return } // Drop "Coroutine dump" line diff --git a/kotlinx-coroutines-test/README.md b/kotlinx-coroutines-test/README.md index aefd5a3f07..e348387835 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.3.3' + testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.4' } ``` diff --git a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-test.txt b/kotlinx-coroutines-test/api/kotlinx-coroutines-test.api similarity index 100% rename from binary-compatibility-validator/reference-public-api/kotlinx-coroutines-test.txt rename to kotlinx-coroutines-test/api/kotlinx-coroutines-test.api diff --git a/kotlinx-coroutines-test/src/DelayController.kt b/kotlinx-coroutines-test/src/DelayController.kt index 54e9c8ae5e..a777d4b9a3 100644 --- a/kotlinx-coroutines-test/src/DelayController.kt +++ b/kotlinx-coroutines-test/src/DelayController.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package kotlinx.coroutines.test import kotlinx.coroutines.CoroutineDispatcher diff --git a/kotlinx-coroutines-test/src/TestBuilders.kt b/kotlinx-coroutines-test/src/TestBuilders.kt index 7ef77bd643..3b8efb9fe1 100644 --- a/kotlinx-coroutines-test/src/TestBuilders.kt +++ b/kotlinx-coroutines-test/src/TestBuilders.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.test diff --git a/kotlinx-coroutines-test/src/TestCoroutineDispatcher.kt b/kotlinx-coroutines-test/src/TestCoroutineDispatcher.kt index 386fc8380d..aab869c98b 100644 --- a/kotlinx-coroutines-test/src/TestCoroutineDispatcher.kt +++ b/kotlinx-coroutines-test/src/TestCoroutineDispatcher.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.test @@ -212,4 +212,4 @@ private class TimedRunnable( } override fun toString() = "TimedRunnable(time=$time, run=$runnable)" -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-test/src/TestCoroutineExceptionHandler.kt b/kotlinx-coroutines-test/src/TestCoroutineExceptionHandler.kt index 41d14d0841..f585aa03ab 100644 --- a/kotlinx-coroutines-test/src/TestCoroutineExceptionHandler.kt +++ b/kotlinx-coroutines-test/src/TestCoroutineExceptionHandler.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.test diff --git a/kotlinx-coroutines-test/src/TestCoroutineScope.kt b/kotlinx-coroutines-test/src/TestCoroutineScope.kt index d39e6adf53..4034fcaa74 100644 --- a/kotlinx-coroutines-test/src/TestCoroutineScope.kt +++ b/kotlinx-coroutines-test/src/TestCoroutineScope.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.test @@ -69,4 +69,4 @@ private inline val CoroutineContext.delayController: DelayController "TestCoroutineScope requires a DelayController such as TestCoroutineDispatcher as " + "the ContinuationInterceptor (Dispatcher)" ) - } \ No newline at end of file + } diff --git a/kotlinx-coroutines-test/src/TestDispatchers.kt b/kotlinx-coroutines-test/src/TestDispatchers.kt index 532db9381f..a247ca8365 100644 --- a/kotlinx-coroutines-test/src/TestDispatchers.kt +++ b/kotlinx-coroutines-test/src/TestDispatchers.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("unused") @file:JvmName("TestDispatchers") diff --git a/kotlinx-coroutines-test/src/internal/MainTestDispatcher.kt b/kotlinx-coroutines-test/src/internal/MainTestDispatcher.kt index bb52999d08..c18e4108bb 100644 --- a/kotlinx-coroutines-test/src/internal/MainTestDispatcher.kt +++ b/kotlinx-coroutines-test/src/internal/MainTestDispatcher.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.test.internal diff --git a/kotlinx-coroutines-test/test/TestBuildersTest.kt b/kotlinx-coroutines-test/test/TestBuildersTest.kt index 1f163eeff1..27c8f5fb19 100644 --- a/kotlinx-coroutines-test/test/TestBuildersTest.kt +++ b/kotlinx-coroutines-test/test/TestBuildersTest.kt @@ -5,9 +5,9 @@ package kotlinx.coroutines.test import kotlinx.coroutines.* -import org.junit.* -import org.junit.Assert.* +import org.junit.Test import kotlin.coroutines.* +import kotlin.test.* class TestBuildersTest { diff --git a/publication-validator/README.md b/publication-validator/README.md index 7437513afa..a60ff00e3c 100644 --- a/publication-validator/README.md +++ b/publication-validator/README.md @@ -1,9 +1,13 @@ # Publication validator -This is a supplementary subproject of kotlinx.coroutines to test its publication correctness. +This is a supplementary subproject of kotlinx.coroutines that provides a new +task, `testPublishing`, to test its publication correctness. -It is used as part of "Dependency validation" build chain on TeamCity: -* kotlinx.corotoutines are built with `publishToMavenLocal` -* kotlinx.coroutines are built with `npmPublish -PdryRun=true` to have a packed publication +The tests are the following: * `NpmPublicationValidator` tests that version of NPM artifact is correct and that it has neither source nor package dependencies on atomicfu * `MavenPublicationValidator` depends on the published artifacts and tests artifacts binary content and absence of atomicfu in the classpath + +To test publication, one needs to run gradle with `-PdryRun=true`, and the +task that actually does the testing is `publication-validator:test`. +`-PdryRun` affects `npmPublish` so that it only provides a packed publication +and does not in fact attempt to send the build for publication. diff --git a/publication-validator/build.gradle b/publication-validator/build.gradle index cc19ae4027..a22ccf46d2 100644 --- a/publication-validator/build.gradle +++ b/publication-validator/build.gradle @@ -1,17 +1,8 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -plugins { - id 'org.jetbrains.kotlin.jvm' version '1.3.30' -} - -def deployVersion = properties['DeployVersion'] -ext.coroutines_version = deployVersion -println "Checking coroutines version $coroutines_version" - -group 'org.jetbrains.kotlinx' -version '1.0-SNAPSHOT' +apply from: rootProject.file("gradle/compile-jvm.gradle") repositories { mavenLocal() @@ -19,17 +10,28 @@ repositories { } dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + testCompile "org.jetbrains.kotlin:kotlin-stdlib-jdk8" testCompile 'junit:junit:4.12' testCompile 'org.apache.commons:commons-compress:1.18' testCompile 'com.google.code.gson:gson:2.8.5' - testCompile "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" - testCompile "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" + testCompile project(':kotlinx-coroutines-core') + testCompile project(':kotlinx-coroutines-android') } -compileKotlin { - kotlinOptions.jvmTarget = "1.8" -} compileTestKotlin { kotlinOptions.jvmTarget = "1.8" } + +def dryRunNpm = properties['dryRun'] + +test { + onlyIf { dryRunNpm == "true" } // so that we don't accidentally publish anything, especially before the test + doFirst { println "Verifying publishing version $version" } // all modules share the same version + environment "projectRoot", project.rootDir + environment "deployVersion", version + if (dryRunNpm == "true") { // `onlyIf` only affects execution of the task, not the dependency subtree + dependsOn(project(':').getTasksByName("publishNpm", true) + + project(':').getTasksByName("publishToMavenLocal", true)) + dependsOn.remove(project(':').getTasksByName("dokka", true)) + } +} diff --git a/publication-validator/gradle.properties b/publication-validator/gradle.properties deleted file mode 100644 index 29e08e8ca8..0000000000 --- a/publication-validator/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -kotlin.code.style=official \ No newline at end of file diff --git a/publication-validator/gradle/wrapper/gradle-wrapper.jar b/publication-validator/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 94336fcae9..0000000000 Binary files a/publication-validator/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/publication-validator/gradle/wrapper/gradle-wrapper.properties b/publication-validator/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index ecc69863ed..0000000000 --- a/publication-validator/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Wed May 08 12:45:59 MSK 2019 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-all.zip diff --git a/publication-validator/gradlew b/publication-validator/gradlew deleted file mode 100755 index cccdd3d517..0000000000 --- a/publication-validator/gradlew +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/env sh - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - -exec "$JAVACMD" "$@" diff --git a/publication-validator/gradlew.bat b/publication-validator/gradlew.bat deleted file mode 100644 index f9553162f1..0000000000 --- a/publication-validator/gradlew.bat +++ /dev/null @@ -1,84 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/publication-validator/settings.gradle b/publication-validator/settings.gradle deleted file mode 100644 index 552030630d..0000000000 --- a/publication-validator/settings.gradle +++ /dev/null @@ -1,8 +0,0 @@ -pluginManagement { - repositories { - mavenLocal() - mavenCentral() - maven { url 'https://plugins.gradle.org/m2/' } - } -} -rootProject.name = 'kotlinx-coroutines-validator' diff --git a/publication-validator/src/test/kotlin/kotlinx/coroutines/tools/MavenPublicationValidator.kt b/publication-validator/src/test/kotlin/kotlinx/coroutines/tools/MavenPublicationValidator.kt index b7f97f033e..53fd65d31f 100644 --- a/publication-validator/src/test/kotlin/kotlinx/coroutines/tools/MavenPublicationValidator.kt +++ b/publication-validator/src/test/kotlin/kotlinx/coroutines/tools/MavenPublicationValidator.kt @@ -5,7 +5,7 @@ package kotlinx.coroutines.validator import org.junit.* -import org.junit.Assert.* +import org.junit.Assert.assertTrue import java.io.* import java.util.jar.* diff --git a/publication-validator/src/test/kotlin/kotlinx/coroutines/tools/NpmPublicationValidator.kt b/publication-validator/src/test/kotlin/kotlinx/coroutines/tools/NpmPublicationValidator.kt index 2c2351f754..52479d56ef 100644 --- a/publication-validator/src/test/kotlin/kotlinx/coroutines/tools/NpmPublicationValidator.kt +++ b/publication-validator/src/test/kotlin/kotlinx/coroutines/tools/NpmPublicationValidator.kt @@ -7,13 +7,13 @@ package kotlinx.coroutines.validator import com.google.gson.* import org.apache.commons.compress.archivers.tar.* import org.junit.* -import org.junit.Assert.* import java.io.* import java.util.zip.* +import org.junit.Assert.* class NpmPublicationValidator { - private val VERSION = System.getenv("DeployVersion") - private val BUILD_DIR = System.getenv("teamcity.build.checkoutDir") + private val VERSION = System.getenv("deployVersion")!! + private val BUILD_DIR = System.getenv("projectRoot")!! private val NPM_ARTIFACT = "$BUILD_DIR/kotlinx-coroutines-core/build/npm/kotlinx-coroutines-core-$VERSION.tgz" @Test diff --git a/reactive/knit.properties b/reactive/knit.properties new file mode 100644 index 0000000000..18aecba666 --- /dev/null +++ b/reactive/knit.properties @@ -0,0 +1,6 @@ +# +# Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. +# + +knit.package=kotlinx.coroutines.rx2.guide +knit.dir=kotlinx-coroutines-rx2/test/guide/ \ No newline at end of file diff --git a/reactive/knit.test.include b/reactive/knit.test.include new file mode 100644 index 0000000000..dd9e61d041 --- /dev/null +++ b/reactive/knit.test.include @@ -0,0 +1,11 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from ${file.name} by Knit tool. Do not edit. +package ${test.package} + +import kotlinx.coroutines.guide.test.* +import org.junit.Test + +class ${test.name} : ReactiveTestBase() { \ No newline at end of file diff --git a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-reactive.txt b/reactive/kotlinx-coroutines-reactive/api/kotlinx-coroutines-reactive.api similarity index 100% rename from binary-compatibility-validator/reference-public-api/kotlinx-coroutines-reactive.txt rename to reactive/kotlinx-coroutines-reactive/api/kotlinx-coroutines-reactive.api diff --git a/reactive/kotlinx-coroutines-reactive/build.gradle b/reactive/kotlinx-coroutines-reactive/build.gradle index f544ab448b..ad97c63f45 100644 --- a/reactive/kotlinx-coroutines-reactive/build.gradle +++ b/reactive/kotlinx-coroutines-reactive/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ dependencies { @@ -31,4 +31,4 @@ tasks.withType(dokka.getClass()) { url = new URL("https://www.reactive-streams.org/reactive-streams-$reactive_streams_version-javadoc/") packageListUrl = projectDir.toPath().resolve("package.list").toUri().toURL() } -} \ No newline at end of file +} diff --git a/reactive/kotlinx-coroutines-reactive/src/Await.kt b/reactive/kotlinx-coroutines-reactive/src/Await.kt index 072773a4fe..317baab5f5 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Await.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Await.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.reactive diff --git a/reactive/kotlinx-coroutines-reactive/src/Channel.kt b/reactive/kotlinx-coroutines-reactive/src/Channel.kt index f27665b702..8c8187c130 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Channel.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Channel.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.reactive diff --git a/reactive/kotlinx-coroutines-reactive/src/ContextInjector.kt b/reactive/kotlinx-coroutines-reactive/src/ContextInjector.kt index 45f6553093..d5390fd6f2 100644 --- a/reactive/kotlinx-coroutines-reactive/src/ContextInjector.kt +++ b/reactive/kotlinx-coroutines-reactive/src/ContextInjector.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package kotlinx.coroutines.reactive import kotlinx.coroutines.InternalCoroutinesApi @@ -12,4 +16,4 @@ public interface ContextInjector { * This API used as an indirection layer between `reactive` and `reactor` modules. */ public fun injectCoroutineContext(publisher: Publisher, coroutineContext: CoroutineContext): Publisher -} \ No newline at end of file +} diff --git a/reactive/kotlinx-coroutines-reactive/src/Convert.kt b/reactive/kotlinx-coroutines-reactive/src/Convert.kt index f1ebd23a73..fb06ca3a3c 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Convert.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Convert.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.reactive diff --git a/reactive/kotlinx-coroutines-reactive/src/Migration.kt b/reactive/kotlinx-coroutines-reactive/src/Migration.kt index 40dfec03f5..4e0dca63de 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Migration.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Migration.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:JvmMultifileClass @@ -33,4 +33,4 @@ public fun Flow.asPublisherDeprecated(): Publisher = asPublisher level = DeprecationLevel.ERROR, replaceWith = ReplaceWith("asFlow().buffer(batchSize)", imports = ["kotlinx.coroutines.flow.*"]) ) -public fun Publisher.asFlow(batchSize: Int): Flow = asFlow().buffer(batchSize) \ No newline at end of file +public fun Publisher.asFlow(batchSize: Int): Flow = asFlow().buffer(batchSize) diff --git a/reactive/kotlinx-coroutines-reactive/src/Publish.kt b/reactive/kotlinx-coroutines-reactive/src/Publish.kt index 704b7142b3..68c1702913 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Publish.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Publish.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") diff --git a/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt b/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt index b3b764a43c..3a81c0696d 100644 --- a/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt +++ b/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.reactive @@ -18,16 +18,16 @@ import kotlin.coroutines.* * Transforms the given reactive [Publisher] into [Flow]. * Use [buffer] operator on the resulting flow to specify the size of the backpressure. * More precisely, it specifies the value of the subscription's [request][Subscription.request]. - * `1` is used by default. + * [buffer] default capacity is used by default. * - * If any of the resulting flow transformations fails, subscription is immediately cancelled and all in-flights elements + * If any of the resulting flow transformations fails, subscription is immediately cancelled and all in-flight elements * are discarded. * * This function is integrated with `ReactorContext` from `kotlinx-coroutines-reactor` module, * see its documentation for additional details. */ public fun Publisher.asFlow(): Flow = - PublisherAsFlow(this, 1) + PublisherAsFlow(this) /** * Transforms the given flow to a reactive specification compliant [Publisher]. @@ -39,42 +39,48 @@ public fun Flow.asPublisher(): Publisher = FlowAsPublisher(this) private class PublisherAsFlow( private val publisher: Publisher, - capacity: Int -) : ChannelFlow(EmptyCoroutineContext, capacity) { + context: CoroutineContext = EmptyCoroutineContext, + capacity: Int = Channel.BUFFERED +) : ChannelFlow(context, capacity) { override fun create(context: CoroutineContext, capacity: Int): ChannelFlow = - PublisherAsFlow(publisher, capacity) - - override fun produceImpl(scope: CoroutineScope): ReceiveChannel { - // use another channel for conflation (cannot do openSubscription) - if (capacity < 0) return super.produceImpl(scope) - // Open subscription channel directly - val channel = publisher - .injectCoroutineContext(scope.coroutineContext) - .openSubscription(capacity) - val handle = scope.coroutineContext[Job]?.invokeOnCompletion(onCancelling = true) { cause -> - channel.cancel(cause?.let { - it as? CancellationException ?: CancellationException("Job was cancelled", it) - }) - } - if (handle != null && handle !== NonDisposableHandle) { - (channel as SendChannel<*>).invokeOnClose { - handle.dispose() - } - } - return channel - } + PublisherAsFlow(publisher, context, capacity) + /* + * Suppress for Channel.CHANNEL_DEFAULT_CAPACITY. + * It's too counter-intuitive to be public and moving it to Flow companion + * will also create undesired effect. + */ + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") private val requestSize: Long get() = when (capacity) { Channel.CONFLATED -> Long.MAX_VALUE // request all and conflate incoming Channel.RENDEZVOUS -> 1L // need to request at least one anyway Channel.UNLIMITED -> Long.MAX_VALUE // reactive streams way to say "give all" must be Long.MAX_VALUE + Channel.BUFFERED -> Channel.CHANNEL_DEFAULT_CAPACITY.toLong() else -> capacity.toLong().also { check(it >= 1) } } override suspend fun collect(collector: FlowCollector) { + val collectContext = coroutineContext + val newDispatcher = context[ContinuationInterceptor] + if (newDispatcher == null || newDispatcher == collectContext[ContinuationInterceptor]) { + // fast path -- subscribe directly in this dispatcher + return collectImpl(collectContext + context, collector) + } + // slow path -- produce in a separate dispatcher + collectSlowPath(collector) + } + + private suspend fun collectSlowPath(collector: FlowCollector) { + coroutineScope { + collector.emitAll(produceImpl(this + context)) + } + } + + private suspend fun collectImpl(injectContext: CoroutineContext, collector: FlowCollector) { val subscriber = ReactiveSubscriber(capacity, requestSize) - publisher.injectCoroutineContext(coroutineContext).subscribe(subscriber) + // inject subscribe context into publisher + publisher.injectCoroutineContext(injectContext).subscribe(subscriber) try { var consumed = 0L while (true) { @@ -90,9 +96,9 @@ private class PublisherAsFlow( } } - // The second channel here is used only for broadcast + // The second channel here is used for produceIn/broadcastIn and slow-path (dispatcher change) override suspend fun collectTo(scope: ProducerScope) = - collect(SendingCollector(scope.channel)) + collectImpl(scope.coroutineContext, SendingCollector(scope.channel)) } @Suppress("SubscriberImplementation") diff --git a/reactive/kotlinx-coroutines-reactive/test/IntegrationTest.kt b/reactive/kotlinx-coroutines-reactive/test/IntegrationTest.kt index aaeaa009b3..3ec0b93481 100644 --- a/reactive/kotlinx-coroutines-reactive/test/IntegrationTest.kt +++ b/reactive/kotlinx-coroutines-reactive/test/IntegrationTest.kt @@ -5,13 +5,12 @@ package kotlinx.coroutines.reactive import kotlinx.coroutines.* -import org.hamcrest.MatcherAssert.* -import org.hamcrest.core.* -import org.junit.* +import org.junit.Test import org.junit.runner.* import org.junit.runners.* import org.reactivestreams.* import kotlin.coroutines.* +import kotlin.test.* @RunWith(Parameterized::class) class IntegrationTest( @@ -44,14 +43,14 @@ class IntegrationTest( // does not send anything } assertNSE { pub.awaitFirst() } - assertThat(pub.awaitFirstOrDefault("OK"), IsEqual("OK")) - assertThat(pub.awaitFirstOrNull(), IsNull()) - assertThat(pub.awaitFirstOrElse { "ELSE" }, IsEqual("ELSE")) + assertEquals("OK", pub.awaitFirstOrDefault("OK")) + assertNull(pub.awaitFirstOrNull()) + assertEquals("ELSE", pub.awaitFirstOrElse { "ELSE" }) assertNSE { pub.awaitLast() } assertNSE { pub.awaitSingle() } var cnt = 0 pub.collect { cnt++ } - assertThat(cnt, IsEqual(0)) + assertEquals(0, cnt) } @Test @@ -60,18 +59,18 @@ class IntegrationTest( if (delay) delay(1) send("OK") } - assertThat(pub.awaitFirst(), IsEqual("OK")) - assertThat(pub.awaitFirstOrDefault("!"), IsEqual("OK")) - assertThat(pub.awaitFirstOrNull(), IsEqual("OK")) - assertThat(pub.awaitFirstOrElse { "ELSE" }, IsEqual("OK")) - assertThat(pub.awaitLast(), IsEqual("OK")) - assertThat(pub.awaitSingle(), IsEqual("OK")) + assertEquals("OK", pub.awaitFirst()) + assertEquals("OK", pub.awaitFirstOrDefault("!")) + assertEquals("OK", pub.awaitFirstOrNull()) + assertEquals("OK", pub.awaitFirstOrElse { "ELSE" }) + assertEquals("OK", pub.awaitLast()) + assertEquals("OK", pub.awaitSingle()) var cnt = 0 pub.collect { - assertThat(it, IsEqual("OK")) + assertEquals("OK", it) cnt++ } - assertThat(cnt, IsEqual(1)) + assertEquals(1, cnt) } @Test @@ -83,11 +82,11 @@ class IntegrationTest( if (delay) delay(1) } } - assertThat(pub.awaitFirst(), IsEqual(1)) - assertThat(pub.awaitFirstOrDefault(0), IsEqual(1)) - assertThat(pub.awaitLast(), IsEqual(n)) - assertThat(pub.awaitFirstOrNull(), IsEqual(1)) - assertThat(pub.awaitFirstOrElse { 0 }, IsEqual(1)) + assertEquals(1, pub.awaitFirst()) + assertEquals(1, pub.awaitFirstOrDefault(0)) + assertEquals(n, pub.awaitLast()) + assertEquals(1, pub.awaitFirstOrNull()) + assertEquals(1, pub.awaitFirstOrElse { 0 }) assertIAE { pub.awaitSingle() } checkNumbers(n, pub) val channel = pub.openSubscription() @@ -125,9 +124,9 @@ class IntegrationTest( private suspend fun checkNumbers(n: Int, pub: Publisher) { var last = 0 pub.collect { - assertThat(it, IsEqual(++last)) + assertEquals(++last, it) } - assertThat(last, IsEqual(n)) + assertEquals(n, last) } private inline fun assertIAE(block: () -> Unit) { @@ -135,7 +134,7 @@ class IntegrationTest( block() expectUnreached() } catch (e: Throwable) { - assertThat(e, IsInstanceOf(IllegalArgumentException::class.java)) + assertTrue(e is IllegalArgumentException) } } @@ -144,7 +143,7 @@ class IntegrationTest( block() expectUnreached() } catch (e: Throwable) { - assertThat(e, IsInstanceOf(NoSuchElementException::class.java)) + assertTrue(e is NoSuchElementException) } } } \ No newline at end of file diff --git a/reactive/kotlinx-coroutines-reactive/test/IterableFlowTckTest.kt b/reactive/kotlinx-coroutines-reactive/test/IterableFlowTckTest.kt index 5dfd9d537d..906b2579d7 100644 --- a/reactive/kotlinx-coroutines-reactive/test/IterableFlowTckTest.kt +++ b/reactive/kotlinx-coroutines-reactive/test/IterableFlowTckTest.kt @@ -8,16 +8,18 @@ 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.junit.Assert.* 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 kotlin.test.* class IterableFlowTckTest : PublisherVerification(TestEnvironment()) { diff --git a/reactive/kotlinx-coroutines-reactive/test/PublishTest.kt b/reactive/kotlinx-coroutines-reactive/test/PublishTest.kt index 4ffa0746ca..9e3c07b6b7 100644 --- a/reactive/kotlinx-coroutines-reactive/test/PublishTest.kt +++ b/reactive/kotlinx-coroutines-reactive/test/PublishTest.kt @@ -5,10 +5,9 @@ package kotlinx.coroutines.reactive import kotlinx.coroutines.* -import org.hamcrest.core.* -import org.junit.* -import org.junit.Assert.* +import org.junit.Test import org.reactivestreams.* +import kotlin.test.* class PublishTest : TestBase() { @Test @@ -45,7 +44,7 @@ class PublishTest : TestBase() { } override fun onNext(t: Int) { expect(6) - assertThat(t, IsEqual(42)) + assertEquals(42, t) } override fun onComplete() { expect(8) } override fun onError(t: Throwable?) { expectUnreached() } @@ -72,8 +71,8 @@ class PublishTest : TestBase() { override fun onComplete() { expectUnreached() } override fun onError(t: Throwable) { expect(6) - assertThat(t, IsInstanceOf(RuntimeException::class.java)) - assertThat(t.message, IsEqual("OK")) + assertTrue(t is RuntimeException) + assertEquals("OK", t.message) } }) expect(4) diff --git a/reactive/kotlinx-coroutines-reactive/test/PublisherAsFlowTest.kt b/reactive/kotlinx-coroutines-reactive/test/PublisherAsFlowTest.kt index a37719de64..61f88f6af3 100644 --- a/reactive/kotlinx-coroutines-reactive/test/PublisherAsFlowTest.kt +++ b/reactive/kotlinx-coroutines-reactive/test/PublisherAsFlowTest.kt @@ -56,7 +56,43 @@ class PublisherAsFlowTest : TestBase() { expect(6) } + publisher.asFlow().buffer(1).collect { + expect(it) + } + + finish(8) + } + + @Test + fun testBufferSizeDefault() = runTest { + val publisher = publish(currentDispatcher()) { + repeat(64) { + send(it + 1) + expect(it + 1) + } + assertFalse { offer(-1) } + } + publisher.asFlow().collect { + expect(64 + it) + } + + finish(129) + } + + @Test + fun testDefaultCapacityIsProperlyOverwritten() = runTest { + val publisher = publish(currentDispatcher()) { + expect(1) + send(3) + expect(2) + send(5) + expect(4) + send(7) + expect(6) + } + + publisher.asFlow().flowOn(wrapperDispatcher()).buffer(1).collect { expect(it) } @@ -120,13 +156,13 @@ class PublisherAsFlowTest : TestBase() { 7 -> try { send(value) } catch (e: CancellationException) { - finish(6) + expect(5) throw e } else -> expectUnreached() } } - }.asFlow() + }.asFlow().buffer(1) assertFailsWith { coroutineScope { expect(2) @@ -143,6 +179,6 @@ class PublisherAsFlowTest : TestBase() { } } } - expect(5) + finish(6) } -} \ No newline at end of file +} diff --git a/reactive/kotlinx-coroutines-reactive/test/PublisherMultiTest.kt b/reactive/kotlinx-coroutines-reactive/test/PublisherMultiTest.kt index e238d396a8..e3b1d3b384 100644 --- a/reactive/kotlinx-coroutines-reactive/test/PublisherMultiTest.kt +++ b/reactive/kotlinx-coroutines-reactive/test/PublisherMultiTest.kt @@ -5,9 +5,8 @@ package kotlinx.coroutines.reactive import kotlinx.coroutines.* -import org.hamcrest.core.* -import org.junit.* -import org.junit.Assert.* +import org.junit.Test +import kotlin.test.* class PublisherMultiTest : TestBase() { @Test @@ -27,6 +26,6 @@ class PublisherMultiTest : TestBase() { observable.collect { assertTrue(resultSet.add(it)) } - assertThat(resultSet.size, IsEqual(n)) + assertEquals(n, resultSet.size) } } diff --git a/reactive/kotlinx-coroutines-reactive/test/PublisherSubscriptionSelectTest.kt b/reactive/kotlinx-coroutines-reactive/test/PublisherSubscriptionSelectTest.kt index ef1784776d..110718ac55 100644 --- a/reactive/kotlinx-coroutines-reactive/test/PublisherSubscriptionSelectTest.kt +++ b/reactive/kotlinx-coroutines-reactive/test/PublisherSubscriptionSelectTest.kt @@ -6,10 +6,10 @@ package kotlinx.coroutines.reactive import kotlinx.coroutines.* import kotlinx.coroutines.selects.* -import org.junit.* -import org.junit.Assert.* +import org.junit.Test import org.junit.runner.* import org.junit.runners.* +import kotlin.test.* @RunWith(Parameterized::class) class PublisherSubscriptionSelectTest(private val request: Int) : TestBase() { diff --git a/reactive/kotlinx-coroutines-reactor/README.md b/reactive/kotlinx-coroutines-reactor/README.md index db1af0dab2..50e1602a3a 100644 --- a/reactive/kotlinx-coroutines-reactor/README.md +++ b/reactive/kotlinx-coroutines-reactor/README.md @@ -19,7 +19,7 @@ Integration with [Flow]: | --------------- | -------------- | --------------- | [Flow.asFlux] | `Flux` | Converts the given flow to the TCK-compliant Flux. -This adapter is integrated with Reactor's `Context` and coroutines [ReactiveContext]. +This adapter is integrated with Reactor's `Context` and coroutines [ReactorContext]. Conversion functions: @@ -35,11 +35,14 @@ Conversion functions: [CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html [CoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-dispatcher/index.html + +[Flow]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/index.html [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 +[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 diff --git a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-reactor.txt b/reactive/kotlinx-coroutines-reactor/api/kotlinx-coroutines-reactor.api similarity index 100% rename from binary-compatibility-validator/reference-public-api/kotlinx-coroutines-reactor.txt rename to reactive/kotlinx-coroutines-reactor/api/kotlinx-coroutines-reactor.api diff --git a/reactive/kotlinx-coroutines-reactor/build.gradle b/reactive/kotlinx-coroutines-reactor/build.gradle index c73716d494..a33d3e687a 100644 --- a/reactive/kotlinx-coroutines-reactor/build.gradle +++ b/reactive/kotlinx-coroutines-reactor/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ dependencies { @@ -20,4 +20,4 @@ compileTestKotlin { compileKotlin { kotlinOptions.jvmTarget = "1.8" -} \ No newline at end of file +} diff --git a/reactive/kotlinx-coroutines-reactor/resources/META-INF/services/kotlinx.coroutines.reactive.ContextInjector b/reactive/kotlinx-coroutines-reactor/resources/META-INF/services/kotlinx.coroutines.reactive.ContextInjector index 0097ec3539..9838c12bd4 100644 --- a/reactive/kotlinx-coroutines-reactor/resources/META-INF/services/kotlinx.coroutines.reactive.ContextInjector +++ b/reactive/kotlinx-coroutines-reactor/resources/META-INF/services/kotlinx.coroutines.reactive.ContextInjector @@ -1 +1 @@ -kotlinx.coroutines.reactor.ReactorContextInjector \ No newline at end of file +kotlinx.coroutines.reactor.ReactorContextInjector diff --git a/reactive/kotlinx-coroutines-reactor/src/Convert.kt b/reactive/kotlinx-coroutines-reactor/src/Convert.kt index 3d9aa13e43..dc264f8d89 100644 --- a/reactive/kotlinx-coroutines-reactor/src/Convert.kt +++ b/reactive/kotlinx-coroutines-reactor/src/Convert.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.reactor @@ -51,4 +51,4 @@ public fun Deferred.asMono(context: CoroutineContext): Mono = mono(co public fun ReceiveChannel.asFlux(context: CoroutineContext = EmptyCoroutineContext): Flux = flux(context) { for (t in this@asFlux) send(t) -} \ No newline at end of file +} diff --git a/reactive/kotlinx-coroutines-reactor/src/Flux.kt b/reactive/kotlinx-coroutines-reactor/src/Flux.kt index b6cc1615b0..addc528c32 100644 --- a/reactive/kotlinx-coroutines-reactor/src/Flux.kt +++ b/reactive/kotlinx-coroutines-reactor/src/Flux.kt @@ -1,6 +1,6 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") diff --git a/reactive/kotlinx-coroutines-reactor/src/Migration.kt b/reactive/kotlinx-coroutines-reactor/src/Migration.kt index c80d2690ab..f0c24bb08d 100644 --- a/reactive/kotlinx-coroutines-reactor/src/Migration.kt +++ b/reactive/kotlinx-coroutines-reactor/src/Migration.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + @file:JvmName("FlowKt") package kotlinx.coroutines.reactor diff --git a/reactive/kotlinx-coroutines-reactor/src/Mono.kt b/reactive/kotlinx-coroutines-reactor/src/Mono.kt index 415932dd7d..503c891ea4 100644 --- a/reactive/kotlinx-coroutines-reactor/src/Mono.kt +++ b/reactive/kotlinx-coroutines-reactor/src/Mono.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") diff --git a/reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt b/reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt index 19240c42ad..9f5eb23169 100644 --- a/reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt +++ b/reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package kotlinx.coroutines.reactor import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/reactive/kotlinx-coroutines-reactor/src/ReactorContextInjector.kt b/reactive/kotlinx-coroutines-reactor/src/ReactorContextInjector.kt index 68309bbcdb..a9d140a9fd 100644 --- a/reactive/kotlinx-coroutines-reactor/src/ReactorContextInjector.kt +++ b/reactive/kotlinx-coroutines-reactor/src/ReactorContextInjector.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package kotlinx.coroutines.reactor import kotlinx.coroutines.reactive.* @@ -19,4 +23,4 @@ internal class ReactorContextInjector : ContextInjector { else -> publisher } } -} \ No newline at end of file +} diff --git a/reactive/kotlinx-coroutines-reactor/src/ReactorFlow.kt b/reactive/kotlinx-coroutines-reactor/src/ReactorFlow.kt index 5d47e0218c..d665c88d35 100644 --- a/reactive/kotlinx-coroutines-reactor/src/ReactorFlow.kt +++ b/reactive/kotlinx-coroutines-reactor/src/ReactorFlow.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.reactor @@ -25,4 +25,4 @@ private class FlowAsFlux(private val flow: Flow) : Flux() { val source = if (hasContext) flow.flowOn(subscriber.currentContext().asCoroutineContext()) else flow subscriber.onSubscribe(FlowSubscription(source, subscriber)) } -} \ No newline at end of file +} diff --git a/reactive/kotlinx-coroutines-reactor/src/Scheduler.kt b/reactive/kotlinx-coroutines-reactor/src/Scheduler.kt index 78c0c5e1e6..833ceb2d6a 100644 --- a/reactive/kotlinx-coroutines-reactor/src/Scheduler.kt +++ b/reactive/kotlinx-coroutines-reactor/src/Scheduler.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.reactor @@ -53,4 +53,4 @@ public class SchedulerCoroutineDispatcher( private fun Disposable.asDisposableHandle(): DisposableHandle = object : DisposableHandle { override fun dispose() = this@asDisposableHandle.dispose() - } \ No newline at end of file + } diff --git a/reactive/kotlinx-coroutines-reactor/test/ConvertTest.kt b/reactive/kotlinx-coroutines-reactor/test/ConvertTest.kt index 10e05b7658..82664a2dee 100644 --- a/reactive/kotlinx-coroutines-reactor/test/ConvertTest.kt +++ b/reactive/kotlinx-coroutines-reactor/test/ConvertTest.kt @@ -8,7 +8,8 @@ import kotlinx.coroutines.* import kotlinx.coroutines.channels.* import kotlinx.coroutines.reactive.* import org.junit.* -import org.junit.Assert.* +import org.junit.Test +import kotlin.test.* class ConvertTest : TestBase() { @Test @@ -66,9 +67,9 @@ class ConvertTest : TestBase() { null } val mono1 = d.asMono(Dispatchers.Unconfined) - checkMonoValue(mono1, ::assertNull) + checkMonoValue(mono1, Assert::assertNull) val mono2 = d.asMono(Dispatchers.Unconfined) - checkMonoValue(mono2, ::assertNull) + checkMonoValue(mono2, Assert::assertNull) } @Test diff --git a/reactive/kotlinx-coroutines-reactor/test/FlowAsFluxTest.kt b/reactive/kotlinx-coroutines-reactor/test/FlowAsFluxTest.kt index ccef000b5b..e4bd8b315b 100644 --- a/reactive/kotlinx-coroutines-reactor/test/FlowAsFluxTest.kt +++ b/reactive/kotlinx-coroutines-reactor/test/FlowAsFluxTest.kt @@ -4,16 +4,17 @@ import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import kotlinx.coroutines.reactive.* import org.junit.Test -import reactor.core.publisher.Mono +import reactor.core.publisher.* import reactor.util.context.Context -import kotlin.test.assertEquals +import kotlin.test.* class FlowAsFluxTest : TestBase() { @Test - fun testFlowToFluxContextPropagation() { + fun testFlowAsFluxContextPropagation() { val flux = flow { (1..4).forEach { i -> emit(createMono(i).awaitFirst()) } - } .asFlux() + } + .asFlux() .subscriberContext(Context.of(1, "1")) .subscriberContext(Context.of(2, "2", 3, "3", 4, "4")) val list = flux.collectList().block()!! @@ -24,4 +25,47 @@ class FlowAsFluxTest : TestBase() { val ctx = coroutineContext[ReactorContext]!!.context ctx.getOrDefault(i, "noValue") } + + @Test + fun testFluxAsFlowContextPropagationWithFlowOn() = runTest { + expect(1) + Flux.create { + it.next("OK") + it.complete() + } + .subscriberContext { ctx -> + expect(2) + assertEquals("CTX", ctx.get(1)) + ctx + } + .asFlow() + .flowOn(ReactorContext(Context.of(1, "CTX"))) + .collect { + expect(3) + assertEquals("OK", it) + } + finish(4) + } + + @Test + fun testFluxAsFlowContextPropagationFromScope() = runTest { + expect(1) + withContext(ReactorContext(Context.of(1, "CTX"))) { + Flux.create { + it.next("OK") + it.complete() + } + .subscriberContext { ctx -> + expect(2) + assertEquals("CTX", ctx.get(1)) + ctx + } + .asFlow() + .collect { + expect(3) + assertEquals("OK", it) + } + } + finish(4) + } } \ No newline at end of file diff --git a/reactive/kotlinx-coroutines-reactor/test/FluxContextTest.kt b/reactive/kotlinx-coroutines-reactor/test/FluxContextTest.kt new file mode 100644 index 0000000000..1ed3a164f6 --- /dev/null +++ b/reactive/kotlinx-coroutines-reactor/test/FluxContextTest.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.reactor + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.reactive.* +import org.junit.* +import org.junit.Test +import reactor.core.publisher.* +import kotlin.test.* + +class FluxContextTest : TestBase() { + private val dispatcher = newSingleThreadContext("FluxContextTest") + + @After + fun tearDown() { + dispatcher.close() + } + + @Test + fun testFluxCreateAsFlowThread() = runTest { + expect(1) + val mainThread = Thread.currentThread() + val dispatcherThread = withContext(dispatcher) { Thread.currentThread() } + assertTrue(dispatcherThread != mainThread) + Flux.create { + assertEquals(dispatcherThread, Thread.currentThread()) + it.next("OK") + it.complete() + } + .asFlow() + .flowOn(dispatcher) + .collect { + expect(2) + assertEquals("OK", it) + assertEquals(mainThread, Thread.currentThread()) + } + finish(3) + } +} \ No newline at end of file diff --git a/reactive/kotlinx-coroutines-reactor/test/FluxMultiTest.kt b/reactive/kotlinx-coroutines-reactor/test/FluxMultiTest.kt index 7203120dae..0d5f9e2c18 100644 --- a/reactive/kotlinx-coroutines-reactor/test/FluxMultiTest.kt +++ b/reactive/kotlinx-coroutines-reactor/test/FluxMultiTest.kt @@ -7,9 +7,10 @@ package kotlinx.coroutines.reactor import kotlinx.coroutines.* import kotlinx.coroutines.reactive.* import org.junit.* -import org.junit.Assert.* +import org.junit.Test import reactor.core.publisher.* import java.io.* +import kotlin.test.* class FluxMultiTest : TestBase() { @Test diff --git a/reactive/kotlinx-coroutines-reactor/test/FluxSingleTest.kt b/reactive/kotlinx-coroutines-reactor/test/FluxSingleTest.kt index a3e9658930..3879c62c71 100644 --- a/reactive/kotlinx-coroutines-reactor/test/FluxSingleTest.kt +++ b/reactive/kotlinx-coroutines-reactor/test/FluxSingleTest.kt @@ -7,9 +7,10 @@ package kotlinx.coroutines.reactor import kotlinx.coroutines.* import kotlinx.coroutines.reactive.* import org.junit.* -import org.junit.Assert.* +import org.junit.Test import reactor.core.publisher.* import java.time.Duration.* +import kotlin.test.* class FluxSingleTest : TestBase() { diff --git a/reactive/kotlinx-coroutines-reactor/test/FluxTest.kt b/reactive/kotlinx-coroutines-reactor/test/FluxTest.kt index 2562c9d3db..31f5f5d979 100644 --- a/reactive/kotlinx-coroutines-reactor/test/FluxTest.kt +++ b/reactive/kotlinx-coroutines-reactor/test/FluxTest.kt @@ -7,7 +7,6 @@ package kotlinx.coroutines.reactor import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import kotlinx.coroutines.reactive.* -import org.hamcrest.core.* import org.junit.* import org.junit.Test import kotlin.test.* @@ -23,7 +22,7 @@ class FluxTest : TestBase() { expect(2) flux.subscribe { value -> expect(5) - Assert.assertThat(value, IsEqual("OK")) + assertEquals("OK", value) } expect(3) yield() // to started coroutine @@ -42,8 +41,8 @@ class FluxTest : TestBase() { expectUnreached() }, { error -> expect(5) - Assert.assertThat(error, IsInstanceOf(RuntimeException::class.java)) - Assert.assertThat(error.message, IsEqual("OK")) + assertTrue(error is RuntimeException) + assertEquals("OK", error.message) }) expect(3) yield() // to started coroutine diff --git a/reactive/kotlinx-coroutines-reactor/test/MonoTest.kt b/reactive/kotlinx-coroutines-reactor/test/MonoTest.kt index 223ba7be5d..551988b814 100644 --- a/reactive/kotlinx-coroutines-reactor/test/MonoTest.kt +++ b/reactive/kotlinx-coroutines-reactor/test/MonoTest.kt @@ -7,15 +7,15 @@ package kotlinx.coroutines.reactor import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import kotlinx.coroutines.reactive.* -import org.hamcrest.core.* import org.junit.* -import org.junit.Assert.* +import org.junit.Test import org.reactivestreams.* import reactor.core.publisher.* import reactor.util.context.* import java.time.* import java.time.Duration.* import java.util.function.* +import kotlin.test.* class MonoTest : TestBase() { @Before @@ -33,7 +33,7 @@ class MonoTest : TestBase() { expect(2) mono.subscribe { value -> expect(5) - assertThat(value, IsEqual("OK")) + assertEquals("OK", value) } expect(3) yield() // to started coroutine @@ -52,8 +52,8 @@ class MonoTest : TestBase() { expectUnreached() }, { error -> expect(5) - assertThat(error, IsInstanceOf(RuntimeException::class.java)) - assertThat(error.message, IsEqual("OK")) + assertTrue(error is RuntimeException) + assertEquals("OK", error.message) }) expect(3) yield() // to started coroutine diff --git a/reactive/kotlinx-coroutines-reactor/test/SchedulerTest.kt b/reactive/kotlinx-coroutines-reactor/test/SchedulerTest.kt index 8bc72c2f1f..bed607c9ed 100644 --- a/reactive/kotlinx-coroutines-reactor/test/SchedulerTest.kt +++ b/reactive/kotlinx-coroutines-reactor/test/SchedulerTest.kt @@ -5,12 +5,10 @@ package kotlinx.coroutines.reactor import kotlinx.coroutines.* -import org.hamcrest.core.IsEqual -import org.hamcrest.core.IsNot -import org.junit.Assert.assertThat import org.junit.Before import org.junit.Test import reactor.core.scheduler.Schedulers +import kotlin.test.* class SchedulerTest : TestBase() { @Before @@ -24,11 +22,11 @@ class SchedulerTest : TestBase() { val mainThread = Thread.currentThread() withContext(Schedulers.single().asCoroutineDispatcher()) { val t1 = Thread.currentThread() - assertThat(t1, IsNot(IsEqual(mainThread))) + assertNotSame(t1, mainThread) expect(2) delay(100) val t2 = Thread.currentThread() - assertThat(t2, IsNot(IsEqual(mainThread))) + assertNotSame(t2, mainThread) expect(3) } finish(4) diff --git a/reactive/kotlinx-coroutines-rx2/README.md b/reactive/kotlinx-coroutines-rx2/README.md index b3874b05d3..f0fbeb001e 100644 --- a/reactive/kotlinx-coroutines-rx2/README.md +++ b/reactive/kotlinx-coroutines-rx2/README.md @@ -14,10 +14,11 @@ Coroutine builders: Integration with [Flow]: -| **Name** | **Result** | **Description** -| --------------- | -------------- | --------------- -| [Flow.asFlowable] | `Flowable` | Converts the given flow to a cold Flowable. -| [Flow.asObservable] | `Observable` | Converts the given flow to a cold Observable. +| **Name** | **Result** | **Description** +| --------------- | -------------- | --------------- +| [Flow.asFlowable] | `Flowable` | Converts the given flow to a cold Flowable. +| [Flow.asObservable] | `Observable` | Converts the given flow to a cold Observable. +| [ObservableSource.asFlow] | `Flow` | Converts the given cold ObservableSource to flow Suspending extension functions and suspending iteration: @@ -56,6 +57,8 @@ 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 + +[Flow]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/index.html [rxCompletable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/rx-completable.html @@ -65,6 +68,7 @@ Conversion functions: [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 diff --git a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-rx2.txt b/reactive/kotlinx-coroutines-rx2/api/kotlinx-coroutines-rx2.api similarity index 98% rename from binary-compatibility-validator/reference-public-api/kotlinx-coroutines-rx2.txt rename to reactive/kotlinx-coroutines-rx2/api/kotlinx-coroutines-rx2.api index 54a9663a41..22f40384f0 100644 --- a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-rx2.txt +++ b/reactive/kotlinx-coroutines-rx2/api/kotlinx-coroutines-rx2.api @@ -29,6 +29,7 @@ public final class kotlinx/coroutines/rx2/RxCompletableKt { public final class kotlinx/coroutines/rx2/RxConvertKt { public static final fun asCompletable (Lkotlinx/coroutines/Job;Lkotlin/coroutines/CoroutineContext;)Lio/reactivex/Completable; + public static final fun asFlow (Lio/reactivex/ObservableSource;)Lkotlinx/coroutines/flow/Flow; public static final fun asMaybe (Lkotlinx/coroutines/Deferred;Lkotlin/coroutines/CoroutineContext;)Lio/reactivex/Maybe; public static final fun asObservable (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;)Lio/reactivex/Observable; public static final fun asSingle (Lkotlinx/coroutines/Deferred;Lkotlin/coroutines/CoroutineContext;)Lio/reactivex/Single; diff --git a/reactive/kotlinx-coroutines-rx2/build.gradle b/reactive/kotlinx-coroutines-rx2/build.gradle index 8fa85092b5..b583025ce3 100644 --- a/reactive/kotlinx-coroutines-rx2/build.gradle +++ b/reactive/kotlinx-coroutines-rx2/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ dependencies { diff --git a/reactive/kotlinx-coroutines-rx2/src/RxAwait.kt b/reactive/kotlinx-coroutines-rx2/src/RxAwait.kt index ce1040e911..d9435b6b9e 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxAwait.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxAwait.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.rx2 diff --git a/reactive/kotlinx-coroutines-rx2/src/RxCancellable.kt b/reactive/kotlinx-coroutines-rx2/src/RxCancellable.kt index a0c32f9f8a..f7596f27d0 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxCancellable.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxCancellable.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.rx2 @@ -22,4 +22,4 @@ internal fun handleUndeliverableException(cause: Throwable, context: CoroutineCo } catch (e: Throwable) { handleCoroutineException(context, cause) } -} \ No newline at end of file +} diff --git a/reactive/kotlinx-coroutines-rx2/src/RxChannel.kt b/reactive/kotlinx-coroutines-rx2/src/RxChannel.kt index 0a19eda860..9f31b2aaa8 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxChannel.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxChannel.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.rx2 diff --git a/reactive/kotlinx-coroutines-rx2/src/RxCompletable.kt b/reactive/kotlinx-coroutines-rx2/src/RxCompletable.kt index ab96844c60..bc91fa53e0 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxCompletable.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxCompletable.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") diff --git a/reactive/kotlinx-coroutines-rx2/src/RxConvert.kt b/reactive/kotlinx-coroutines-rx2/src/RxConvert.kt index bd369cad55..0be606ffc2 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxConvert.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxConvert.kt @@ -1,14 +1,16 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.rx2 import io.reactivex.* +import io.reactivex.disposables.* import kotlinx.coroutines.* import kotlinx.coroutines.channels.* import kotlinx.coroutines.flow.* import kotlinx.coroutines.reactive.* +import java.util.concurrent.atomic.* import kotlin.coroutines.* /** @@ -77,6 +79,30 @@ public fun ReceiveChannel.asObservable(context: CoroutineContext): send(t) } +/** + * Transforms given cold [ObservableSource] into cold [Flow]. + * + * The resulting flow is _cold_, which means that [ObservableSource.subscribe] is called every time a terminal operator + * is applied to the resulting flow. + * + * A channel with the [default][Channel.BUFFERED] buffer size is used. Use the [buffer] operator on the + * resulting flow to specify a user-defined value and to control what happens when data is produced faster + * than consumed, i.e. to control the back-pressure behavior. Check [callbackFlow] for more details. + */ +@ExperimentalCoroutinesApi +public fun ObservableSource.asFlow(): Flow = callbackFlow { + val disposableRef = AtomicReference() + val observer = object : Observer { + override fun onComplete() { close() } + override fun onSubscribe(d: Disposable) { if (!disposableRef.compareAndSet(null, d)) d.dispose() } + override fun onNext(t: T) { sendBlocking(t) } + override fun onError(e: Throwable) { close(e) } + } + + subscribe(observer) + awaitClose { disposableRef.getAndSet(Disposables.disposed())?.dispose() } +} + /** * Converts the given flow to a cold observable. * The original flow is cancelled when the observable subscriber is disposed. diff --git a/reactive/kotlinx-coroutines-rx2/src/RxFlowable.kt b/reactive/kotlinx-coroutines-rx2/src/RxFlowable.kt index 7924a3f15c..78d397c3b3 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxFlowable.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxFlowable.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") @@ -52,4 +52,4 @@ public fun CoroutineScope.rxFlowable( @BuilderInference block: suspend ProducerScope.() -> Unit ): Flowable = Flowable.fromPublisher(publishInternal(this, context, RX_HANDLER, block)) -private val RX_HANDLER: (Throwable, CoroutineContext) -> Unit = ::handleUndeliverableException \ No newline at end of file +private val RX_HANDLER: (Throwable, CoroutineContext) -> Unit = ::handleUndeliverableException diff --git a/reactive/kotlinx-coroutines-rx2/src/RxMaybe.kt b/reactive/kotlinx-coroutines-rx2/src/RxMaybe.kt index 9fb5f650f4..bfbcb38d92 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxMaybe.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxMaybe.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") diff --git a/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt b/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt index b8de66df08..2f483879b8 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") diff --git a/reactive/kotlinx-coroutines-rx2/src/RxScheduler.kt b/reactive/kotlinx-coroutines-rx2/src/RxScheduler.kt index 610a5bcd6d..3ddb67649e 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxScheduler.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxScheduler.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.rx2 diff --git a/reactive/kotlinx-coroutines-rx2/src/RxSingle.kt b/reactive/kotlinx-coroutines-rx2/src/RxSingle.kt index 07088909b5..23040601df 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxSingle.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxSingle.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") diff --git a/reactive/kotlinx-coroutines-rx2/test/CompletableTest.kt b/reactive/kotlinx-coroutines-rx2/test/CompletableTest.kt index 04e869758c..298b32bf32 100644 --- a/reactive/kotlinx-coroutines-rx2/test/CompletableTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/CompletableTest.kt @@ -8,9 +8,8 @@ import io.reactivex.* import io.reactivex.disposables.* import io.reactivex.exceptions.* import kotlinx.coroutines.* -import org.hamcrest.core.* -import org.junit.* -import org.junit.Assert.* +import org.junit.Test +import kotlin.test.* class CompletableTest : TestBase() { @Test @@ -40,8 +39,8 @@ class CompletableTest : TestBase() { expectUnreached() }, { error -> expect(5) - assertThat(error, IsInstanceOf(RuntimeException::class.java)) - assertThat(error.message, IsEqual("OK")) + assertTrue(error is RuntimeException) + assertEquals("OK", error.message) }) expect(3) yield() // to completable coroutine @@ -95,7 +94,7 @@ class CompletableTest : TestBase() { expectUnreached() } catch (e: RuntimeException) { finish(4) - assertThat(e.message, IsEqual("OK")) + assertEquals("OK", e.message) } } diff --git a/reactive/kotlinx-coroutines-rx2/test/ConvertTest.kt b/reactive/kotlinx-coroutines-rx2/test/ConvertTest.kt index 758b632604..a43366555e 100644 --- a/reactive/kotlinx-coroutines-rx2/test/ConvertTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/ConvertTest.kt @@ -6,8 +6,9 @@ package kotlinx.coroutines.rx2 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* -import org.junit.* -import org.junit.Assert.* +import org.junit.Assert +import org.junit.Test +import kotlin.test.* class ConvertTest : TestBase() { @Test @@ -64,9 +65,9 @@ class ConvertTest : TestBase() { null } val maybe1 = d.asMaybe(Dispatchers.Unconfined) - checkMaybeValue(maybe1, ::assertNull) + checkMaybeValue(maybe1, Assert::assertNull) val maybe2 = d.asMaybe(Dispatchers.Unconfined) - checkMaybeValue(maybe2, ::assertNull) + checkMaybeValue(maybe2, Assert::assertNull) } @Test diff --git a/reactive/kotlinx-coroutines-rx2/test/FlowAsObservableTest.kt b/reactive/kotlinx-coroutines-rx2/test/FlowAsObservableTest.kt index ab9e402893..0908b34cf2 100644 --- a/reactive/kotlinx-coroutines-rx2/test/FlowAsObservableTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/FlowAsObservableTest.kt @@ -6,9 +6,8 @@ package kotlinx.coroutines.rx2 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* -import org.hamcrest.core.* -import org.junit.* -import org.junit.Assert.* +import org.junit.Test +import kotlin.test.* class FlowAsObservableTest : TestBase() { @Test @@ -39,7 +38,7 @@ class FlowAsObservableTest : TestBase() { expect(2) observable.subscribe({ expectUnreached() }, { error -> expect(4) - assertThat(error, IsInstanceOf(RuntimeException::class.java)) + assertTrue(error is RuntimeException) assertEquals("OK", error.message) }) finish(5) diff --git a/reactive/kotlinx-coroutines-rx2/test/FlowableContextTest.kt b/reactive/kotlinx-coroutines-rx2/test/FlowableContextTest.kt new file mode 100644 index 0000000000..2cc3243551 --- /dev/null +++ b/reactive/kotlinx-coroutines-rx2/test/FlowableContextTest.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.rx2 + +import io.reactivex.* +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.reactive.* +import org.junit.* +import org.junit.Test +import kotlin.test.* + +class FlowableContextTest : TestBase() { + private val dispatcher = newSingleThreadContext("FlowableContextTest") + + @After + fun tearDown() { + dispatcher.close() + } + + @Test + fun testFlowableCreateAsFlowThread() = runTest { + expect(1) + val mainThread = Thread.currentThread() + val dispatcherThread = withContext(dispatcher) { Thread.currentThread() } + assertTrue(dispatcherThread != mainThread) + Flowable.create({ + assertEquals(dispatcherThread, Thread.currentThread()) + it.onNext("OK") + it.onComplete() + }, BackpressureStrategy.BUFFER) + .asFlow() + .flowOn(dispatcher) + .collect { + expect(2) + assertEquals("OK", it) + assertEquals(mainThread, Thread.currentThread()) + } + finish(3) + } +} \ No newline at end of file diff --git a/reactive/kotlinx-coroutines-rx2/test/FlowableTest.kt b/reactive/kotlinx-coroutines-rx2/test/FlowableTest.kt index aebf9993cc..148d1f9999 100644 --- a/reactive/kotlinx-coroutines-rx2/test/FlowableTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/FlowableTest.kt @@ -6,7 +6,6 @@ package kotlinx.coroutines.rx2 import kotlinx.coroutines.* import kotlinx.coroutines.reactive.* -import org.hamcrest.core.* import org.junit.* import org.junit.Test import kotlin.test.* @@ -22,7 +21,7 @@ class FlowableTest : TestBase() { expect(2) observable.subscribe { value -> expect(5) - Assert.assertThat(value, IsEqual("OK")) + assertEquals("OK", value) } expect(3) yield() // to started coroutine @@ -41,8 +40,8 @@ class FlowableTest : TestBase() { expectUnreached() }, { error -> expect(5) - Assert.assertThat(error, IsInstanceOf(RuntimeException::class.java)) - Assert.assertThat(error.message, IsEqual("OK")) + assertTrue(error is RuntimeException) + assertEquals("OK", error.message) }) expect(3) yield() // to started coroutine diff --git a/reactive/kotlinx-coroutines-rx2/test/IntegrationTest.kt b/reactive/kotlinx-coroutines-rx2/test/IntegrationTest.kt index ca7c0ca5ce..4faebbd251 100644 --- a/reactive/kotlinx-coroutines-rx2/test/IntegrationTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/IntegrationTest.kt @@ -6,12 +6,11 @@ package kotlinx.coroutines.rx2 import io.reactivex.* import kotlinx.coroutines.* -import org.hamcrest.MatcherAssert.* -import org.hamcrest.core.* -import org.junit.* +import org.junit.Test import org.junit.runner.* import org.junit.runners.* import kotlin.coroutines.* +import kotlin.test.* @RunWith(Parameterized::class) class IntegrationTest( @@ -44,16 +43,16 @@ class IntegrationTest( // does not send anything } assertNSE { observable.awaitFirst() } - assertThat(observable.awaitFirstOrDefault("OK"), IsEqual("OK")) - assertThat(observable.awaitFirstOrNull(), IsNull()) - assertThat(observable.awaitFirstOrElse { "ELSE" }, IsEqual("ELSE")) + assertEquals("OK", observable.awaitFirstOrDefault("OK")) + assertNull(observable.awaitFirstOrNull()) + assertEquals("ELSE", observable.awaitFirstOrElse { "ELSE" }) assertNSE { observable.awaitLast() } assertNSE { observable.awaitSingle() } var cnt = 0 observable.collect { cnt++ } - assertThat(cnt, IsEqual(0)) + assertEquals(0, cnt) } @Test @@ -62,18 +61,18 @@ class IntegrationTest( if (delay) delay(1) send("OK") } - assertThat(observable.awaitFirst(), IsEqual("OK")) - assertThat(observable.awaitFirstOrDefault("OK"), IsEqual("OK")) - assertThat(observable.awaitFirstOrNull(), IsEqual("OK")) - assertThat(observable.awaitFirstOrElse { "ELSE" }, IsEqual("OK")) - assertThat(observable.awaitLast(), IsEqual("OK")) - assertThat(observable.awaitSingle(), IsEqual("OK")) + assertEquals("OK", observable.awaitFirst()) + assertEquals("OK", observable.awaitFirstOrDefault("OK")) + assertEquals("OK", observable.awaitFirstOrNull()) + assertEquals("OK", observable.awaitFirstOrElse { "ELSE" }) + assertEquals("OK", observable.awaitLast()) + assertEquals("OK", observable.awaitSingle()) var cnt = 0 observable.collect { - assertThat(it, IsEqual("OK")) + assertEquals("OK", it) cnt++ } - assertThat(cnt, IsEqual(1)) + assertEquals(1, cnt) } @Test @@ -85,11 +84,11 @@ class IntegrationTest( if (delay) delay(1) } } - assertThat(observable.awaitFirst(), IsEqual(1)) - assertThat(observable.awaitFirstOrDefault(0), IsEqual(1)) - assertThat(observable.awaitFirstOrNull(), IsEqual(1)) - assertThat(observable.awaitFirstOrElse { 0 }, IsEqual(1)) - assertThat(observable.awaitLast(), IsEqual(n)) + assertEquals(1, observable.awaitFirst()) + assertEquals(1, observable.awaitFirstOrDefault(0)) + assertEquals(1, observable.awaitFirstOrNull()) + assertEquals(1, observable.awaitFirstOrElse { 0 }) + assertEquals(n, observable.awaitLast()) assertIAE { observable.awaitSingle() } checkNumbers(n, observable) val channel = observable.openSubscription() @@ -127,9 +126,9 @@ class IntegrationTest( private suspend fun checkNumbers(n: Int, observable: Observable) { var last = 0 observable.collect { - assertThat(it, IsEqual(++last)) + assertEquals(++last, it) } - assertThat(last, IsEqual(n)) + assertEquals(n, last) } @@ -138,7 +137,7 @@ class IntegrationTest( block() expectUnreached() } catch (e: Throwable) { - assertThat(e, IsInstanceOf(IllegalArgumentException::class.java)) + assertTrue(e is IllegalArgumentException) } } @@ -147,7 +146,7 @@ class IntegrationTest( block() expectUnreached() } catch (e: Throwable) { - assertThat(e, IsInstanceOf(NoSuchElementException::class.java)) + assertTrue(e is NoSuchElementException) } } } \ No newline at end of file diff --git a/reactive/kotlinx-coroutines-rx2/test/LeakedExceptionTest.kt b/reactive/kotlinx-coroutines-rx2/test/LeakedExceptionTest.kt index 1430dbf381..7252ca2132 100644 --- a/reactive/kotlinx-coroutines-rx2/test/LeakedExceptionTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/LeakedExceptionTest.kt @@ -6,12 +6,12 @@ package kotlinx.coroutines.rx2 import io.reactivex.* import io.reactivex.exceptions.* -import io.reactivex.plugins.* import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import kotlinx.coroutines.reactive.* import org.junit.Test -import java.io.* +import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit import kotlin.test.* // Check that exception is not leaked to the global exception handler @@ -22,37 +22,86 @@ class LeakedExceptionTest : TestBase() { @Test fun testSingle() = withExceptionHandler(handler) { - val flow = rxSingle { throw TestException() }.toFlowable().asFlow() - runBlocking { - repeat(10000) { - combine(flow, flow) { _, _ -> Unit } - .catch {} - .collect { } + withFixedThreadPool(4) { dispatcher -> + val flow = rxSingle(dispatcher) { throw TestException() }.toFlowable().asFlow() + runBlocking { + repeat(10000) { + combine(flow, flow) { _, _ -> Unit } + .catch {} + .collect {} + } } } } @Test fun testObservable() = withExceptionHandler(handler) { - val flow = rxObservable { throw TestException() }.toFlowable(BackpressureStrategy.BUFFER).asFlow() - runBlocking { - repeat(10000) { - combine(flow, flow) { _, _ -> Unit } - .catch {} - .collect { } + withFixedThreadPool(4) { dispatcher -> + val flow = rxObservable(dispatcher) { throw TestException() } + .toFlowable(BackpressureStrategy.BUFFER) + .asFlow() + runBlocking { + repeat(10000) { + combine(flow, flow) { _, _ -> Unit } + .catch {} + .collect {} + } } } } @Test fun testFlowable() = withExceptionHandler(handler) { - val flow = rxFlowable { throw TestException() }.asFlow() - runBlocking { - repeat(10000) { + withFixedThreadPool(4) { dispatcher -> + val flow = rxFlowable(dispatcher) { throw TestException() }.asFlow() + runBlocking { + repeat(10000) { + combine(flow, flow) { _, _ -> Unit } + .catch {} + .collect {} + } + } + } + } + + /** + * This test doesn't test much and was added to display a problem with straighforward use of + * [withExceptionHandler]. + * + * If one was to remove `dispatcher` and launch `rxFlowable` with an empty coroutine context, + * this test would fail fairly often, while other tests were also vulnerable, but the problem is + * much more difficult to reproduce. Thus, this test is a justification for adding `dispatcher` + * to other tests. + * + * See the commit that introduced this test for a better explanation. + */ + @Test + fun testResettingExceptionHandler() = withExceptionHandler(handler) { + withFixedThreadPool(4) { dispatcher -> + val flow = rxFlowable(dispatcher) { + if ((0..1).random() == 0) { + Thread.sleep(100) + } + throw TestException() + }.asFlow() + runBlocking { combine(flow, flow) { _, _ -> Unit } .catch {} - .collect { } + .collect {} } } } + + /** + * Run in a thread pool, then wait for all the tasks to finish. + */ + private fun withFixedThreadPool(numberOfThreads: Int, block: (CoroutineDispatcher) -> Unit) { + val pool = Executors.newFixedThreadPool(numberOfThreads) + val dispatcher = pool.asCoroutineDispatcher() + block(dispatcher) + pool.shutdown() + while (!pool.awaitTermination(10, TimeUnit.SECONDS)) { + /* deliberately empty */ + } + } } diff --git a/reactive/kotlinx-coroutines-rx2/test/MaybeTest.kt b/reactive/kotlinx-coroutines-rx2/test/MaybeTest.kt index deca961e6d..08427dcf3d 100644 --- a/reactive/kotlinx-coroutines-rx2/test/MaybeTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/MaybeTest.kt @@ -10,11 +10,11 @@ import io.reactivex.exceptions.* import io.reactivex.functions.* import io.reactivex.internal.functions.Functions.* import kotlinx.coroutines.* -import org.hamcrest.core.* import org.junit.* -import org.junit.Assert.* +import org.junit.Test import java.util.concurrent.* import java.util.concurrent.CancellationException +import kotlin.test.* class MaybeTest : TestBase() { @Before @@ -32,7 +32,7 @@ class MaybeTest : TestBase() { expect(2) maybe.subscribe { value -> expect(5) - assertThat(value, IsEqual("OK")) + assertEquals("OK", value) } expect(3) yield() // to started coroutine @@ -67,8 +67,8 @@ class MaybeTest : TestBase() { expectUnreached() }, { error -> expect(5) - assertThat(error, IsInstanceOf(RuntimeException::class.java)) - assertThat(error.message, IsEqual("OK")) + assertTrue(error is RuntimeException) + assertEquals("OK", error.message) }) expect(3) yield() // to started coroutine diff --git a/reactive/kotlinx-coroutines-rx2/test/ObservableAsFlowTest.kt b/reactive/kotlinx-coroutines-rx2/test/ObservableAsFlowTest.kt new file mode 100644 index 0000000000..c14c3cc4e8 --- /dev/null +++ b/reactive/kotlinx-coroutines-rx2/test/ObservableAsFlowTest.kt @@ -0,0 +1,185 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.rx2 + +import io.reactivex.Observable +import io.reactivex.ObservableSource +import io.reactivex.Observer +import io.reactivex.disposables.Disposables +import io.reactivex.subjects.PublishSubject +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.* +import kotlinx.coroutines.flow.* +import kotlin.test.* + +class ObservableAsFlowTest : TestBase() { + @Test + fun testCancellation() = runTest { + var onNext = 0 + var onCancelled = 0 + var onError = 0 + + val source = rxObservable(currentDispatcher()) { + coroutineContext[Job]?.invokeOnCompletion { + if (it is CancellationException) ++onCancelled + } + + repeat(100) { + send(it) + } + } + + source.asFlow().launchIn(CoroutineScope(Dispatchers.Unconfined)) { + onEach { + ++onNext + throw RuntimeException() + } + catch { + ++onError + } + }.join() + + + assertEquals(1, onNext) + assertEquals(1, onError) + assertEquals(1, onCancelled) + } + + @Test + fun testImmediateCollection() { + val source = PublishSubject.create() + val flow = source.asFlow() + GlobalScope.launch(Dispatchers.Unconfined) { + expect(1) + flow.collect { expect(it) } + expect(6) + } + expect(2) + source.onNext(3) + expect(4) + source.onNext(5) + source.onComplete() + finish(7) + } + + @Test + fun testOnErrorCancellation() { + val source = PublishSubject.create() + val flow = source.asFlow() + val exception = RuntimeException() + GlobalScope.launch(Dispatchers.Unconfined) { + try { + expect(1) + flow.collect { expect(it) } + expectUnreached() + } + catch (e: Exception) { + assertSame(exception, e.cause) + expect(5) + } + expect(6) + } + expect(2) + source.onNext(3) + expect(4) + source.onError(exception) + finish(7) + } + + @Test + fun testUnsubscribeOnCollectionException() { + val source = PublishSubject.create() + val flow = source.asFlow() + val exception = RuntimeException() + GlobalScope.launch(Dispatchers.Unconfined) { + try { + expect(1) + flow.collect { + expect(it) + if (it == 3) throw exception + } + expectUnreached() + } + catch (e: Exception) { + assertSame(exception, e.cause) + expect(4) + } + expect(5) + } + expect(2) + assertTrue(source.hasObservers()) + source.onNext(3) + assertFalse(source.hasObservers()) + finish(6) + } + + @Test + fun testLateOnSubscribe() { + var observer: Observer? = null + val source = ObservableSource { observer = it } + val flow = source.asFlow() + assertNull(observer) + val job = GlobalScope.launch(Dispatchers.Unconfined) { + expect(1) + flow.collect { expectUnreached() } + expectUnreached() + } + expect(2) + assertNotNull(observer) + job.cancel() + val disposable = Disposables.empty() + observer!!.onSubscribe(disposable) + assertTrue(disposable.isDisposed) + finish(3) + } + + @Test + fun testBufferUnlimited() = runTest { + val source = rxObservable(currentDispatcher()) { + expect(1); send(10) + expect(2); send(11) + expect(3); send(12) + expect(4); send(13) + expect(5); send(14) + expect(6); send(15) + expect(7); send(16) + expect(8); send(17) + expect(9) + } + source.asFlow().buffer(Channel.UNLIMITED).collect { expect(it) } + finish(18) + } + + @Test + fun testConflated() = runTest { + val source = Observable.range(1, 5) + val list = source.asFlow().conflate().toList() + assertEquals(listOf(1, 5), list) + } + + @Test + fun testLongRange() = runTest { + val source = Observable.range(1, 10_000) + val count = source.asFlow().count() + assertEquals(10_000, count) + } + + @Test + fun testProduce() = runTest { + val source = Observable.range(0, 10) + val flow = source.asFlow() + check((0..9).toList(), flow.produceIn(this)) + check((0..9).toList(), flow.buffer(Channel.UNLIMITED).produceIn(this)) + check((0..9).toList(), flow.buffer(2).produceIn(this)) + check((0..9).toList(), flow.buffer(0).produceIn(this)) + check(listOf(0, 9), flow.conflate().produceIn(this)) + } + + private suspend fun check(expected: List, channel: ReceiveChannel) { + val result = ArrayList(10) + channel.consumeEach { result.add(it) } + assertEquals(expected, result) + } +} \ No newline at end of file diff --git a/reactive/kotlinx-coroutines-rx2/test/ObservableMultiTest.kt b/reactive/kotlinx-coroutines-rx2/test/ObservableMultiTest.kt index 6971918723..074fcf4900 100644 --- a/reactive/kotlinx-coroutines-rx2/test/ObservableMultiTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/ObservableMultiTest.kt @@ -6,10 +6,9 @@ package kotlinx.coroutines.rx2 import io.reactivex.* import kotlinx.coroutines.* -import org.junit.* -import org.junit.Assert.* +import org.junit.Test import java.io.* -import kotlin.experimental.* +import kotlin.test.* /** * Test emitting multiple values with [rxObservable]. diff --git a/reactive/kotlinx-coroutines-rx2/test/ObservableSingleTest.kt b/reactive/kotlinx-coroutines-rx2/test/ObservableSingleTest.kt index 7604b4ac26..4454190f8f 100644 --- a/reactive/kotlinx-coroutines-rx2/test/ObservableSingleTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/ObservableSingleTest.kt @@ -7,8 +7,9 @@ package kotlinx.coroutines.rx2 import io.reactivex.* import kotlinx.coroutines.* import org.junit.* -import org.junit.Assert.* +import org.junit.Test import java.util.concurrent.* +import kotlin.test.* class ObservableSingleTest : TestBase() { @Before diff --git a/reactive/kotlinx-coroutines-rx2/test/ObservableSubscriptionSelectTest.kt b/reactive/kotlinx-coroutines-rx2/test/ObservableSubscriptionSelectTest.kt index 28eb8074f9..3cd3bbffff 100644 --- a/reactive/kotlinx-coroutines-rx2/test/ObservableSubscriptionSelectTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/ObservableSubscriptionSelectTest.kt @@ -6,8 +6,8 @@ package kotlinx.coroutines.rx2 import kotlinx.coroutines.* import kotlinx.coroutines.selects.* -import org.junit.* -import org.junit.Assert.* +import org.junit.Test +import kotlin.test.* class ObservableSubscriptionSelectTest : TestBase() { @Test diff --git a/reactive/kotlinx-coroutines-rx2/test/ObservableTest.kt b/reactive/kotlinx-coroutines-rx2/test/ObservableTest.kt index b9f6fe35a6..4f7fa547d2 100644 --- a/reactive/kotlinx-coroutines-rx2/test/ObservableTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/ObservableTest.kt @@ -8,7 +8,6 @@ import io.reactivex.* import io.reactivex.plugins.* import kotlinx.coroutines.* import kotlinx.coroutines.CancellationException -import org.hamcrest.core.* import org.junit.* import org.junit.Test import java.util.concurrent.* @@ -30,7 +29,7 @@ class ObservableTest : TestBase() { expect(2) observable.subscribe { value -> expect(5) - Assert.assertThat(value, IsEqual("OK")) + assertEquals("OK", value) } expect(3) yield() // to started coroutine @@ -49,8 +48,8 @@ class ObservableTest : TestBase() { expectUnreached() }, { error -> expect(5) - Assert.assertThat(error, IsInstanceOf(RuntimeException::class.java)) - Assert.assertThat(error.message, IsEqual("OK")) + assertTrue(error is RuntimeException) + assertEquals("OK", error.message) }) expect(3) yield() // to started coroutine diff --git a/reactive/kotlinx-coroutines-rx2/test/SchedulerTest.kt b/reactive/kotlinx-coroutines-rx2/test/SchedulerTest.kt index ca98b45d48..26dbe8f4cf 100644 --- a/reactive/kotlinx-coroutines-rx2/test/SchedulerTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/SchedulerTest.kt @@ -6,11 +6,9 @@ package kotlinx.coroutines.rx2 import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.* -import org.hamcrest.core.IsEqual -import org.hamcrest.core.IsNot -import org.junit.Assert.assertThat import org.junit.Before import org.junit.Test +import kotlin.test.* class SchedulerTest : TestBase() { @Before @@ -24,11 +22,11 @@ class SchedulerTest : TestBase() { val mainThread = Thread.currentThread() withContext(Schedulers.io().asCoroutineDispatcher()) { val t1 = Thread.currentThread() - assertThat(t1, IsNot(IsEqual(mainThread))) + assertNotSame(t1, mainThread) expect(2) delay(100) val t2 = Thread.currentThread() - assertThat(t2, IsNot(IsEqual(mainThread))) + assertNotSame(t2, mainThread) expect(3) } finish(4) diff --git a/reactive/kotlinx-coroutines-rx2/test/SingleTest.kt b/reactive/kotlinx-coroutines-rx2/test/SingleTest.kt index d9581f86f9..c66188a183 100644 --- a/reactive/kotlinx-coroutines-rx2/test/SingleTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/SingleTest.kt @@ -9,10 +9,10 @@ import io.reactivex.disposables.* import io.reactivex.exceptions.* import io.reactivex.functions.* import kotlinx.coroutines.* -import org.hamcrest.core.* import org.junit.* -import org.junit.Assert.* +import org.junit.Test import java.util.concurrent.* +import kotlin.test.* class SingleTest : TestBase() { @Before @@ -30,7 +30,7 @@ class SingleTest : TestBase() { expect(2) single.subscribe { value -> expect(5) - assertThat(value, IsEqual("OK")) + assertEquals("OK", value) } expect(3) yield() // to started coroutine @@ -49,8 +49,8 @@ class SingleTest : TestBase() { expectUnreached() }, { error -> expect(5) - assertThat(error, IsInstanceOf(RuntimeException::class.java)) - assertThat(error.message, IsEqual("OK")) + assertTrue(error is RuntimeException) + assertEquals("OK", error.message) }) expect(3) yield() // to started coroutine diff --git a/settings.gradle b/settings.gradle index e161cfeada..15d377d3f9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ rootProject.name = 'kotlinx.coroutines' @@ -15,11 +15,8 @@ def module(String path) { // --------------------------- include('benchmarks') -include('knit') include('site') -module('binary-compatibility-validator') - include "kotlinx-coroutines-core" module('kotlinx-coroutines-test') @@ -43,3 +40,5 @@ module('ui/kotlinx-coroutines-swing') module('js/js-stub') module('js/example-frontend-js') + +module('publication-validator') diff --git a/site/README.md b/site/README.md index 8533dbf442..7ffb4103fc 100644 --- a/site/README.md +++ b/site/README.md @@ -2,13 +2,14 @@ This module builds references documentation. -## Building - -* Install [Jekyll](https://jekyllrb.com) -* If you already have Ruby/Jekyll installed you might need to update its version: - * `cd site/docs` - * `bundle install` -* In project root directory do: - * Run `./gradlew site` -* The result is in `site/build/gh-pages/_site` -* Upload it to github pages (`gh-pages` branch) +## 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 b/site/build.gradle index 796fcacaed..6b2c5b7916 100644 --- a/site/build.gradle +++ b/site/build.gradle @@ -1,8 +1,9 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ def buildDocsDir = "$buildDir/docs" +def jekyllDockerImage = "jekyll/jekyll:$jekyll_version" task copyDocs(type: Copy, dependsOn: rootProject.getTasksByName("dokka", true)) { from (rootProject.getTasksByName("dokka", true).collect { "$it.project.buildDir/dokka" }) { @@ -23,7 +24,17 @@ task site(type: Exec, description: 'Generate github pages', dependsOn: [copyDocs inputs.files(fileTree(buildDocsDir)) outputs.dir("$buildDir/dist") workingDir file(buildDocsDir) - commandLine 'bundle', 'exec', 'jekyll', 'build' + commandLine 'docker', 'run', '--rm', "--volume=$buildDir:/srv/jekyll", + '-t', jekyllDockerImage, + '/bin/bash', '-c', 'cd docs; jekyll build' +} + +// A useful task for local debugging -- serves a site locally +task serve(type: Exec, dependsOn: [copyDocs, copyExampleFrontendJs]) { + 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' } task clean(type: Delete) { diff --git a/site/docs/Gemfile b/site/docs/Gemfile deleted file mode 100644 index dcf29c30b1..0000000000 --- a/site/docs/Gemfile +++ /dev/null @@ -1,16 +0,0 @@ -source "https://rubygems.org" -ruby RUBY_VERSION - -# Hello! This is where you manage which Jekyll version is used to run. -# When you want to use a different version, change it below, save the -# file and run `bundle install`. Run Jekyll with `bundle exec`, like so: -# -# bundle exec jekyll serve -# -# This will help ensure the proper Jekyll version is running. -# Happy Jekylling! - -gem "jekyll", "3.6.3" - -# Windows does not include zoneinfo files, so bundle the tzinfo-data gem -gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] diff --git a/site/docs/Gemfile.lock b/site/docs/Gemfile.lock deleted file mode 100644 index f7f42451da..0000000000 --- a/site/docs/Gemfile.lock +++ /dev/null @@ -1,64 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - addressable (2.5.2) - public_suffix (>= 2.0.2, < 4.0) - colorator (1.1.0) - ffi (1.9.25) - ffi (1.9.25-x64-mingw32) - forwardable-extended (2.6.0) - jekyll (3.6.3) - addressable (~> 2.4) - colorator (~> 1.0) - jekyll-sass-converter (~> 1.0) - jekyll-watch (~> 1.1) - kramdown (~> 1.14) - liquid (~> 4.0) - mercenary (~> 0.3.3) - pathutil (~> 0.9) - rouge (>= 1.7, < 3) - safe_yaml (~> 1.0) - jekyll-sass-converter (1.5.2) - sass (~> 3.4) - jekyll-watch (1.5.1) - listen (~> 3.0) - kramdown (1.17.0) - liquid (4.0.1) - listen (3.1.5) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - ruby_dep (~> 1.2) - mercenary (0.3.6) - pathutil (0.16.2) - forwardable-extended (~> 2.6) - public_suffix (3.0.3) - rb-fsevent (0.10.3) - rb-inotify (0.9.10) - ffi (>= 0.5.0, < 2) - rouge (2.2.1) - ruby_dep (1.5.0) - safe_yaml (1.0.4) - sass (3.7.2) - sass-listen (~> 4.0.0) - sass-listen (4.0.0) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - thread_safe (0.3.6) - tzinfo (1.2.4) - thread_safe (~> 0.1) - tzinfo-data (1.2017.3) - tzinfo (>= 1.0.0) - -PLATFORMS - ruby - x64-mingw32 - -DEPENDENCIES - jekyll (= 3.6.3) - tzinfo-data - -RUBY VERSION - ruby 2.3.7p456 - -BUNDLED WITH - 1.16.1 diff --git a/site/docs/_config.yml b/site/docs/_config.yml index 978e0fb2ec..d7617e1ba1 100644 --- a/site/docs/_config.yml +++ b/site/docs/_config.yml @@ -10,8 +10,5 @@ destination: ../dist # Build settings markdown: kramdown -exclude: - - Gemfile - - Gemfile.lock include: - package-list diff --git a/site/docs/assets/js/api.js b/site/docs/assets/js/api.js index 9b1d034736..08f41fa087 100644 --- a/site/docs/assets/js/api.js +++ b/site/docs/assets/js/api.js @@ -1,19 +1,8 @@ /* - * Copyright 2016-2017 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * 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) { diff --git a/stdlib-stubs/build.gradle b/stdlib-stubs/build.gradle index 4d7eee1e2a..b2ca0398d9 100644 --- a/stdlib-stubs/build.gradle +++ b/stdlib-stubs/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ compileKotlin { diff --git a/stdlib-stubs/src/Continuation.kt b/stdlib-stubs/src/Continuation.kt index d5834dacbb..662f9dae8e 100644 --- a/stdlib-stubs/src/Continuation.kt +++ b/stdlib-stubs/src/Continuation.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlin.coroutines @@ -7,4 +7,4 @@ package kotlin.coroutines public interface Continuation { public val context: CoroutineContext public fun resumeWith(result: Result) -} \ No newline at end of file +} diff --git a/stdlib-stubs/src/ContinuationInterceptor.kt b/stdlib-stubs/src/ContinuationInterceptor.kt index 6fbfa70a90..47935e3b03 100644 --- a/stdlib-stubs/src/ContinuationInterceptor.kt +++ b/stdlib-stubs/src/ContinuationInterceptor.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlin.coroutines diff --git a/stdlib-stubs/src/CoroutineContext.kt b/stdlib-stubs/src/CoroutineContext.kt index e26315475b..ac216a0bec 100644 --- a/stdlib-stubs/src/CoroutineContext.kt +++ b/stdlib-stubs/src/CoroutineContext.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlin.coroutines diff --git a/stdlib-stubs/src/Result.kt b/stdlib-stubs/src/Result.kt index 5fe48cb76e..6dc8d9c8f7 100644 --- a/stdlib-stubs/src/Result.kt +++ b/stdlib-stubs/src/Result.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlin diff --git a/ui/coroutines-guide-ui.md b/ui/coroutines-guide-ui.md index fc020cbd86..bfe45f2230 100644 --- a/ui/coroutines-guide-ui.md +++ b/ui/coroutines-guide-ui.md @@ -1,58 +1,3 @@ - - - # Guide to UI programming with coroutines This guide assumes familiarity with basic coroutine concepts that are @@ -100,7 +45,7 @@ context object for your favourite UI library, even if it is not included out of * [Advanced topics](#advanced-topics) * [Starting coroutine in UI event handlers without dispatch](#starting-coroutine-in-ui-event-handlers-without-dispatch) - + ## Setup @@ -165,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.3.3" +implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.4" ``` You can clone [kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) project from GitHub onto your @@ -409,7 +354,6 @@ You cannot do that from the main UI thread nor from the UI-confined coroutine di block the main UI thread and cause the freeze up of the UI. + @@ -20,4 +24,4 @@ - \ No newline at end of file + diff --git a/ui/kotlinx-coroutines-android/animation-app/app/src/main/res/layout/activity_main.xml b/ui/kotlinx-coroutines-android/animation-app/app/src/main/res/layout/activity_main.xml index ad11f2aa61..8e06e90179 100644 --- a/ui/kotlinx-coroutines-android/animation-app/app/src/main/res/layout/activity_main.xml +++ b/ui/kotlinx-coroutines-android/animation-app/app/src/main/res/layout/activity_main.xml @@ -1,6 +1,6 @@ + + - \ No newline at end of file + diff --git a/ui/kotlinx-coroutines-android/animation-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/ui/kotlinx-coroutines-android/animation-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index eca70cfe52..8bc717e4ad 100644 --- a/ui/kotlinx-coroutines-android/animation-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/ui/kotlinx-coroutines-android/animation-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,5 +1,9 @@ + + - \ No newline at end of file + diff --git a/ui/kotlinx-coroutines-android/animation-app/build.gradle b/ui/kotlinx-coroutines-android/animation-app/build.gradle index d198b1ab77..d98ab8cfe7 100644 --- a/ui/kotlinx-coroutines-android/animation-app/build.gradle +++ b/ui/kotlinx-coroutines-android/animation-app/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ // Top-level build file where you can add configuration options common to all sub-projects/modules. diff --git a/ui/kotlinx-coroutines-android/animation-app/gradle.properties b/ui/kotlinx-coroutines-android/animation-app/gradle.properties index c07db5d8f0..e7e3beca2d 100644 --- a/ui/kotlinx-coroutines-android/animation-app/gradle.properties +++ b/ui/kotlinx-coroutines-android/animation-app/gradle.properties @@ -1,5 +1,5 @@ # -# Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. +# Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. # # Project-wide Gradle settings. @@ -20,8 +20,8 @@ org.gradle.jvmargs=-Xmx1536m # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true -kotlin_version=1.3.61 -coroutines_version=1.3.3 +kotlin_version=1.3.70 +coroutines_version=1.3.4 android.useAndroidX=true android.enableJetifier=true diff --git a/ui/kotlinx-coroutines-android/animation-app/gradle/wrapper/gradle-wrapper.properties b/ui/kotlinx-coroutines-android/animation-app/gradle/wrapper/gradle-wrapper.properties index ab5d60b4d9..dfd98a959a 100644 --- a/ui/kotlinx-coroutines-android/animation-app/gradle/wrapper/gradle-wrapper.properties +++ b/ui/kotlinx-coroutines-android/animation-app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ # -# Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. +# Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. # distributionBase=GRADLE_USER_HOME diff --git a/ui/kotlinx-coroutines-android/animation-app/settings.gradle b/ui/kotlinx-coroutines-android/animation-app/settings.gradle index e7b4def49c..0087705ce7 100644 --- a/ui/kotlinx-coroutines-android/animation-app/settings.gradle +++ b/ui/kotlinx-coroutines-android/animation-app/settings.gradle @@ -1 +1,5 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + include ':app' diff --git a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-android.txt b/ui/kotlinx-coroutines-android/api/kotlinx-coroutines-android.api similarity index 100% rename from binary-compatibility-validator/reference-public-api/kotlinx-coroutines-android.txt rename to ui/kotlinx-coroutines-android/api/kotlinx-coroutines-android.api diff --git a/ui/kotlinx-coroutines-android/build.gradle b/ui/kotlinx-coroutines-android/build.gradle index b3075f780e..68e2232cf2 100644 --- a/ui/kotlinx-coroutines-android/build.gradle +++ b/ui/kotlinx-coroutines-android/build.gradle @@ -1,16 +1,9 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ repositories { google() - // TODO Remove once R8 is updated to a 1.6.x version. - maven { - url "http://storage.googleapis.com/r8-releases/raw/master" - metadataSources { - artifact() - } - } } configurations { @@ -18,15 +11,14 @@ configurations { } dependencies { - compileOnly 'com.google.android:android:4.1.1.4' - compileOnly 'com.android.support:support-annotations:26.1.0' + compileOnly "com.google.android:android:$android_version" + compileOnly "com.android.support:support-annotations:$android_support_version" - testImplementation 'com.google.android:android:4.1.1.4' - testImplementation 'org.robolectric:robolectric:4.0-alpha-3' - testImplementation 'org.smali:baksmali:2.2.7' + testImplementation "com.google.android:android:$android_version" + testImplementation "org.robolectric:robolectric:$robolectric_version" + testImplementation "org.smali:baksmali:$baksmali_version" - // TODO Replace with a 1.6.x version once released to maven.google.com. - r8 'com.android.tools:r8:a7ce65837bec81c62261bf0adac73d9c09d32af2' + r8 'com.android.tools.build:builder:4.0.0-alpha06' // Contains r8-2.0.4-dev } class RunR8Task extends JavaExec { @@ -38,7 +30,7 @@ class RunR8Task extends JavaExec { File inputConfig @InputFile - final File inputConfigCommon = new File('r8-test-common.pro') + final File inputConfigCommon = new File('testdata/r8-test-common.pro') @InputFiles final File jarFile = project.jar.archivePath @@ -74,30 +66,36 @@ class RunR8Task extends JavaExec { } } -def optimizedDex = new File(buildDir, "dex-optim/") -def unOptimizedDex = new File(buildDir, "dex-unoptim/") +def optimizedDexDir = new File(buildDir, "dex-optim/") +def unOptimizedDexDir = new File(buildDir, "dex-unoptim/") + +def optimizedDexFile = new File(optimizedDexDir, "classes.dex") +def unOptimizedDexFile = new File(unOptimizedDexDir, "classes.dex") task runR8(type: RunR8Task, dependsOn: 'jar'){ - outputDex = optimizedDex - inputConfig = file('r8-test-rules.pro') + outputDex = optimizedDexDir + inputConfig = file('testdata/r8-test-rules.pro') } task runR8NoOptim(type: RunR8Task, dependsOn: 'jar'){ - outputDex = unOptimizedDex - inputConfig = file('r8-test-rules-no-optim.pro') + outputDex = unOptimizedDexDir + inputConfig = file('testdata/r8-test-rules-no-optim.pro') } test { // Ensure the R8-processed dex is built and supply its path as a property to the test. dependsOn(runR8) dependsOn(runR8NoOptim) - def dex1 = new File(optimizedDex, "classes.dex") - def dex2 = new File(unOptimizedDex, "classes.dex") - inputs.files(dex1, dex2) + inputs.files(optimizedDexFile, unOptimizedDexFile) - systemProperty 'dexPath', dex1.absolutePath - systemProperty 'noOptimDexPath', dex2.absolutePath + 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.withType(dokka.getClass()) { diff --git a/ui/kotlinx-coroutines-android/example-app/app/build.gradle b/ui/kotlinx-coroutines-android/example-app/app/build.gradle index f970baa6d6..3f013247d7 100644 --- a/ui/kotlinx-coroutines-android/example-app/app/build.gradle +++ b/ui/kotlinx-coroutines-android/example-app/app/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ apply plugin: 'com.android.application' diff --git a/ui/kotlinx-coroutines-android/example-app/app/src/main/AndroidManifest.xml b/ui/kotlinx-coroutines-android/example-app/app/src/main/AndroidManifest.xml index a04a27e0a3..b8b2a76c90 100644 --- a/ui/kotlinx-coroutines-android/example-app/app/src/main/AndroidManifest.xml +++ b/ui/kotlinx-coroutines-android/example-app/app/src/main/AndroidManifest.xml @@ -1,4 +1,8 @@ + + @@ -21,4 +25,4 @@ - \ No newline at end of file + diff --git a/ui/kotlinx-coroutines-android/example-app/app/src/main/res/layout/activity_main.xml b/ui/kotlinx-coroutines-android/example-app/app/src/main/res/layout/activity_main.xml index b98ce43711..0b72802215 100644 --- a/ui/kotlinx-coroutines-android/example-app/app/src/main/res/layout/activity_main.xml +++ b/ui/kotlinx-coroutines-android/example-app/app/src/main/res/layout/activity_main.xml @@ -1,6 +1,6 @@ { + AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler +{ + @Volatile + private var _preHandler: Any? = this // uninitialized marker - private val preHandler by lazy(this) - - // Reflectively lookup pre-handler. Implement Function0 to avoid generating second class for lambda - override fun invoke(): Method? = try { - Thread::class.java.getDeclaredMethod("getUncaughtExceptionPreHandler").takeIf { - Modifier.isPublic(it.modifiers) && Modifier.isStatic(it.modifiers) + // Reflectively lookup pre-handler. + private fun preHandler(): Method? { + val current = _preHandler + if (current !== this) return current as Method? + val declared = try { + Thread::class.java.getDeclaredMethod("getUncaughtExceptionPreHandler").takeIf { + Modifier.isPublic(it.modifiers) && Modifier.isStatic(it.modifiers) + } + } catch (e: Throwable) { + null /* not found */ } - } catch (e: Throwable) { - null /* not found */ + _preHandler = declared + return declared } override fun handleException(context: CoroutineContext, exception: Throwable) { @@ -39,7 +46,7 @@ internal class AndroidExceptionPreHandler : if (Build.VERSION.SDK_INT >= 28) { thread.uncaughtExceptionHandler.uncaughtException(thread, exception) } else { - (preHandler?.invoke(null) as? Thread.UncaughtExceptionHandler) + (preHandler()?.invoke(null) as? Thread.UncaughtExceptionHandler) ?.uncaughtException(thread, exception) } } diff --git a/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt b/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt index 8d4cecb0e0..d415b003bf 100644 --- a/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt +++ b/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("unused") diff --git a/ui/kotlinx-coroutines-android/r8-test-common.pro b/ui/kotlinx-coroutines-android/testdata/r8-test-common.pro similarity index 73% rename from ui/kotlinx-coroutines-android/r8-test-common.pro rename to ui/kotlinx-coroutines-android/testdata/r8-test-common.pro index 03f36a82fa..d29377eb40 100644 --- a/ui/kotlinx-coroutines-android/r8-test-common.pro +++ b/ui/kotlinx-coroutines-android/testdata/r8-test-common.pro @@ -8,5 +8,11 @@ void handleCoroutineException(...); } +# Entry point for the rest of coroutines machinery +-keep class kotlinx.coroutines.BuildersKt { + ** runBlocking(...); + ** launch(...); +} + # We are cheating a bit by not having android.jar on R8's library classpath. Ignore those warnings. -ignorewarnings \ No newline at end of file diff --git a/ui/kotlinx-coroutines-android/r8-test-rules-no-optim.pro b/ui/kotlinx-coroutines-android/testdata/r8-test-rules-no-optim.pro similarity index 58% rename from ui/kotlinx-coroutines-android/r8-test-rules-no-optim.pro rename to ui/kotlinx-coroutines-android/testdata/r8-test-rules-no-optim.pro index d6bd4a420b..61afeed2dd 100644 --- a/ui/kotlinx-coroutines-android/r8-test-rules-no-optim.pro +++ b/ui/kotlinx-coroutines-android/testdata/r8-test-rules-no-optim.pro @@ -1,4 +1,4 @@ -include r8-test-common.pro # Include the shrinker config used by legacy versions of AGP and ProGuard --include resources/META-INF/com.android.tools/proguard/coroutines.pro +-include ../resources/META-INF/com.android.tools/proguard/coroutines.pro diff --git a/ui/kotlinx-coroutines-android/testdata/r8-test-rules.pro b/ui/kotlinx-coroutines-android/testdata/r8-test-rules.pro new file mode 100644 index 0000000000..dde8600854 --- /dev/null +++ b/ui/kotlinx-coroutines-android/testdata/r8-test-rules.pro @@ -0,0 +1,14 @@ +-include r8-test-common.pro + +-include ../resources/META-INF/com.android.tools/r8-from-1.6.0/coroutines.pro + +# Validate that service-loader & debugger classes are discarded +-checkdiscard class kotlinx.coroutines.internal.FastServiceLoader +-checkdiscard class kotlinx.coroutines.DebugKt +-checkdiscard class kotlinx.coroutines.internal.StackTraceRecoveryKt + +# Real android projects do not keep this class, but somehow it is kept in this test (R8 bug) +# -checkdiscard class kotlinx.coroutines.internal.MissingMainCoroutineDispatcher + +# Should not keep this class, but it is still there (R8 bug) +#-checkdiscard class kotlinx.coroutines.CoroutineId \ No newline at end of file diff --git a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-javafx.txt b/ui/kotlinx-coroutines-javafx/api/kotlinx-coroutines-javafx.api similarity index 81% rename from binary-compatibility-validator/reference-public-api/kotlinx-coroutines-javafx.txt rename to ui/kotlinx-coroutines-javafx/api/kotlinx-coroutines-javafx.api index 24c5b70b9d..620e904612 100644 --- a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-javafx.txt +++ b/ui/kotlinx-coroutines-javafx/api/kotlinx-coroutines-javafx.api @@ -1,3 +1,7 @@ +public final class kotlinx/coroutines/javafx/JavaFxConvertKt { + public static final fun asFlow (Ljavafx/beans/value/ObservableValue;)Lkotlinx/coroutines/flow/Flow; +} + public abstract class kotlinx/coroutines/javafx/JavaFxDispatcher : kotlinx/coroutines/MainCoroutineDispatcher, kotlinx/coroutines/Delay { public fun delay (JLkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun dispatch (Lkotlin/coroutines/CoroutineContext;Ljava/lang/Runnable;)V diff --git a/ui/kotlinx-coroutines-javafx/build.gradle b/ui/kotlinx-coroutines-javafx/build.gradle index 3b17101f53..9d1c128239 100644 --- a/ui/kotlinx-coroutines-javafx/build.gradle +++ b/ui/kotlinx-coroutines-javafx/build.gradle @@ -1,4 +1,41 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ +static int javaVersionMajor() { + String javaVersion = System.properties["java.version"] + int i = javaVersion.indexOf('.') + return (i < 0 ? javaVersion : javaVersion.substring(0, i)).toInteger() +} + +// JDK11+ does not bundle JavaFx and the plugin for JavaFx support is compiled with class file version 55.0 (JDK 11) +if (javaVersionMajor() >= 11) { + apply plugin: 'org.openjfx.javafxplugin' + + javafx { + version = javafx_version + modules = ['javafx.controls'] + configuration = 'compile' + } +} + +task checkJdk8() { + // only fail w/o JDK_18 when actually trying to test, not during project setup phase + doLast { + if (!System.env.JDK_18) { + throw new GradleException("JDK_18 environment variable is not defined. " + + "Can't run JDK 8 compatibility tests. " + + "Please ensure JDK 8 is installed and that JDK_18 points to it.") + } + } +} + +task jdk8Test(type: Test, dependsOn: [compileTestKotlin, checkJdk8]) { + classpath = files { test.classpath } + testClassesDirs = files { test.testClassesDirs } + executable = "$System.env.JDK_18/bin/java" +} + +// Run these tests only during nightly stress test +jdk8Test.onlyIf { project.properties['stressTest'] != null } +build.dependsOn jdk8Test diff --git a/ui/kotlinx-coroutines-javafx/src/JavaFxConvert.kt b/ui/kotlinx-coroutines-javafx/src/JavaFxConvert.kt new file mode 100644 index 0000000000..903b60a2cf --- /dev/null +++ b/ui/kotlinx-coroutines-javafx/src/JavaFxConvert.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.javafx + +import javafx.beans.value.ChangeListener +import javafx.beans.value.ObservableValue +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.* + +/** + * Creates an instance of a cold [Flow] that subscribes to the given [ObservableValue] and emits + * its values as they change. The resulting flow is conflated, meaning that if several values arrive in quick + * succession, only the last one will be emitted. + * Since this implementation uses [ObservableValue.addListener], even if this [ObservableValue] + * supports lazy evaluation, eager computation will be enforced while the flow is being collected. + * All the calls to JavaFX API are performed in [Dispatchers.JavaFx]. + * This flow emits at least the initial value. + * + * ### Operator fusion + * + * Adjacent applications of [flowOn], [buffer], [conflate], and [produceIn] to the result of `asFlow` are fused. + * [conflate] has no effect, as this flow is already conflated; one can use [buffer] to change that instead. + */ +@ExperimentalCoroutinesApi +public fun ObservableValue.asFlow(): Flow = callbackFlow { + val listener = ChangeListener { _, _, newValue -> + try { + offer(newValue) + } catch (e: CancellationException) { + // In case the event fires after the channel is closed + } + } + addListener(listener) + send(value) + awaitClose { + removeListener(listener) + } +}.flowOn(Dispatchers.JavaFx).conflate() diff --git a/ui/kotlinx-coroutines-javafx/src/JavaFxDispatcher.kt b/ui/kotlinx-coroutines-javafx/src/JavaFxDispatcher.kt index 4d7571c29f..ed74ad6a56 100644 --- a/ui/kotlinx-coroutines-javafx/src/JavaFxDispatcher.kt +++ b/ui/kotlinx-coroutines-javafx/src/JavaFxDispatcher.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.javafx @@ -11,6 +11,7 @@ import javafx.util.* import kotlinx.coroutines.* import kotlinx.coroutines.internal.* import kotlinx.coroutines.javafx.JavaFx.delay +import java.lang.UnsupportedOperationException import java.lang.reflect.* import java.util.concurrent.* import kotlin.coroutines.* @@ -115,6 +116,7 @@ private class PulseTimer : AnimationTimer() { } } +/** @return true if initialized successfully, and false if no display is detected */ internal fun initPlatform(): Boolean = PlatformInitializer.success // Lazily try to initialize JavaFx platform just once @@ -122,34 +124,34 @@ private object PlatformInitializer { val success = run { /* * Try to instantiate JavaFx platform in a way which works - * both on Java 8 and Java 11 and does not produce "illegal reflective access": - * - * 1) Try to invoke javafx.application.Platform.startup if this class is - * present in a classpath. - * 2) If it is not successful and does not because it is already started, - * fallback to PlatformImpl. - * - * Ignore exception anyway in case of unexpected changes in API, in that case - * user will have to instantiate it manually. + * both on Java 8 and Java 11 and does not produce "illegal reflective access". */ - val runnable = Runnable {} - runCatching { - // Invoke public API if it is present - Class.forName("javafx.application.Platform") - .getMethod("startup", java.lang.Runnable::class.java) - .invoke(null, runnable) - }.recoverCatching { exception -> - // Recover -> check re-initialization - val cause = exception.cause - if (exception is InvocationTargetException && cause is IllegalStateException - && "Toolkit already initialized" == cause.message) { - // Toolkit is already initialized -> success, return - Unit - } else { // Fallback to Java 8 API - Class.forName("com.sun.javafx.application.PlatformImpl") + try { + val runnable = Runnable {} + // Invoke the public API if it is present. + runCatching { + Class.forName("javafx.application.Platform") + .getMethod("startup", java.lang.Runnable::class.java) + }.map { method -> + method.invoke(null, runnable) + return@run true + } + // If we are here, it means the public API is not present. Try the private API. + Class.forName("com.sun.javafx.application.PlatformImpl") .getMethod("startup", java.lang.Runnable::class.java) .invoke(null, runnable) + true + } catch (exception: InvocationTargetException) { + // Can only happen as a result of [Method.invoke]. + val cause = exception.cause!! + when { + // Maybe the problem is that JavaFX is already initialized? Everything is good then. + cause is IllegalStateException && "Toolkit already initialized" == cause.message -> true + // If the problem is the headless environment, it is okay. + cause is UnsupportedOperationException && "Unable to open DISPLAY" == cause.message -> false + // Otherwise, the exception demonstrates an anomaly. + else -> throw cause } - }.isSuccess + } } } diff --git a/ui/kotlinx-coroutines-javafx/test/JavaFxTest.kt b/ui/kotlinx-coroutines-javafx/test/JavaFxDispatcherTest.kt similarity index 97% rename from ui/kotlinx-coroutines-javafx/test/JavaFxTest.kt rename to ui/kotlinx-coroutines-javafx/test/JavaFxDispatcherTest.kt index e6a1ddb414..724be6d77b 100644 --- a/ui/kotlinx-coroutines-javafx/test/JavaFxTest.kt +++ b/ui/kotlinx-coroutines-javafx/test/JavaFxDispatcherTest.kt @@ -8,7 +8,7 @@ import javafx.application.* import kotlinx.coroutines.* import org.junit.* -class JavaFxTest : TestBase() { +class JavaFxDispatcherTest : TestBase() { @Before fun setup() { ignoreLostThreads("JavaFX Application Thread", "Thread-", "QuantumRenderer-", "InvokeLaterDispatcher") diff --git a/ui/kotlinx-coroutines-javafx/test/JavaFxObservableAsFlowTest.kt b/ui/kotlinx-coroutines-javafx/test/JavaFxObservableAsFlowTest.kt new file mode 100644 index 0000000000..6964050102 --- /dev/null +++ b/ui/kotlinx-coroutines-javafx/test/JavaFxObservableAsFlowTest.kt @@ -0,0 +1,86 @@ +package kotlinx.coroutines.javafx + +import javafx.beans.property.SimpleIntegerProperty +import kotlinx.coroutines.TestBase +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* +import org.junit.Before +import org.junit.Test +import kotlin.test.* + + +class JavaFxObservableAsFlowTest : TestBase() { + + @Before + fun setup() { + ignoreLostThreads("JavaFX Application Thread", "Thread-", "QuantumRenderer-", "InvokeLaterDispatcher") + } + + @Test + fun testFlowOrder() = runTest { + if (!initPlatform()) { + println("Skipping JavaFxTest in headless environment") + return@runTest // ignore test in headless environments + } + + val integerProperty = SimpleIntegerProperty(0) + val n = 1000 + val flow = integerProperty.asFlow().takeWhile { j -> j != n } + newSingleThreadContext("setter").use { pool -> + launch(pool) { + for (i in 1..n) { + launch(Dispatchers.JavaFx) { + integerProperty.set(i) + } + } + } + var i = -1 + flow.collect { j -> + assertTrue(i < (j as Int), "Elements are neither repeated nor shuffled") + i = j + } + } + } + + @Test + fun testConflation() = runTest { + if (!initPlatform()) { + println("Skipping JavaFxTest in headless environment") + return@runTest // ignore test in headless environments + } + + withContext(Dispatchers.JavaFx) { + val END_MARKER = -1 + val integerProperty = SimpleIntegerProperty(0) + val flow = integerProperty.asFlow().takeWhile { j -> j != END_MARKER } + launch { + yield() // to subscribe to [integerProperty] + yield() // send 0 + integerProperty.set(1) + expect(3) + yield() // send 1 + expect(5) + integerProperty.set(2) + for (i in (-100..-2)) { + integerProperty.set(i) // should be skipped due to conflation + } + integerProperty.set(3) + expect(6) + yield() // send 2 and 3 + integerProperty.set(-1) + } + expect(1) + flow.collect { i -> + when (i) { + 0 -> expect(2) + 1 -> expect(4) + 2 -> expect(7) + 3 -> expect(8) + else -> fail("i is $i") + } + } + finish(9) + } + } + +} diff --git a/ui/kotlinx-coroutines-javafx/test/JavaFxStressTest.kt b/ui/kotlinx-coroutines-javafx/test/JavaFxStressTest.kt new file mode 100644 index 0000000000..5338835d84 --- /dev/null +++ b/ui/kotlinx-coroutines-javafx/test/JavaFxStressTest.kt @@ -0,0 +1,39 @@ +package kotlinx.coroutines.javafx + +import javafx.beans.property.SimpleIntegerProperty +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.first +import org.junit.* + +class JavaFxStressTest : TestBase() { + + @Before + fun setup() { + ignoreLostThreads("JavaFX Application Thread", "Thread-", "QuantumRenderer-", "InvokeLaterDispatcher") + } + + @get:Rule + val pool = ExecutorRule(1) + + @Test + fun testCancellationRace() = runTest { + if (!initPlatform()) { + println("Skipping JavaFxTest in headless environment") + return@runTest // ignore test in headless environments + } + + val integerProperty = SimpleIntegerProperty(0) + val flow = integerProperty.asFlow() + var i = 1 + val n = 1000 * stressTestMultiplier + repeat (n) { + launch(pool) { + flow.first() + } + withContext(Dispatchers.JavaFx) { + integerProperty.set(i) + } + i += 1 + } + } +} \ No newline at end of file diff --git a/ui/kotlinx-coroutines-javafx/test/examples/FxAsFlow.kt b/ui/kotlinx-coroutines-javafx/test/examples/FxAsFlow.kt new file mode 100644 index 0000000000..00003f7860 --- /dev/null +++ b/ui/kotlinx-coroutines-javafx/test/examples/FxAsFlow.kt @@ -0,0 +1,101 @@ +package examples + +import javafx.application.Application +import javafx.scene.Scene +import javafx.scene.control.* +import javafx.scene.layout.GridPane +import javafx.stage.Stage +import javafx.beans.property.SimpleStringProperty +import javafx.event.EventHandler +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.javafx.* +import kotlin.coroutines.CoroutineContext + +fun main(args: Array) { + Application.launch(FxAsFlowApp::class.java, *args) +} + +/** + * Adapted from + * https://github.com/ReactiveX/RxJavaFX/blob/a78ca7d15f7d82d201df8fafb6eba732ec17e327/src/test/java/io/reactivex/rxjavafx/RxJavaFXTest.java + */ +class FxAsFlowApp: Application(), CoroutineScope { + + private var job = Job() + override val coroutineContext: CoroutineContext + get() = JavaFx + job + + private val incrementButton = Button("Increment") + private val incrementLabel = Label("") + private val textInput = TextField() + private val flippedTextLabel = Label() + private val spinner = Spinner() + private val spinnerChangesLabel = Label() + + public override fun start( primaryStage: Stage) { + val gridPane = GridPane() + gridPane.apply { + hgap = 10.0 + vgap = 10.0 + add(incrementButton, 0, 0) + add(incrementLabel, 1, 0) + add(textInput, 0, 1) + add(flippedTextLabel, 1, 1) + add(spinner, 0, 2) + add(spinnerChangesLabel, 1, 2) + } + val scene = Scene(gridPane) + primaryStage.apply { + width = 275.0 + setScene(scene) + show() + } + } + + public override fun stop() { + super.stop() + job.cancel() + job = Job() + } + + init { + // Initializing the "Increment" button + val stringProperty = SimpleStringProperty() + var i = 0 + incrementButton.onAction = EventHandler { + i += 1 + stringProperty.set(i.toString()) + } + launch { + stringProperty.asFlow().collect { + if (it != null) { + stringProperty.set(it) + } + } + } + incrementLabel.textProperty().bind(stringProperty) + // Initializing the reversed text field + val stringProperty2 = SimpleStringProperty() + launch { + textInput.textProperty().asFlow().collect { + if (it != null) { + stringProperty2.set(it.reversed()) + } + } + } + flippedTextLabel.textProperty().bind(stringProperty2) + // Initializing the spinner + spinner.valueFactory = SpinnerValueFactory.IntegerSpinnerValueFactory(0, 100) + spinner.isEditable = true + val stringProperty3 = SimpleStringProperty() + launch { + spinner.valueProperty().asFlow().collect { + if (it != null) { + stringProperty3.set("NEW: $it") + } + } + } + spinnerChangesLabel.textProperty().bind(stringProperty3) + } +} diff --git a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-actor-01.kt b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-actor-01.kt index 58da16da89..d02a77a299 100644 --- a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-actor-01.kt +++ b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-actor-01.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ // This file was automatically generated from coroutines-guide-ui.md by Knit tool. Do not edit. -package kotlinx.coroutines.javafx.guide.actor01 +package kotlinx.coroutines.javafx.guide.exampleUiActor01 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-actor-02.kt b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-actor-02.kt index a7be2f51d0..51e94779e7 100644 --- a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-actor-02.kt +++ b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-actor-02.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ // This file was automatically generated from coroutines-guide-ui.md by Knit tool. Do not edit. -package kotlinx.coroutines.javafx.guide.actor02 +package kotlinx.coroutines.javafx.guide.exampleUiActor02 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-actor-03.kt b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-actor-03.kt index c2926afc70..81371678a9 100644 --- a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-actor-03.kt +++ b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-actor-03.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ // This file was automatically generated from coroutines-guide-ui.md by Knit tool. Do not edit. -package kotlinx.coroutines.javafx.guide.actor03 +package kotlinx.coroutines.javafx.guide.exampleUiActor03 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-advanced-01.kt b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-advanced-01.kt index 2965c04cbc..06e439ef01 100644 --- a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-advanced-01.kt +++ b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-advanced-01.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ // This file was automatically generated from coroutines-guide-ui.md by Knit tool. Do not edit. -package kotlinx.coroutines.javafx.guide.advanced01 +package kotlinx.coroutines.javafx.guide.exampleUiAdvanced01 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-advanced-02.kt b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-advanced-02.kt index fa27d18449..a3f89fcf94 100644 --- a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-advanced-02.kt +++ b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-advanced-02.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ // This file was automatically generated from coroutines-guide-ui.md by Knit tool. Do not edit. -package kotlinx.coroutines.javafx.guide.advanced02 +package kotlinx.coroutines.javafx.guide.exampleUiAdvanced02 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-basic-01.kt b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-basic-01.kt index d7ea599940..f54be59fc9 100644 --- a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-basic-01.kt +++ b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-basic-01.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ // This file was automatically generated from coroutines-guide-ui.md by Knit tool. Do not edit. -package kotlinx.coroutines.javafx.guide.basic01 +package kotlinx.coroutines.javafx.guide.exampleUiBasic01 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-basic-02.kt b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-basic-02.kt index 45967e73a2..42751c4de5 100644 --- a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-basic-02.kt +++ b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-basic-02.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ // This file was automatically generated from coroutines-guide-ui.md by Knit tool. Do not edit. -package kotlinx.coroutines.javafx.guide.basic02 +package kotlinx.coroutines.javafx.guide.exampleUiBasic02 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-basic-03.kt b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-basic-03.kt index 0610660549..dd778cecef 100644 --- a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-basic-03.kt +++ b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-basic-03.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ // This file was automatically generated from coroutines-guide-ui.md by Knit tool. Do not edit. -package kotlinx.coroutines.javafx.guide.basic03 +package kotlinx.coroutines.javafx.guide.exampleUiBasic03 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-blocking-01.kt b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-blocking-01.kt index 2ff5a2fbdb..ea5ac90a4b 100644 --- a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-blocking-01.kt +++ b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-blocking-01.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ // This file was automatically generated from coroutines-guide-ui.md by Knit tool. Do not edit. -package kotlinx.coroutines.javafx.guide.blocking01 +package kotlinx.coroutines.javafx.guide.exampleUiBlocking01 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-blocking-02.kt b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-blocking-02.kt index 6a87025d7b..504f2ee6c7 100644 --- a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-blocking-02.kt +++ b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-blocking-02.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ // This file was automatically generated from coroutines-guide-ui.md by Knit tool. Do not edit. -package kotlinx.coroutines.javafx.guide.blocking02 +package kotlinx.coroutines.javafx.guide.exampleUiBlocking02 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-blocking-03.kt b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-blocking-03.kt index 1388e63517..0e1153673a 100644 --- a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-blocking-03.kt +++ b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-blocking-03.kt @@ -1,9 +1,9 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ // This file was automatically generated from coroutines-guide-ui.md by Knit tool. Do not edit. -package kotlinx.coroutines.javafx.guide.blocking03 +package kotlinx.coroutines.javafx.guide.exampleUiBlocking03 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* diff --git a/binary-compatibility-validator/reference-public-api/kotlinx-coroutines-swing.txt b/ui/kotlinx-coroutines-swing/api/kotlinx-coroutines-swing.api similarity index 100% rename from binary-compatibility-validator/reference-public-api/kotlinx-coroutines-swing.txt rename to ui/kotlinx-coroutines-swing/api/kotlinx-coroutines-swing.api diff --git a/ui/kotlinx-coroutines-swing/build.gradle b/ui/kotlinx-coroutines-swing/build.gradle index 31761abe10..ad8bef0e2f 100644 --- a/ui/kotlinx-coroutines-swing/build.gradle +++ b/ui/kotlinx-coroutines-swing/build.gradle @@ -1,7 +1,7 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ dependencies { testCompile project(':kotlinx-coroutines-jdk8') -} \ No newline at end of file +} diff --git a/ui/kotlinx-coroutines-swing/src/SwingDispatcher.kt b/ui/kotlinx-coroutines-swing/src/SwingDispatcher.kt index 81176da332..3fad55f314 100644 --- a/ui/kotlinx-coroutines-swing/src/SwingDispatcher.kt +++ b/ui/kotlinx-coroutines-swing/src/SwingDispatcher.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.swing diff --git a/ui/kotlinx-coroutines-swing/test/SwingTest.kt b/ui/kotlinx-coroutines-swing/test/SwingTest.kt index 8b41b494cc..cbed5bf1e9 100644 --- a/ui/kotlinx-coroutines-swing/test/SwingTest.kt +++ b/ui/kotlinx-coroutines-swing/test/SwingTest.kt @@ -1,10 +1,9 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.swing -import javafx.application.* import kotlinx.coroutines.* import org.junit.* import org.junit.Test @@ -81,7 +80,7 @@ class SwingTest : TestBase() { join(component) } - private suspend fun join(component: SwingTest.SwingComponent) { + private suspend fun join(component: SwingComponent) { component.coroutineContext[Job]!!.join() }