aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoman Elizarov <elizarov@gmail.com>2019-08-22 20:08:48 +0300
committerVsevolod Tolstopyatov <qwwdfsad@gmail.com>2019-08-22 20:08:48 +0300
commit3258e1f81e2b18cee439e80dc95877a04ff24402 (patch)
tree93719e7d5f43637bf2848f2e7f643abe0efbc8c6
parent3807a74e34348b42eef13175113e71d26b9cc515 (diff)
downloadkotlinx.coroutines-3258e1f81e2b18cee439e80dc95877a04ff24402.tar.gz
Flow docs (#1432)
* Flow guide by example * Consistent chapter naming and ordering in docs * Make "supervision" a subsection of "Exception Handling" chapter * Knit: filter out sampleStart/End from Knit-generated source files * Knit: Support example auto-numbering
-rw-r--r--coroutines-guide.md69
-rw-r--r--docs/_nav.yml17
-rw-r--r--docs/basics.md4
-rw-r--r--docs/cancellation-and-timeouts.md4
-rw-r--r--docs/composing-suspending-functions.md4
-rw-r--r--docs/coroutine-context-and-dispatchers.md4
-rw-r--r--docs/coroutines-guide.md17
-rw-r--r--docs/exception-handling.md20
-rw-r--r--docs/flow.md1855
-rw-r--r--docs/select-expression.md4
-rw-r--r--knit/src/Knit.kt49
-rw-r--r--kotlinx-coroutines-core/common/src/flow/internal/SafeCollector.kt13
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-basic-03.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-basic-07.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-cancel-01.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-cancel-02.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-cancel-03.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-cancel-04.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-cancel-05.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-cancel-06.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-cancel-07.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-channel-01.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-channel-02.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-channel-03.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-channel-04.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-channel-05.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-channel-06.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-channel-07.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-channel-08.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-channel-09.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-compose-01.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-compose-02.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-compose-03.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-compose-04.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-compose-05.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-context-01.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-context-02.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-context-03.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-context-04.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-context-05.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-context-06.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-context-07.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-context-08.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-context-09.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-context-10.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-context-11.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-exceptions-02.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-exceptions-03.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-exceptions-04.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-exceptions-06.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-01.kt12
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-02.kt17
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-03.kt17
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-04.kt28
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-05.kt26
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-06.kt24
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-07.kt14
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-08.kt20
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-09.kt23
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-10.kt26
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-11.kt16
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-12.kt23
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-13.kt22
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-14.kt23
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-15.kt25
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-16.kt27
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-17.kt29
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-18.kt29
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-19.kt29
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-20.kt16
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-21.kt19
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-22.kt19
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-23.kt24
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-24.kt24
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-25.kt24
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-26.kt27
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-27.kt29
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-28.kt27
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-29.kt25
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-30.kt26
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-31.kt19
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-32.kt17
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-33.kt21
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-34.kt20
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-35.kt19
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-flow-36.kt19
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-select-01.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-select-02.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-select-03.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-select-04.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-select-05.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-sync-01.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-sync-02.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-sync-03.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-sync-04.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-sync-05.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-sync-06.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/example-sync-07.kt2
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/test/FlowGuideTest.kt381
-rw-r--r--kotlinx-coroutines-core/jvm/test/guide/test/TestUtil.kt14
100 files changed, 3199 insertions, 161 deletions
diff --git a/coroutines-guide.md b/coroutines-guide.md
index 62fa2c17..9267efd1 100644
--- a/coroutines-guide.md
+++ b/coroutines-guide.md
@@ -3,7 +3,7 @@ The main coroutines guide has moved to the [docs folder](docs/coroutines-guide.m
## Table of contents
<!--- TOC_REF docs/basics.md -->
-* <a name='coroutine-basics'></a>[Coroutine basics](docs/basics.md#coroutine-basics)
+* <a name='coroutine-basics'></a>[Coroutine Basics](docs/basics.md#coroutine-basics)
* <a name='your-first-coroutine'></a>[Your first coroutine](docs/basics.md#your-first-coroutine)
* <a name='bridging-blocking-and-non-blocking-worlds'></a>[Bridging blocking and non-blocking worlds](docs/basics.md#bridging-blocking-and-non-blocking-worlds)
* <a name='waiting-for-a-job'></a>[Waiting for a job](docs/basics.md#waiting-for-a-job)
@@ -13,7 +13,7 @@ The main coroutines guide has moved to the [docs folder](docs/coroutines-guide.m
* <a name='coroutines-are-light-weight'></a>[Coroutines ARE light-weight](docs/basics.md#coroutines-are-light-weight)
* <a name='global-coroutines-are-like-daemon-threads'></a>[Global coroutines are like daemon threads](docs/basics.md#global-coroutines-are-like-daemon-threads)
<!--- TOC_REF docs/cancellation-and-timeouts.md -->
-* <a name='cancellation-and-timeouts'></a>[Cancellation and timeouts](docs/cancellation-and-timeouts.md#cancellation-and-timeouts)
+* <a name='cancellation-and-timeouts'></a>[Cancellation and Timeouts](docs/cancellation-and-timeouts.md#cancellation-and-timeouts)
* <a name='cancelling-coroutine-execution'></a>[Cancelling coroutine execution](docs/cancellation-and-timeouts.md#cancelling-coroutine-execution)
* <a name='cancellation-is-cooperative'></a>[Cancellation is cooperative](docs/cancellation-and-timeouts.md#cancellation-is-cooperative)
* <a name='making-computation-code-cancellable'></a>[Making computation code cancellable](docs/cancellation-and-timeouts.md#making-computation-code-cancellable)
@@ -21,14 +21,14 @@ The main coroutines guide has moved to the [docs folder](docs/coroutines-guide.m
* <a name='run-non-cancellable-block'></a>[Run non-cancellable block](docs/cancellation-and-timeouts.md#run-non-cancellable-block)
* <a name='timeout'></a>[Timeout](docs/cancellation-and-timeouts.md#timeout)
<!--- TOC_REF docs/composing-suspending-functions.md -->
-* <a name='composing-suspending-functions'></a>[Composing suspending functions](docs/composing-suspending-functions.md#composing-suspending-functions)
+* <a name='composing-suspending-functions'></a>[Composing Suspending Functions](docs/composing-suspending-functions.md#composing-suspending-functions)
* <a name='sequential-by-default'></a>[Sequential by default](docs/composing-suspending-functions.md#sequential-by-default)
* <a name='concurrent-using-async'></a>[Concurrent using async](docs/composing-suspending-functions.md#concurrent-using-async)
* <a name='lazily-started-async'></a>[Lazily started async](docs/composing-suspending-functions.md#lazily-started-async)
* <a name='async-style-functions'></a>[Async-style functions](docs/composing-suspending-functions.md#async-style-functions)
* <a name='structured-concurrency-with-async'></a>[Structured concurrency with async](docs/composing-suspending-functions.md#structured-concurrency-with-async)
<!--- TOC_REF docs/coroutine-context-and-dispatchers.md -->
-* <a name='coroutine-context-and-dispatchers'></a>[Coroutine context and dispatchers](docs/coroutine-context-and-dispatchers.md#coroutine-context-and-dispatchers)
+* <a name='coroutine-context-and-dispatchers'></a>[Coroutine Context and Dispatchers](docs/coroutine-context-and-dispatchers.md#coroutine-context-and-dispatchers)
* <a name='dispatchers-and-threads'></a>[Dispatchers and threads](docs/coroutine-context-and-dispatchers.md#dispatchers-and-threads)
* <a name='unconfined-vs-confined-dispatcher'></a>[Unconfined vs confined dispatcher](docs/coroutine-context-and-dispatchers.md#unconfined-vs-confined-dispatcher)
* <a name='debugging-coroutines-and-threads'></a>[Debugging coroutines and threads](docs/coroutine-context-and-dispatchers.md#debugging-coroutines-and-threads)
@@ -40,16 +40,45 @@ The main coroutines guide has moved to the [docs folder](docs/coroutines-guide.m
* <a name='combining-context-elements'></a>[Combining context elements](docs/coroutine-context-and-dispatchers.md#combining-context-elements)
* <a name='coroutine-scope'></a>[Coroutine scope](docs/coroutine-context-and-dispatchers.md#coroutine-scope)
* <a name='thread-local-data'></a>[Thread-local data](docs/coroutine-context-and-dispatchers.md#thread-local-data)
-<!--- TOC_REF docs/exception-handling.md -->
-* <a name='exception-handling'></a>[Exception handling](docs/exception-handling.md#exception-handling)
- * <a name='exception-propagation'></a>[Exception propagation](docs/exception-handling.md#exception-propagation)
- * <a name='coroutineexceptionhandler'></a>[CoroutineExceptionHandler](docs/exception-handling.md#coroutineexceptionhandler)
- * <a name='cancellation-and-exceptions'></a>[Cancellation and exceptions](docs/exception-handling.md#cancellation-and-exceptions)
- * <a name='exceptions-aggregation'></a>[Exceptions aggregation](docs/exception-handling.md#exceptions-aggregation)
-* <a name='supervision'></a>[Supervision](docs/exception-handling.md#supervision)
- * <a name='supervision-job'></a>[Supervision job](docs/exception-handling.md#supervision-job)
- * <a name='supervision-scope'></a>[Supervision scope](docs/exception-handling.md#supervision-scope)
- * <a name='exceptions-in-supervised-coroutines'></a>[Exceptions in supervised coroutines](docs/exception-handling.md#exceptions-in-supervised-coroutines)
+<!--- TOC_REF docs/flow.md -->
+* <a name='asynchronous-flow'></a>[Asynchronous Flow](docs/flow.md#asynchronous-flow)
+ * <a name='representing-multiple-values'></a>[Representing multiple values](docs/flow.md#representing-multiple-values)
+ * <a name='sequences'></a>[Sequences](docs/flow.md#sequences)
+ * <a name='suspending-functions'></a>[Suspending functions](docs/flow.md#suspending-functions)
+ * <a name='flows'></a>[Flows](docs/flow.md#flows)
+ * <a name='flows-are-cold'></a>[Flows are cold](docs/flow.md#flows-are-cold)
+ * <a name='flow-cancellation'></a>[Flow cancellation](docs/flow.md#flow-cancellation)
+ * <a name='flow-builders'></a>[Flow builders](docs/flow.md#flow-builders)
+ * <a name='intermediate-flow-operators'></a>[Intermediate flow operators](docs/flow.md#intermediate-flow-operators)
+ * <a name='transform-operator'></a>[Transform operator](docs/flow.md#transform-operator)
+ * <a name='size-limiting-operators'></a>[Size-limiting operators](docs/flow.md#size-limiting-operators)
+ * <a name='terminal-flow-operators'></a>[Terminal flow operators](docs/flow.md#terminal-flow-operators)
+ * <a name='flows-are-sequential'></a>[Flows are sequential](docs/flow.md#flows-are-sequential)
+ * <a name='flow-context'></a>[Flow context](docs/flow.md#flow-context)
+ * <a name='wrong-emission-withcontext'></a>[Wrong emission withContext](docs/flow.md#wrong-emission-withcontext)
+ * <a name='flowon-operator'></a>[flowOn operator](docs/flow.md#flowon-operator)
+ * <a name='buffering'></a>[Buffering](docs/flow.md#buffering)
+ * <a name='conflation'></a>[Conflation](docs/flow.md#conflation)
+ * <a name='processing-the-latest-value'></a>[Processing the latest value](docs/flow.md#processing-the-latest-value)
+ * <a name='composing-multiple-flows'></a>[Composing multiple flows](docs/flow.md#composing-multiple-flows)
+ * <a name='zip'></a>[Zip](docs/flow.md#zip)
+ * <a name='combine'></a>[Combine](docs/flow.md#combine)
+ * <a name='flattening-flows'></a>[Flattening flows](docs/flow.md#flattening-flows)
+ * <a name='flatmapconcat'></a>[flatMapConcat](docs/flow.md#flatmapconcat)
+ * <a name='flatmapmerge'></a>[flatMapMerge](docs/flow.md#flatmapmerge)
+ * <a name='flatmaplatest'></a>[flatMapLatest](docs/flow.md#flatmaplatest)
+ * <a name='flow-exceptions'></a>[Flow exceptions](docs/flow.md#flow-exceptions)
+ * <a name='collector-try-and-catch'></a>[Collector try and catch](docs/flow.md#collector-try-and-catch)
+ * <a name='everything-is-caught'></a>[Everything is caught](docs/flow.md#everything-is-caught)
+ * <a name='exception-transparency'></a>[Exception transparency](docs/flow.md#exception-transparency)
+ * <a name='transparent-catch'></a>[Transparent catch](docs/flow.md#transparent-catch)
+ * <a name='catching-declaratively'></a>[Catching declaratively](docs/flow.md#catching-declaratively)
+ * <a name='flow-completion'></a>[Flow completion](docs/flow.md#flow-completion)
+ * <a name='imperative-finally-block'></a>[Imperative finally block](docs/flow.md#imperative-finally-block)
+ * <a name='declarative-handling'></a>[Declarative handling](docs/flow.md#declarative-handling)
+ * <a name='upstream-exceptions-only'></a>[Upstream exceptions only](docs/flow.md#upstream-exceptions-only)
+ * <a name='imperative-versus-declarative'></a>[Imperative versus declarative](docs/flow.md#imperative-versus-declarative)
+ * <a name='launching-flow'></a>[Launching flow](docs/flow.md#launching-flow)
<!--- TOC_REF docs/channels.md -->
* <a name='channels'></a>[Channels](docs/channels.md#channels)
* <a name='channel-basics'></a>[Channel basics](docs/channels.md#channel-basics)
@@ -62,6 +91,16 @@ The main coroutines guide has moved to the [docs folder](docs/coroutines-guide.m
* <a name='buffered-channels'></a>[Buffered channels](docs/channels.md#buffered-channels)
* <a name='channels-are-fair'></a>[Channels are fair](docs/channels.md#channels-are-fair)
* <a name='ticker-channels'></a>[Ticker channels](docs/channels.md#ticker-channels)
+<!--- TOC_REF docs/exception-handling.md -->
+* <a name='exception-handling'></a>[Exception Handling](docs/exception-handling.md#exception-handling)
+ * <a name='exception-propagation'></a>[Exception propagation](docs/exception-handling.md#exception-propagation)
+ * <a name='coroutineexceptionhandler'></a>[CoroutineExceptionHandler](docs/exception-handling.md#coroutineexceptionhandler)
+ * <a name='cancellation-and-exceptions'></a>[Cancellation and exceptions](docs/exception-handling.md#cancellation-and-exceptions)
+ * <a name='exceptions-aggregation'></a>[Exceptions aggregation](docs/exception-handling.md#exceptions-aggregation)
+ * <a name='supervision'></a>[Supervision](docs/exception-handling.md#supervision)
+ * <a name='supervision-job'></a>[Supervision job](docs/exception-handling.md#supervision-job)
+ * <a name='supervision-scope'></a>[Supervision scope](docs/exception-handling.md#supervision-scope)
+ * <a name='exceptions-in-supervised-coroutines'></a>[Exceptions in supervised coroutines](docs/exception-handling.md#exceptions-in-supervised-coroutines)
<!--- TOC_REF docs/shared-mutable-state-and-concurrency.md -->
* <a name='shared-mutable-state-and-concurrency'></a>[Shared mutable state and concurrency](docs/shared-mutable-state-and-concurrency.md#shared-mutable-state-and-concurrency)
* <a name='the-problem'></a>[The problem](docs/shared-mutable-state-and-concurrency.md#the-problem)
@@ -72,7 +111,7 @@ The main coroutines guide has moved to the [docs folder](docs/coroutines-guide.m
* <a name='mutual-exclusion'></a>[Mutual exclusion](docs/shared-mutable-state-and-concurrency.md#mutual-exclusion)
* <a name='actors'></a>[Actors](docs/shared-mutable-state-and-concurrency.md#actors)
<!--- TOC_REF docs/select-expression.md -->
-* <a name='select-expression-experimental'></a>[Select expression (experimental)](docs/select-expression.md#select-expression-experimental)
+* <a name='select-expression-experimental'></a>[Select Expression (experimental)](docs/select-expression.md#select-expression-experimental)
* <a name='selecting-from-channels'></a>[Selecting from channels](docs/select-expression.md#selecting-from-channels)
* <a name='selecting-on-close'></a>[Selecting on close](docs/select-expression.md#selecting-on-close)
* <a name='selecting-to-send'></a>[Selecting to send](docs/select-expression.md#selecting-to-send)
diff --git a/docs/_nav.yml b/docs/_nav.yml
index 3a7dbfc4..79a69690 100644
--- a/docs/_nav.yml
+++ b/docs/_nav.yml
@@ -7,21 +7,24 @@
- md: cancellation-and-timeouts.md
url: cancellation-and-timeouts.html
title: Cancellation and Timeouts
-- md: channels.md
- url: channels.html
- title: Channels
- md: composing-suspending-functions.md
url: composing-suspending-functions.html
title: Composing Suspending Functions
- md: coroutine-context-and-dispatchers.md
url: coroutine-context-and-dispatchers.html
title: Coroutine Context and Dispatchers
+- md: flow.md
+ url: flow.html
+ title: Asynchronous Flow
+- md: channels.md
+ url: channels.html
+ title: Channels
- md: exception-handling.md
url: exception-handling.html
- title: Exception Handling
-- md: select-expression.md
- url: select-expression.html
- title: Select Expression
+ title: Exception Handling and Supervision
- md: shared-mutable-state-and-concurrency.md
url: shared-mutable-state-and-concurrency.html
title: Shared Mutable State and Concurrency
+- md: select-expression.md
+ url: select-expression.html
+ title: Select Expression (experimental)
diff --git a/docs/basics.md b/docs/basics.md
index a7321bb2..6a1248b5 100644
--- a/docs/basics.md
+++ b/docs/basics.md
@@ -20,7 +20,7 @@ class BasicsGuideTest {
<!--- TOC -->
-* [Coroutine basics](#coroutine-basics)
+* [Coroutine Basics](#coroutine-basics)
* [Your first coroutine](#your-first-coroutine)
* [Bridging blocking and non-blocking worlds](#bridging-blocking-and-non-blocking-worlds)
* [Waiting for a job](#waiting-for-a-job)
@@ -33,7 +33,7 @@ class BasicsGuideTest {
<!--- END_TOC -->
-## Coroutine basics
+## Coroutine Basics
This section covers basic coroutine concepts.
diff --git a/docs/cancellation-and-timeouts.md b/docs/cancellation-and-timeouts.md
index afb8da97..ef4a9c9e 100644
--- a/docs/cancellation-and-timeouts.md
+++ b/docs/cancellation-and-timeouts.md
@@ -19,7 +19,7 @@ class CancellationTimeOutsGuideTest {
<!--- TOC -->
-* [Cancellation and timeouts](#cancellation-and-timeouts)
+* [Cancellation and Timeouts](#cancellation-and-timeouts)
* [Cancelling coroutine execution](#cancelling-coroutine-execution)
* [Cancellation is cooperative](#cancellation-is-cooperative)
* [Making computation code cancellable](#making-computation-code-cancellable)
@@ -29,7 +29,7 @@ class CancellationTimeOutsGuideTest {
<!--- END_TOC -->
-## Cancellation and timeouts
+## Cancellation and Timeouts
This section covers coroutine cancellation and timeouts.
diff --git a/docs/composing-suspending-functions.md b/docs/composing-suspending-functions.md
index a910f49c..0cd02762 100644
--- a/docs/composing-suspending-functions.md
+++ b/docs/composing-suspending-functions.md
@@ -20,7 +20,7 @@ class ComposingGuideTest {
<!--- TOC -->
-* [Composing suspending functions](#composing-suspending-functions)
+* [Composing Suspending Functions](#composing-suspending-functions)
* [Sequential by default](#sequential-by-default)
* [Concurrent using async](#concurrent-using-async)
* [Lazily started async](#lazily-started-async)
@@ -29,7 +29,7 @@ class ComposingGuideTest {
<!--- END_TOC -->
-## Composing suspending functions
+## Composing Suspending Functions
This section covers various approaches to composition of suspending functions.
diff --git a/docs/coroutine-context-and-dispatchers.md b/docs/coroutine-context-and-dispatchers.md
index 4769c1e2..558b0397 100644
--- a/docs/coroutine-context-and-dispatchers.md
+++ b/docs/coroutine-context-and-dispatchers.md
@@ -20,7 +20,7 @@ class DispatchersGuideTest {
<!--- TOC -->
-* [Coroutine context and dispatchers](#coroutine-context-and-dispatchers)
+* [Coroutine Context and Dispatchers](#coroutine-context-and-dispatchers)
* [Dispatchers and threads](#dispatchers-and-threads)
* [Unconfined vs confined dispatcher](#unconfined-vs-confined-dispatcher)
* [Debugging coroutines and threads](#debugging-coroutines-and-threads)
@@ -35,7 +35,7 @@ class DispatchersGuideTest {
<!--- END_TOC -->
-## Coroutine context and dispatchers
+## Coroutine Context and Dispatchers
Coroutines always execute in some context represented by a value of the
[CoroutineContext](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines/-coroutine-context/)
diff --git a/docs/coroutines-guide.md b/docs/coroutines-guide.md
index 3d0ca535..5f41d060 100644
--- a/docs/coroutines-guide.md
+++ b/docs/coroutines-guide.md
@@ -15,14 +15,15 @@ In order to use coroutines as well as follow the examples in this guide, you nee
## Table of contents
-* [Coroutine basics](basics.md)
-* [Cancellation and timeouts](cancellation-and-timeouts.md)
-* [Composing suspending functions](composing-suspending-functions.md)
-* [Coroutine context and dispatchers](coroutine-context-and-dispatchers.md)
-* [Exception handling and supervision](exception-handling.md)
-* [Channels (experimental)](channels.md)
-* [Shared mutable state and concurrency](shared-mutable-state-and-concurrency.md)
-* [Select expression (experimental)](select-expression.md)
+* [Basics](basics.md)
+* [Cancellation and Timeouts](cancellation-and-timeouts.md)
+* [Composing Suspending Functions](composing-suspending-functions.md)
+* [Coroutine Context and Dispatchers](coroutine-context-and-dispatchers.md)
+* [Asynchronous Flow](flow.md)
+* [Channels](channels.md)
+* [Exception Handling and Supervision](exception-handling.md)
+* [Shared Mutable State and Concurrency](shared-mutable-state-and-concurrency.md)
+* [Select Expression (experimental)](select-expression.md)
## Additional references
diff --git a/docs/exception-handling.md b/docs/exception-handling.md
index 349b703e..409acd53 100644
--- a/docs/exception-handling.md
+++ b/docs/exception-handling.md
@@ -19,19 +19,19 @@ class ExceptionsGuideTest {
<!--- TOC -->
-* [Exception handling](#exception-handling)
+* [Exception Handling](#exception-handling)
* [Exception propagation](#exception-propagation)
* [CoroutineExceptionHandler](#coroutineexceptionhandler)
* [Cancellation and exceptions](#cancellation-and-exceptions)
* [Exceptions aggregation](#exceptions-aggregation)
-* [Supervision](#supervision)
- * [Supervision job](#supervision-job)
- * [Supervision scope](#supervision-scope)
- * [Exceptions in supervised coroutines](#exceptions-in-supervised-coroutines)
+ * [Supervision](#supervision)
+ * [Supervision job](#supervision-job)
+ * [Supervision scope](#supervision-scope)
+ * [Exceptions in supervised coroutines](#exceptions-in-supervised-coroutines)
<!--- END_TOC -->
-## Exception handling
+## Exception Handling
This section covers exception handling and cancellation on exceptions.
@@ -355,7 +355,7 @@ Caught original java.io.IOException
```
<!--- TEST-->
-## Supervision
+### Supervision
As we have studied before, cancellation is a bidirectional relationship propagating through the whole
coroutines hierarchy. But what if unidirectional cancellation is required?
@@ -367,7 +367,7 @@ but if UI component is destroyed (and its job is cancelled), then it is necessar
Another example is a server process that spawns several children jobs and needs to _supervise_
their execution, tracking their failures and restarting just those children jobs that had failed.
-### Supervision job
+#### Supervision job
For these purposes [SupervisorJob][SupervisorJob()] can be used. It is similar to a regular [Job][Job()] with the only exception that cancellation is propagated
only downwards. It is easy to demonstrate with an example:
@@ -421,7 +421,7 @@ Second child is cancelled because supervisor is cancelled
<!--- TEST-->
-### Supervision scope
+#### Supervision scope
For *scoped* concurrency [supervisorScope] can be used instead of [coroutineScope] for the same purpose. It propagates cancellation
only in one direction and cancels all children only if it has failed itself. It also waits for all children before completion
@@ -469,7 +469,7 @@ Caught assertion error
```
<!--- TEST-->
-### Exceptions in supervised coroutines
+#### Exceptions in supervised coroutines
Another crucial difference between regular and supervisor jobs is exception handling.
Every child should handle its exceptions by itself via exception handling mechanisms.
diff --git a/docs/flow.md b/docs/flow.md
new file mode 100644
index 00000000..134d808b
--- /dev/null
+++ b/docs/flow.md
@@ -0,0 +1,1855 @@
+<!--- INCLUDE .*/example-([a-z]+)-([0-9a-z]+)\.kt
+/*
+ * 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.$$1$$2
+-->
+<!--- KNIT ../kotlinx-coroutines-core/jvm/test/guide/.*-##\.kt -->
+<!--- TEST_OUT ../kotlinx-coroutines-core/jvm/test/guide/test/FlowGuideTest.kt
+// 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 {
+-->
+
+**Table of contents**
+
+<!--- TOC -->
+
+* [Asynchronous Flow](#asynchronous-flow)
+ * [Representing multiple values](#representing-multiple-values)
+ * [Sequences](#sequences)
+ * [Suspending functions](#suspending-functions)
+ * [Flows](#flows)
+ * [Flows are cold](#flows-are-cold)
+ * [Flow cancellation](#flow-cancellation)
+ * [Flow builders](#flow-builders)
+ * [Intermediate flow operators](#intermediate-flow-operators)
+ * [Transform operator](#transform-operator)
+ * [Size-limiting operators](#size-limiting-operators)
+ * [Terminal flow operators](#terminal-flow-operators)
+ * [Flows are sequential](#flows-are-sequential)
+ * [Flow context](#flow-context)
+ * [Wrong emission withContext](#wrong-emission-withcontext)
+ * [flowOn operator](#flowon-operator)
+ * [Buffering](#buffering)
+ * [Conflation](#conflation)
+ * [Processing the latest value](#processing-the-latest-value)
+ * [Composing multiple flows](#composing-multiple-flows)
+ * [Zip](#zip)
+ * [Combine](#combine)
+ * [Flattening flows](#flattening-flows)
+ * [flatMapConcat](#flatmapconcat)
+ * [flatMapMerge](#flatmapmerge)
+ * [flatMapLatest](#flatmaplatest)
+ * [Flow exceptions](#flow-exceptions)
+ * [Collector try and catch](#collector-try-and-catch)
+ * [Everything is caught](#everything-is-caught)
+ * [Exception transparency](#exception-transparency)
+ * [Transparent catch](#transparent-catch)
+ * [Catching declaratively](#catching-declaratively)
+ * [Flow completion](#flow-completion)
+ * [Imperative finally block](#imperative-finally-block)
+ * [Declarative handling](#declarative-handling)
+ * [Upstream exceptions only](#upstream-exceptions-only)
+ * [Imperative versus declarative](#imperative-versus-declarative)
+ * [Launching flow](#launching-flow)
+
+<!--- END_TOC -->
+
+## Asynchronous Flow
+
+Suspending functions asynchronously return a single value, but how can you return
+multiple asynchronously computed values? That is what Kotlin Flows are for.
+
+### Representing multiple values
+
+Multiple values can be represented in Kotlin using [collections].
+For example, we can have a function `foo()` that returns a [List]
+of three numbers and print them all using [forEach]:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+fun foo(): List<Int> = listOf(1, 2, 3)
+
+fun main() {
+ foo().forEach { value -> println(value) }
+}
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-01.kt).
+
+This code outputs:
+
+```text
+1
+2
+3
+```
+
+<!--- TEST -->
+
+#### Sequences
+
+If the numbers are computed with some CPU-consuming blocking code
+(each computation taking 100ms) then we can represent the numbers using a [Sequence]:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+fun foo(): Sequence<Int> = sequence { // sequence builder
+ for (i in 1..3) {
+ Thread.sleep(100) // pretend we are computing it
+ yield(i) // yield next value
+ }
+}
+
+fun main() {
+ foo().forEach { value -> println(value) }
+}
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-02.kt).
+
+This code outputs the same numbers, but it waits 100ms before printing each one.
+
+<!--- TEST
+1
+2
+3
+-->
+
+#### Suspending functions
+
+However, this computation blocks the main thread that is running the code.
+When those values are computed by an asynchronous code we can mark function `foo` with a `suspend` modifier,
+so that it can perform its work without blocking and return the result as a list:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+
+//sampleStart
+suspend fun foo(): List<Int> {
+ delay(1000) // pretend we are doing something asynchronous here
+ return listOf(1, 2, 3)
+}
+
+fun main() = runBlocking<Unit> {
+ foo().forEach { value -> println(value) }
+}
+//sampleEnd
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-03.kt).
+
+This code prints the numbers after waiting for a second.
+
+<!--- TEST
+1
+2
+3
+-->
+
+#### Flows
+
+Using `List<Int>` result type we can only return all the values at once. To represent
+the stream of values that are being asynchronously computed we can use [`Flow<Int>`][Flow] type similarly
+to the `Sequence<Int>` type for synchronously computed values:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+//sampleStart
+fun foo(): Flow<Int> = flow { // flow builder
+ for (i in 1..3) {
+ delay(100) // pretend we are doing something useful here
+ emit(i) // emit next value
+ }
+}
+
+fun main() = runBlocking<Unit> {
+ // Launch a concurrent coroutine to see that the main thread is not blocked
+ launch {
+ for (k in 1..3) {
+ println("I'm not blocked $k")
+ delay(100)
+ }
+ }
+ // Collect the flow
+ foo().collect { value -> println(value) }
+}
+//sampleEnd
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-04.kt).
+
+This code waits 100ms before printing each number without blocking the main thread. This is verified
+by printing "I'm not blocked" every 100ms from a separate coroutine that is running in the main thread:
+
+```text
+I'm not blocked 1
+1
+I'm not blocked 2
+2
+I'm not blocked 3
+3
+```
+
+<!--- TEST -->
+
+Notice the following differences of the code with the [Flow] from the earlier examples:
+
+* A builder function for [Flow] type is called [flow].
+* Code inside the `flow { ... }` builder block can suspend.
+* The function `foo()` is no longer marked with `suspend` modifier.
+* Values are _emitted_ from the flow using [emit][FlowCollector.emit] function.
+* Values are _collected_ from the flow using [collect][collect] function.
+
+> You can replace [delay] with `Thread.sleep` in the body of `foo`'s `flow { ... }` and see that the main
+thread is blocked in this case.
+
+### Flows are cold
+
+Flows are _cold_ streams similarly to sequences &mdash; the code inside a [flow] builder does not
+run until the flow is collected. This becomes clear in the following example:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+//sampleStart
+fun foo(): Flow<Int> = flow {
+ println("Flow started")
+ for (i in 1..3) {
+ delay(100)
+ emit(i)
+ }
+}
+
+fun main() = runBlocking<Unit> {
+ println("Calling foo...")
+ val flow = foo()
+ println("Calling collect...")
+ flow.collect { value -> println(value) }
+ println("Calling collect again...")
+ flow.collect { value -> println(value) }
+}
+//sampleEnd
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-05.kt).
+
+Which prints:
+
+```text
+Calling foo...
+Calling collect...
+Flow started
+1
+2
+3
+Calling collect again...
+Flow started
+1
+2
+3
+```
+
+<!--- TEST -->
+
+That is a key reason why the `foo()` function (which returns a flow) is not marked with `suspend` modifier.
+By itself, `foo()` returns quickly and does not wait for anything. The flow starts every time it is collected,
+that is why we see that when we call `collect` again, we get "Flow started" printed again.
+
+### Flow cancellation
+
+Flow adheres to general cooperative cancellation of coroutines. However, flow infrastructure does not introduce
+additional cancellation points. It is fully transparent for cancellation. As usual, flow collection can be
+cancelled when the flow is suspended in a cancellable suspending function (like [delay]) and cannot be cancelled otherwise.
+
+The following example shows how the flow gets cancelled on timeout when running in [withTimeoutOrNull] block
+and stops executing its code:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+//sampleStart
+fun foo(): Flow<Int> = flow {
+ for (i in 1..3) {
+ delay(100)
+ println("Emitting $i")
+ emit(i)
+ }
+}
+
+fun main() = runBlocking<Unit> {
+ withTimeoutOrNull(250) { // Timeout after 250ms
+ foo().collect { value -> println(value) }
+ }
+ println("Done")
+}
+//sampleEnd
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-06.kt).
+
+Notice how only two numbers get emitted by the flow in `foo()` function, producing the following output:
+
+```text
+Emitting 1
+1
+Emitting 2
+2
+Done
+```
+
+<!--- TEST -->
+
+### Flow builders
+
+The `flow { ... }` builder from the previous examples is the most basic one. There are other builders for
+convenient declaration of flows:
+
+* [flowOf] builder that defines a flow emitting a fixed set of values.
+* Various collections and sequences can be converted to flows using `.asFlow()` extension functions.
+
+Thus, the example that prints numbers from 1 to 3 from a flow can be written as:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun main() = runBlocking<Unit> {
+//sampleStart
+ // Convert an integer range to a flow
+ (1..3).asFlow().collect { value -> println(value) }
+//sampleEnd
+}
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-07.kt).
+
+<!--- TEST
+1
+2
+3
+-->
+
+### Intermediate flow operators
+
+Flows can be transformed with operators similarly to collections and sequences.
+Intermediate operators are applied to an upstream flow and return a downstream flow.
+These operators are cold, just like flows are. A call to such an operator is not
+a suspending function itself. It works quickly, returning the definition of a new transformed flow.
+
+The basic operators have familiar names like [map] and [filter].
+The important difference from sequences is that blocks of
+code inside those operators can call suspending functions.
+
+For example, a flow of incoming requests can be
+mapped to results with the [map] operator even when performing a request is a long-running
+operation that is implemented by a suspending function:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+//sampleStart
+suspend fun performRequest(request: Int): String {
+ delay(1000) // imitate long-running asynchronous work
+ return "response $request"
+}
+
+fun main() = runBlocking<Unit> {
+ (1..3).asFlow() // a flow of requests
+ .map { request -> performRequest(request) }
+ .collect { response -> println(response) }
+}
+//sampleEnd
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-08.kt).
+
+It produces the following three lines, each line appearing after a second:
+
+```text
+response 1
+response 2
+response 3
+```
+
+<!--- TEST -->
+
+#### Transform operator
+
+Among the flow transformation operators, the most general one is called [transform]. It can be used to imitate
+simple transformations like [map] and [filter] as well as implement more complex transformations.
+Using `transform` operator, you can [emit][FlowCollector.emit] arbitrary values an arbitrary number of times.
+
+For example, using `transform` we can emit a string before performing a long-running asynchronous request
+and follow it with a response:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+suspend fun performRequest(request: Int): String {
+ delay(1000) // imitate long-running asynchronous work
+ return "response $request"
+}
+
+fun main() = runBlocking<Unit> {
+//sampleStart
+ (1..3).asFlow() // a flow of requests
+ .transform { request ->
+ emit("Making request $request")
+ emit(performRequest(request))
+ }
+ .collect { response -> println(response) }
+//sampleEnd
+}
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-09.kt).
+
+The output of this code is:
+
+```text
+Making request 1
+response 1
+Making request 2
+response 2
+Making request 3
+response 3
+```
+
+<!--- TEST -->
+
+#### Size-limiting operators
+
+Size-limiting intermediate operators like [take] cancel the execution of the flow when the corresponding limit
+is reached. Cancellation in coroutines is always performed by throwing an exception so that all the resource-management
+functions (like `try { ... } finally { ... }` blocks) operate normally in case of cancellation:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+//sampleStart
+fun numbers(): Flow<Int> = flow {
+ try {
+ emit(1)
+ emit(2)
+ println("This line will not execute")
+ emit(3)
+ } finally {
+ println("Finally in numbers")
+ }
+}
+
+fun main() = runBlocking<Unit> {
+ numbers()
+ .take(2) // take only the first two
+ .collect { value -> println(value) }
+}
+//sampleEnd
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-10.kt).
+
+The output of this code clearly shows that execution of the `flow { ... }` body in `numbers()` function
+had stopped after emitting the second number:
+
+```text
+1
+2
+Finally in numbers
+```
+
+<!--- TEST -->
+
+### Terminal flow operators
+
+Terminal operators on flows are _suspending functions_ that start a collection of the flow.
+The [collect] operator is the most basic one, but there are other terminal operators for
+convenience:
+
+* Conversion to various collections like [toList] and [toSet].
+* Operators to get the [first] value and to ensure that a flow emits a [single] value.
+* Reducing a flow to a value with [reduce] and [fold].
+
+For example:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun main() = runBlocking<Unit> {
+//sampleStart
+ val sum = (1..5).asFlow()
+ .map { it * it } // squares of numbers from 1 to 5
+ .reduce { a, b -> a + b } // sum them (terminal operator)
+ println(sum)
+//sampleEnd
+}
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-11.kt).
+
+Prints a single number:
+
+```text
+55
+```
+
+<!--- TEST -->
+
+### Flows are sequential
+
+Each individual collection of a flow is performed sequentially unless special operators that operate
+on multiple flows are used. The collection works directly in the coroutine that calls a terminal operator.
+No new coroutines are launched by default.
+Each emitted value is processed by all intermediate operators from
+upstream to downstream and is delivered to the terminal operator after that.
+
+See the following example that filters even integers and maps them to strings:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun main() = runBlocking<Unit> {
+//sampleStart
+ (1..5).asFlow()
+ .filter {
+ println("Filter $it")
+ it % 2 == 0
+ }
+ .map {
+ println("Map $it")
+ "string $it"
+ }.collect {
+ println("Collect $it")
+ }
+//sampleEnd
+}
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-12.kt).
+
+Producing:
+
+```text
+Filter 1
+Filter 2
+Map 2
+Collect string 2
+Filter 3
+Filter 4
+Map 4
+Collect string 4
+Filter 5
+```
+
+<!--- TEST -->
+
+### Flow context
+
+Collection of a flow always happens in the context of the calling coroutine. For example, if there is
+a `foo` flow, then the following code runs in the context specified
+by the author of this code, regardless of implementation details of the `foo` flow:
+
+<div class="sample" markdown="1" theme="idea" data-highlight-only>
+
+```kotlin
+withContext(context) {
+ foo.collect { value ->
+ println(value) // run in the specified context
+ }
+}
+```
+
+</div>
+
+<!--- CLEAR -->
+
+This property of a flow is called _context preservation_.
+
+So, by default, code in the `flow { ... }` builder runs in the context that is provided by a collector
+of the corresponding flow. For example, consider the implementation of `foo` that prints the thread
+it is called on and emits three numbers:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun log(msg: String) = println("[${Thread.currentThread().name}] $msg")
+
+//sampleStart
+fun foo(): Flow<Int> = flow {
+ log("Started foo flow")
+ for (i in 1..3) {
+ emit(i)
+ }
+}
+
+fun main() = runBlocking<Unit> {
+ foo().collect { value -> log("Collected $value") }
+}
+//sampleEnd
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-13.kt).
+
+Running this code produces:
+
+```text
+[main @coroutine#1] Started foo flow
+[main @coroutine#1] Collected 1
+[main @coroutine#1] Collected 2
+[main @coroutine#1] Collected 3
+```
+
+<!--- TEST FLEXIBLE_THREAD -->
+
+Since `foo().collect` is called from the main thread, the body of `foo`'s flow is also called in the main thread.
+This is a perfect default for fast-running or asynchronous code that does not care about the execution context and
+does not block the caller.
+
+#### Wrong emission withContext
+
+However, the long-running CPU-consuming code might need to be executed in the context of [Dispatchers.Default] and UI-updating
+code might need to be executed in the context of [Dispatchers.Main]. Usually, [withContext] is used
+to change the context in code using Kotlin coroutines, but code in the `flow { ... }` builder has to honor context
+preservation property and is not allowed to [emit][FlowCollector.emit] from a different context.
+
+Try running the following code:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+//sampleStart
+fun foo(): Flow<Int> = flow {
+ // WRONG way to change context for CPU-consuming code in flow builder
+ kotlinx.coroutines.withContext(Dispatchers.Default) {
+ for (i in 1..3) {
+ Thread.sleep(100) // pretend we are computing it in CPU-consuming way
+ emit(i) // emit next value
+ }
+ }
+}
+
+fun main() = runBlocking<Unit> {
+ foo().collect { value -> println(value) }
+}
+//sampleEnd
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-14.kt).
+
+This code produces the following exception:
+
+<!--- TEST EXCEPTION
+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 ...
+-->
+
+> Note that we had to use a fully qualified name of [kotlinx.coroutines.withContext][withContext] function in this example to
+demonstrate this exception. A short name of `withContext` would have resolved to a special stub function that
+produces compilation error to prevent us from running into this problem.
+
+#### flowOn operator
+
+The exception refers to [flowOn] function that shall be used to change the context of flow emission.
+The correct way of changing the context of a flow is shown in the below example, which also prints
+names of the corresponding threads to show how it all works:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun log(msg: String) = println("[${Thread.currentThread().name}] $msg")
+
+//sampleStart
+fun foo(): Flow<Int> = flow {
+ for (i in 1..3) {
+ Thread.sleep(100) // pretend we are computing it in CPU-consuming way
+ log("Emitting $i")
+ emit(i) // emit next value
+ }
+}.flowOn(Dispatchers.Default) // RIGHT way to change context for CPU-consuming code in flow builder
+
+fun main() = runBlocking<Unit> {
+ foo().collect { value ->
+ log("Collected $value")
+ }
+}
+//sampleEnd
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-15.kt).
+
+Notice how `flow { ... }` works in the background thread, while collection happens in the main thread:
+
+<!--- TEST FLEXIBLE_THREAD
+[DefaultDispatcher-worker-1 @coroutine#2] Emitting 1
+[main @coroutine#1] Collected 1
+[DefaultDispatcher-worker-1 @coroutine#2] Emitting 2
+[main @coroutine#1] Collected 2
+[DefaultDispatcher-worker-1 @coroutine#2] Emitting 3
+[main @coroutine#1] Collected 3
+-->
+
+Another observation here is that [flowOn] operator had changed the default sequential nature of the flow.
+Now collection happens in one coroutine ("coroutine#1") and emission happens in another coroutine
+("coroutine#2") that is running in another thread concurrently with collecting coroutine. The [flowOn] operator
+creates another coroutine for an upstream flow when it has to change the [CoroutineDispatcher] in its context.
+
+### Buffering
+
+Running different parts of a flow in different coroutines can be helpful from the standpoint of overall time it takes
+to collect the flow, especially when long-running asynchronous operations are involved. For example, consider a case when
+emission by `foo()` flow is slow, taking 100 ms to produce an element; and collector is also slow,
+taking 300 ms to process an element. Let us see how long does it take to collect such a flow with three numbers:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+import kotlin.system.*
+
+//sampleStart
+fun foo(): Flow<Int> = flow {
+ for (i in 1..3) {
+ delay(100) // pretend we are asynchronously waiting 100 ms
+ emit(i) // emit next value
+ }
+}
+
+fun main() = runBlocking<Unit> {
+ val time = measureTimeMillis {
+ foo().collect { value ->
+ delay(300) // pretend we are processing it for 300 ms
+ println(value)
+ }
+ }
+ println("Collected in $time ms")
+}
+//sampleEnd
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-16.kt).
+
+It produces something like this, the whole collection taking around 1200 ms (three numbers times 400 ms each):
+
+```text
+1
+2
+3
+Collected in 1220 ms
+```
+
+<!--- TEST ARBITRARY_TIME -->
+
+We can use [buffer] operator on a flow to run emitting code of `foo()` concurrently with collecting code,
+as opposed to running them sequentially:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+import kotlin.system.*
+
+fun foo(): Flow<Int> = flow {
+ for (i in 1..3) {
+ delay(100) // pretend we are asynchronously waiting 100 ms
+ emit(i) // emit next value
+ }
+}
+
+fun main() = runBlocking<Unit> {
+//sampleStart
+ val time = measureTimeMillis {
+ foo()
+ .buffer() // buffer emissions, don't wait
+ .collect { value ->
+ delay(300) // pretend we are processing it for 300 ms
+ println(value)
+ }
+ }
+ println("Collected in $time ms")
+//sampleEnd
+}
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-17.kt).
+
+It produces the same numbers faster, as we have effectively created a processing pipeline,
+only having to wait 100 ms for the first number and then spending only 300 ms to process
+each number. This way it takes around 1000 ms to run:
+
+```text
+1
+2
+3
+Collected in 1071 ms
+```
+
+<!--- TEST ARBITRARY_TIME -->
+
+> Note that [flowOn] operator uses the same buffering mechanism when it has to change [CoroutineDispatcher],
+but here we explicitly request buffering without changing execution context.
+
+#### Conflation
+
+When flow represents partial results of some operation or operation status updates, it may not be necessary
+to process each value, but only to process the most recent ones. In this case, [conflate] operator can be used to skip
+intermediate values when a collector is too slow to process them. Building on the previous example:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+import kotlin.system.*
+
+fun foo(): Flow<Int> = flow {
+ for (i in 1..3) {
+ delay(100) // pretend we are asynchronously waiting 100 ms
+ emit(i) // emit next value
+ }
+}
+
+fun main() = runBlocking<Unit> {
+//sampleStart
+ val time = measureTimeMillis {
+ foo()
+ .conflate() // conflate emissions, don't process each one
+ .collect { value ->
+ delay(300) // pretend we are processing it for 300 ms
+ println(value)
+ }
+ }
+ println("Collected in $time ms")
+//sampleEnd
+}
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-18.kt).
+
+We see that while the first number was being processed the second and the third ones were already produced, so
+the second one was _conflated_ and only the most recent (the third one) was delivered to the collector:
+
+```text
+1
+3
+Collected in 758 ms
+```
+
+<!--- TEST ARBITRARY_TIME -->
+
+#### Processing the latest value
+
+Conflation is one way to speed up processing when both emitter and collector are slow. It does that by dropping emitted values.
+The other way is to cancel slow collector and restart it every time a new value is emitted. There is
+a family of `xxxLatest` operators that perform the same essential logic of `xxx` operator, but cancel the
+code in their block on a new value. Let us change the previous example from [conflate] to [collectLatest]:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+import kotlin.system.*
+
+fun foo(): Flow<Int> = flow {
+ for (i in 1..3) {
+ delay(100) // pretend we are asynchronously waiting 100 ms
+ emit(i) // emit next value
+ }
+}
+
+fun main() = runBlocking<Unit> {
+//sampleStart
+ val time = measureTimeMillis {
+ foo()
+ .collectLatest { value -> // cancel & restart on the latest value
+ println("Collecting $value")
+ delay(300) // pretend we are processing it for 300 ms
+ println("Done $value")
+ }
+ }
+ println("Collected in $time ms")
+//sampleEnd
+}
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-19.kt).
+
+Since the body of [collectLatest] takes 300 ms, but new values are emitted every 100 ms, we see that the block
+is run on every value, but completes only for the last value:
+
+```text
+Collecting 1
+Collecting 2
+Collecting 3
+Done 3
+Collected in 741 ms
+```
+
+<!--- TEST ARBITRARY_TIME -->
+
+### Composing multiple flows
+
+There are several ways to compose multiple flows.
+
+#### Zip
+
+Similarly to [Sequence.zip] extension function in the Kotlin standard library,
+flows have [zip] operator that combines the corresponding values of two flows:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun main() = runBlocking<Unit> {
+//sampleStart
+ val nums = (1..3).asFlow() // numbers 1..3
+ val strs = flowOf("one", "two", "three") // strings
+ nums.zip(strs) { a, b -> "$a -> $b" } // compose a single string
+ .collect { println(it) } // collect and print
+//sampleEnd
+}
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-20.kt).
+
+This example prints:
+
+```text
+1 -> one
+2 -> two
+3 -> three
+```
+
+<!--- TEST -->
+
+#### Combine
+
+When flow represents the most recent value of some variable or operation (see also a related
+section on [conflation](#conflation)) it might be needed to perform a computation that depends on
+the most recent values of the corresponding flows and to recompute it whenever any of upstream
+flows emit a value. The corresponding family of operators is called [combine].
+
+For example, if the numbers in the previous example update every 300ms, but strings update every 400 ms,
+then zipping them using [zip] operator would still produce the same result,
+albeit results are going to be printed every 400 ms:
+
+> We use [onEach] intermediate operator in this example to delay each element and thus make the code
+that emits sample flows more declarative and shorter.
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun main() = runBlocking<Unit> {
+//sampleStart
+ val nums = (1..3).asFlow().onEach { delay(300) } // numbers 1..3 every 300 ms
+ val strs = flowOf("one", "two", "three").onEach { delay(400) } // strings every 400 ms
+ val startTime = System.currentTimeMillis() // remember the start time
+ nums.zip(strs) { a, b -> "$a -> $b" } // compose a single string with "zip"
+ .collect { value -> // collect and print
+ println("$value at ${System.currentTimeMillis() - startTime} ms from start")
+ }
+//sampleEnd
+}
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-21.kt).
+
+<!--- TEST ARBITRARY_TIME
+1 -> one at 437 ms from start
+2 -> two at 837 ms from start
+3 -> three at 1243 ms from start
+-->
+
+However, using [combine] operator here instead of [zip]:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun main() = runBlocking<Unit> {
+//sampleStart
+ val nums = (1..3).asFlow().onEach { delay(300) } // numbers 1..3 every 300 ms
+ val strs = flowOf("one", "two", "three").onEach { delay(400) } // strings every 400 ms
+ val startTime = currentTimeMillis() // remember the start time
+ nums.combine(strs) { a, b -> "$a -> $b" } // compose a single string with "combine"
+ .collect { value -> // collect and print
+ println("$value at ${System.currentTimeMillis() - startTime} ms from start")
+ }
+//sampleEnd
+}
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-22.kt).
+
+We get quite a different output, where a line is printed at each emission from either `nums` or `strs` flows:
+
+```text
+1 -> one at 452 ms from start
+2 -> one at 651 ms from start
+2 -> two at 854 ms from start
+3 -> two at 952 ms from start
+3 -> three at 1256 ms from start
+```
+
+<!--- TEST ARBITRARY_TIME -->
+
+### Flattening flows
+
+Flows represent asynchronously received sequences of values, so it is quite easy to get in a situation where
+each value triggers a request for another sequence of values. For example, we can have the following
+function that returns a flow of two strings 500 ms apart:
+
+<div class="sample" markdown="1" theme="idea" data-highlight-only>
+
+```kotlin
+fun requestFlow(i: Int): Flow<String> = flow {
+ emit("$i: First")
+ delay(500) // wait 500 ms
+ emit("$i: Second")
+}
+```
+
+</div>
+
+<!--- CLEAR -->
+
+Now if we have a flow of three integers and call `requestFlow` for each of them like this:
+
+<div class="sample" markdown="1" theme="idea" data-highlight-only>
+
+```kotlin
+(1..3).asFlow().map { requestFlow(it) }
+```
+
+</div>
+
+<!--- CLEAR -->
+
+Then we end up with a flow of flows (`Flow<Flow<String>>`) that needs to be _flattened_ into a single flow for
+further processing. Collections and sequences have [flatten][Sequence.flatten] and [flatMap][Sequence.flatMap]
+operators for this purpose. However, the asynchronous nature of flows calls for different _modes_ of flattening
+thus there is a family of flattening operators on flows.
+
+#### flatMapConcat
+
+Concatenating mode is implemented by [flatMapConcat] and [flattenConcat] operators. They are the most direct
+analogues of the corresponding sequence operators. They wait for inner flow to complete before
+starting to collect the next one as the following example shows:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun requestFlow(i: Int): Flow<String> = flow {
+ emit("$i: First")
+ delay(500) // wait 500 ms
+ emit("$i: Second")
+}
+
+fun main() = runBlocking<Unit> {
+//sampleStart
+ val startTime = currentTimeMillis() // remember the start time
+ (1..3).asFlow().onEach { delay(100) } // a number every 100 ms
+ .flatMapConcat { requestFlow(it) }
+ .collect { value -> // collect and print
+ println("$value at ${System.currentTimeMillis() - startTime} ms from start")
+ }
+//sampleEnd
+}
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-23.kt).
+
+The sequential nature of [flatMapConcat] is clearly seen in the output:
+
+```text
+1: First at 121 ms from start
+1: Second at 622 ms from start
+2: First at 727 ms from start
+2: Second at 1227 ms from start
+3: First at 1328 ms from start
+3: Second at 1829 ms from start
+```
+
+<!--- TEST ARBITRARY_TIME -->
+
+#### flatMapMerge
+
+Another flattening mode is to concurrently collect all the incoming flows and merge their values into
+a single flow so that values are emitted as soon as possible.
+It is implemented by [flatMapMerge] and [flattenMerge] operators. They both accept an optional
+`concurrency` parameter that limits the number of concurrent flows that are collected at the same time
+(it is equal to [DEFAULT_CONCURRENCY] by default).
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun requestFlow(i: Int): Flow<String> = flow {
+ emit("$i: First")
+ delay(500) // wait 500 ms
+ emit("$i: Second")
+}
+
+fun main() = runBlocking<Unit> {
+//sampleStart
+ val startTime = currentTimeMillis() // remember the start time
+ (1..3).asFlow().onEach { delay(100) } // a number every 100 ms
+ .flatMapMerge { requestFlow(it) }
+ .collect { value -> // collect and print
+ println("$value at ${System.currentTimeMillis() - startTime} ms from start")
+ }
+//sampleEnd
+}
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-24.kt).
+
+The concurrent nature of [flatMapMerge] is obvious:
+
+```text
+1: First at 136 ms from start
+2: First at 231 ms from start
+3: First at 333 ms from start
+1: Second at 639 ms from start
+2: Second at 732 ms from start
+3: Second at 833 ms from start
+```
+
+<!--- TEST ARBITRARY_TIME -->
+
+> Note that [flatMapMerge] call its block of code (`{ requestFlow(it) }` in this example) sequentially, but
+collects the resulting flows concurrently, so it is equivalent to performing a sequential
+`map { requestFlow(it) }` first and then calling [flattenMerge] on the result.
+
+#### flatMapLatest
+
+In a similar way to [collectLatest] operator that was shown in
+["Processing the latest value"](#processing-the-latest-value) section, there is the corresponding "Latest"
+flattening mode where collection of the previous flow is cancelled as soon as new flow is emitted.
+It is implemented by [flatMapLatest] operator.
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun requestFlow(i: Int): Flow<String> = flow {
+ emit("$i: First")
+ delay(500) // wait 500 ms
+ emit("$i: Second")
+}
+
+fun main() = runBlocking<Unit> {
+//sampleStart
+ val startTime = currentTimeMillis() // remember the start time
+ (1..3).asFlow().onEach { delay(100) } // a number every 100 ms
+ .flatMapLatest { requestFlow(it) }
+ .collect { value -> // collect and print
+ println("$value at ${System.currentTimeMillis() - startTime} ms from start")
+ }
+//sampleEnd
+}
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-25.kt).
+
+The output of this example speaks for the way [flatMapLatest] works:
+
+```text
+1: First at 142 ms from start
+2: First at 322 ms from start
+3: First at 425 ms from start
+3: Second at 931 ms from start
+```
+
+<!--- TEST ARBITRARY_TIME -->
+
+> Note that [flatMapLatest] cancels all the code in its block (`{ requestFlow(it) }` in this example) on a new value.
+It makes no difference in this particular example, because the call to `requestFlow` itself is fast, not-suspending,
+and cannot be cancelled. However, it would show up if we were to use suspending functions like `delay` in there.
+
+### Flow exceptions
+
+Flow collection can complete with an exception when emitter or any code inside any of the operators throw an exception.
+There are several ways to handle these exceptions.
+
+#### Collector try and catch
+
+A collector can use Kotlin's [`try/catch`][exceptions] block to handle exceptions:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+//sampleStart
+fun foo(): Flow<Int> = flow {
+ for (i in 1..3) {
+ println("Emitting $i")
+ emit(i) // emit next value
+ }
+}
+
+fun main() = runBlocking<Unit> {
+ try {
+ foo().collect { value ->
+ println(value)
+ check(value <= 1) { "Collected $value" }
+ }
+ } catch (e: Throwable) {
+ println("Caught $e")
+ }
+}
+//sampleEnd
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-26.kt).
+
+This code successfully catches an exception in [collect] terminal operator and,
+as you can see, no more values are emitted after that:
+
+```text
+Emitting 1
+1
+Emitting 2
+2
+Caught java.lang.IllegalStateException: Collected 2
+```
+
+<!--- TEST -->
+
+#### Everything is caught
+
+The previous example actually catches any exception happening in emitter or in any intermediate or terminal operators.
+For example, let us change the code so that emitted values are [mapped][map] to strings,
+but the corresponding code produces an exception:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+//sampleStart
+fun foo(): Flow<String> =
+ flow {
+ for (i in 1..3) {
+ println("Emitting $i")
+ emit(i) // emit next value
+ }
+ }
+ .map { value ->
+ check(value <= 1) { "Crashed on $value" }
+ "string $value"
+ }
+
+fun main() = runBlocking<Unit> {
+ try {
+ foo().collect { value -> println(value) }
+ } catch (e: Throwable) {
+ println("Caught $e")
+ }
+}
+//sampleEnd
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-27.kt).
+
+This exception is still caught and collection is stopped:
+
+```text
+Emitting 1
+string 1
+Emitting 2
+Caught java.lang.IllegalStateException: Crashed on 2
+```
+
+<!--- TEST -->
+
+### Exception transparency
+
+But how can code of emitter encapsulate its exception handling behavior?
+
+Flows must be _transparent to exceptions_ and it is a violation of exception transparency to [emit][FlowCollector.emit] values in the
+`flow { ... }` builder from inside of `try/catch` block. This guarantees that a collector throwing an exception
+can always catch it using `try/catch` as in the previous example.
+
+The emitter can use [catch] operator that preserves this exception transparency and allows encapsulation
+of its exception handling. The body of the `catch` operator can analyze an exception
+and react to it in different ways depending on which exception was caught:
+
+* Exceptions can be rethrown using `throw`.
+* Exceptions can be turned into emission of values using [emit][FlowCollector.emit] from the body of [catch].
+* Exceptions can be ignored, logged, or processed by some other code.
+
+For example, let us emit a text on catching an exception:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun foo(): Flow<String> =
+ flow {
+ for (i in 1..3) {
+ println("Emitting $i")
+ emit(i) // emit next value
+ }
+ }
+ .map { value ->
+ check(value <= 1) { "Crashed on $value" }
+ "string $value"
+ }
+
+fun main() = runBlocking<Unit> {
+//sampleStart
+ foo()
+ .catch { e -> emit("Caught $e") } // emit on exception
+ .collect { value -> println(value) }
+//sampleEnd
+}
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-28.kt).
+
+The output of the example is the same, even though we do not have `try/catch` around the code anymore.
+
+<!--- TEST
+Emitting 1
+string 1
+Emitting 2
+Caught java.lang.IllegalStateException: Crashed on 2
+-->
+
+#### Transparent catch
+
+The [catch] intermediate operator, honoring exception transparency, catches only upstream exceptions
+(that is an exception from all the operators above `catch`, but not below it).
+If the block in `collect { ... }` (placed below `catch`) throws an exception then it escapes:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+//sampleStart
+fun foo(): Flow<Int> = flow {
+ for (i in 1..3) {
+ println("Emitting $i")
+ emit(i)
+ }
+}
+
+fun main() = runBlocking<Unit> {
+ foo()
+ .catch { e -> println("Caught $e") } // does not catch downstream exceptions
+ .collect { value ->
+ check(value <= 1) { "Collected $value" }
+ println(value)
+ }
+}
+//sampleEnd
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-29.kt).
+
+The "Caught ..." message is not printed despite the `catch` operator:
+
+<!--- TEST EXCEPTION
+Emitting 1
+1
+Emitting 2
+Exception in thread "main" java.lang.IllegalStateException: Collected 2
+ at ...
+-->
+
+#### Catching declaratively
+
+We can combine a declarative nature of [catch] operator with a desire to handle all exceptions by moving the body
+of [collect] operator into [onEach] and putting it before the `catch` operator. Collection of this flow must
+be triggered by a call to `collect()` without parameters:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun foo(): Flow<Int> = flow {
+ for (i in 1..3) {
+ println("Emitting $i")
+ emit(i)
+ }
+}
+
+fun main() = runBlocking<Unit> {
+//sampleStart
+ foo()
+ .onEach { value ->
+ check(value <= 1) { "Collected $value" }
+ println(value)
+ }
+ .catch { e -> println("Caught $e") }
+ .collect()
+//sampleEnd
+}
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-30.kt).
+
+Now we can see that "Caught ..." message is printed and thus we can catch all exceptions without explicitly
+using a `try/catch` block:
+
+<!--- TEST EXCEPTION
+Emitting 1
+1
+Emitting 2
+Caught java.lang.IllegalStateException: Collected 2
+-->
+
+### Flow completion
+
+When flow collection completes (normally or exceptionally) it may be needed to execute some action.
+As you might have already noticed, it also can be done in two ways: imperative and declarative.
+
+#### Imperative finally block
+
+In addition to `try`/`catch`, a collector can also use `finally` block to execute an action
+upon `collect` completion.
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+//sampleStart
+fun foo(): Flow<Int> = (1..3).asFlow()
+
+fun main() = runBlocking<Unit> {
+ try {
+ foo().collect { value -> println(value) }
+ } finally {
+ println("Done")
+ }
+}
+//sampleEnd
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-31.kt).
+
+This code prints three numbers produced by the `foo()` flow followed by "Done" string:
+
+```text
+1
+2
+3
+Done
+```
+
+<!--- TEST -->
+
+#### Declarative handling
+
+For declarative approach, flow has [onCompletion] intermediate operator that is invoked
+when the flow is completely collected.
+
+The previous example can be rewritten using [onCompletion] operator and produces the same output:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun foo(): Flow<Int> = (1..3).asFlow()
+
+fun main() = runBlocking<Unit> {
+//sampleStart
+ foo()
+ .onCompletion { println("Done") }
+ .collect { value -> println(value) }
+//sampleEnd
+}
+```
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-32.kt).
+
+<!--- TEST
+1
+2
+3
+Done
+-->
+
+The key advantage of [onCompletion] is a nullable `Throwable` parameter of the lambda that can be used
+to determine whether flow collection was completed normally or exceptionally. In the following
+example `foo()` flow throws exception after emitting number 1:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+//sampleStart
+fun foo(): Flow<Int> = flow {
+ emit(1)
+ throw RuntimeException()
+}
+
+fun main() = runBlocking<Unit> {
+ foo()
+ .onCompletion { cause -> if (cause != null) println("Flow completed exceptionally") }
+ .catch { cause -> println("Caught exception") }
+ .collect { value -> println(value) }
+}
+//sampleEnd
+```
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-33.kt).
+
+As you may expect, it prints:
+
+```text
+1
+Flow completed exceptionally
+Caught exception
+```
+
+<!--- TEST -->
+
+[onCompletion] operator, unlike [catch], does not handle the exception. As we can see from the above
+example code, the exception still flows downstream. It will be delivered to further `onCompletion` operators
+and can be handled with `catch` operator.
+
+#### Upstream exceptions only
+
+Just like [catch] operator, [onCompletion] sees only exception coming from upstream and does not
+see downstream exceptions. For example, run the following code:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+//sampleStart
+fun foo(): Flow<Int> = (1..3).asFlow()
+
+fun main() = runBlocking<Unit> {
+ foo()
+ .onCompletion { cause -> println("Flow completed with $cause") }
+ .collect { value ->
+ check(value <= 1) { "Collected $value" }
+ println(value)
+ }
+}
+//sampleEnd
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-34.kt).
+
+And you can see the completion cause is null, yet collection failed with exception:
+
+```text
+1
+Flow completed with null
+Exception in thread "main" java.lang.IllegalStateException: Collected 2
+```
+
+<!--- TEST EXCEPTION -->
+
+### Imperative versus declarative
+
+Now we know how to collect flow, handle its completion and exceptions in both imperative and declarative ways.
+The natural question here is which approach should be preferred and why.
+As a library, we do not advocate for any particular approach and believe that both options
+are valid and should be selected according to your own preferences and code style.
+
+### Launching flow
+
+It is convenient to use flows to represent asynchronous events that are coming from some source.
+In this case, we need an analogue of `addEventListener` function that registers a piece of code with a reaction
+on incoming events and continues further work. The [onEach] operator can serve this role.
+However, `onEach` is an intermediate operator. We also need a terminal operator to collect the flow.
+Otherwise, just calling `onEach` has no effect.
+
+If we use [collect] terminal operator after `onEach`, then code after it waits until the flow is collected:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+//sampleStart
+// Imitate a flow of events
+fun events(): Flow<Int> = (1..3).asFlow().onEach { delay(100) }
+
+fun main() = runBlocking<Unit> {
+ events()
+ .onEach { event -> println("Event: $event") }
+ .collect() // <--- Collecting the flow waits
+ println("Done")
+}
+//sampleEnd
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-35.kt).
+
+As you can see, it prints:
+
+```text
+Event: 1
+Event: 2
+Event: 3
+Done
+```
+
+<!--- TEST -->
+
+Here [launchIn] terminal operator comes in handy. Replacing `collect` with `launchIn` we can
+launch collection of the flow in a separate coroutine, so that execution of further code
+immediately continues:
+
+<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
+
+```kotlin
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+// Imitate a flow of events
+fun events(): Flow<Int> = (1..3).asFlow().onEach { delay(100) }
+
+//sampleStart
+fun main() = runBlocking<Unit> {
+ events()
+ .onEach { event -> println("Event: $event") }
+ .launchIn(this) // <--- Launching the flow in a separate coroutine
+ println("Done")
+}
+//sampleEnd
+```
+
+</div>
+
+> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-36.kt).
+
+It prints:
+
+```text
+Done
+Event: 1
+Event: 2
+Event: 3
+```
+
+<!--- TEST -->
+
+The required parameter to `launchIn` must specify a [CoroutineScope] in which the coroutine to collect the flow is
+launched. In the above example this scope comes from [runBlocking]
+coroutine builder, so while the flow is running this [runBlocking] scope waits for completion of its child coroutine
+and keeps the main function from returning and terminating this example.
+
+In real applications a scope is going to come from some entity with a limited
+lifetime. As soon as the lifetime of this entity is terminated the corresponding scope is cancelled, cancelling
+collection of the corresponding flow. This way the pair of `onEach { ... }.launchIn(scope)` works
+like `addEventListener`. However, there is no need for the corresponding `removeEventListener` function,
+as cancellation and structured concurrency serve this purpose.
+
+Note, that [launchIn] also returns a [Job] which can be used to [cancel][Job.cancel] the corresponding flow collection
+coroutine only without cancelling the whole scope or to [join][Job.join] it.
+
+<!-- stdlib references -->
+
+[collections]: https://kotlinlang.org/docs/reference/collections-overview.html
+[List]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html
+[forEach]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/for-each.html
+[Sequence]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/index.html
+[Sequence.zip]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/zip.html
+[Sequence.flatten]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/flatten.html
+[Sequence.flatMap]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/flat-map.html
+[exceptions]: https://kotlinlang.org/docs/reference/exceptions.html
+
+<!--- MODULE kotlinx-coroutines-core -->
+<!--- INDEX kotlinx.coroutines -->
+[delay]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/delay.html
+[withTimeoutOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-timeout-or-null.html
+[Dispatchers.Default]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html
+[Dispatchers.Main]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-main.html
+[withContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-context.html
+[CoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-dispatcher/index.html
+[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html
+[runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html
+[Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html
+[Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/cancel.html
+[Job.join]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/join.html
+<!--- INDEX kotlinx.coroutines.flow -->
+[Flow]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/index.html
+[flow]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/flow.html
+[FlowCollector.emit]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow-collector/emit.html
+[collect]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/collect.html
+[flowOf]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/flow-of.html
+[map]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/map.html
+[filter]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/filter.html
+[transform]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/transform.html
+[take]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/take.html
+[toList]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/to-list.html
+[toSet]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/to-set.html
+[first]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/first.html
+[single]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/single.html
+[reduce]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/reduce.html
+[fold]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/fold.html
+[flowOn]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/flow-on.html
+[buffer]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/buffer.html
+[conflate]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/conflate.html
+[collectLatest]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/collect-latest.html
+[zip]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/zip.html
+[combine]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/combine.html
+[onEach]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/on-each.html
+[flatMapConcat]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/flat-map-concat.html
+[flattenConcat]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/flatten-concat.html
+[flatMapMerge]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/flat-map-merge.html
+[flattenMerge]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/flatten-merge.html
+[DEFAULT_CONCURRENCY]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-d-e-f-a-u-l-t_-c-o-n-c-u-r-r-e-n-c-y.html
+[flatMapLatest]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/flat-map-latest.html
+[catch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/catch.html
+[onCompletion]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/on-completion.html
+[launchIn]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/launch-in.html
+<!--- END -->
diff --git a/docs/select-expression.md b/docs/select-expression.md
index 35480abf..f36fa09b 100644
--- a/docs/select-expression.md
+++ b/docs/select-expression.md
@@ -21,7 +21,7 @@ class SelectGuideTest {
<!--- TOC -->
-* [Select expression (experimental)](#select-expression-experimental)
+* [Select Expression (experimental)](#select-expression-experimental)
* [Selecting from channels](#selecting-from-channels)
* [Selecting on close](#selecting-on-close)
* [Selecting to send](#selecting-to-send)
@@ -32,7 +32,7 @@ class SelectGuideTest {
-## Select expression (experimental)
+## Select Expression (experimental)
Select expression makes it possible to await multiple suspending functions simultaneously and _select_
the first one that becomes available.
diff --git a/knit/src/Knit.kt b/knit/src/Knit.kt
index abb66dfa..d3e0a358 100644
--- a/knit/src/Knit.kt
+++ b/knit/src/Knit.kt
@@ -28,6 +28,9 @@ 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"
@@ -36,6 +39,9 @@ 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 = "```"
@@ -73,6 +79,9 @@ fun knit(markdownFile: File): Boolean {
println("*** Reading $markdownFile")
val tocLines = arrayListOf<String>()
var knitRegex: Regex? = null
+ var knitAutonumberGroup = 0
+ var knitAutonumberDigits = 0
+ var knitAutonumberIndex = 1
val includes = arrayListOf<Include>()
val codeLines = arrayListOf<String>()
val testLines = arrayListOf<String>()
@@ -122,7 +131,18 @@ fun knit(markdownFile: File): Boolean {
requireSingleLine(directive)
require(!directive.param.isEmpty()) { "$KNIT_DIRECTIVE directive must include regex parameter" }
require(knitRegex == null) { "Only one KNIT directive is supported"}
- knitRegex = Regex("\\((" + directive.param + ")\\)")
+ 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 -> {
@@ -183,7 +203,9 @@ fun knit(markdownFile: File): Boolean {
if (inLine.startsWith(CODE_START)) {
require(testOut == null || testLines.isEmpty()) { "Previous test was not emitted with $TEST_DIRECTIVE" }
codeLines += ""
- readUntilTo(CODE_END, codeLines)
+ readUntilTo(CODE_END, codeLines) { line ->
+ !line.startsWith(SAMPLE_START) && !line.startsWith(SAMPLE_END)
+ }
continue@mainLoop
}
if (inLine.startsWith(TEST_START)) {
@@ -212,8 +234,19 @@ fun knit(markdownFile: File): Boolean {
remainingApiRefNames += apiRef.name
}
}
- knitRegex?.find(inLine)?.let { knitMatch ->
+ 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 ...")
@@ -328,11 +361,11 @@ private fun flushTestOut(parentDir: File?, testOut: String?, testOutLines: Mutab
private fun MarkdownTextReader.readUntil(marker: String): List<String> =
arrayListOf<String>().also { readUntilTo(marker, it) }
-private fun MarkdownTextReader.readUntilTo(marker: String, list: MutableList<String>) {
+private fun MarkdownTextReader.readUntilTo(marker: String, list: MutableList<String>, linePredicate: (String) -> Boolean = { true }) {
while (true) {
val line = readLine() ?: break
if (line.startsWith(marker)) break
- list += line
+ if (linePredicate(line)) list += line
}
}
@@ -404,6 +437,12 @@ class MarkdownTextReader(r: Reader) : LineNumberReader(r) {
return line
}
+ fun updateLineAndRetry(line: String) {
+ outText.removeAt(outText.lastIndex)
+ outText += line
+ putBackLine = line
+ }
+
fun replaceUntilNextDirective(lines: List<String>): Boolean {
skip = true
while (true) {
diff --git a/kotlinx-coroutines-core/common/src/flow/internal/SafeCollector.kt b/kotlinx-coroutines-core/common/src/flow/internal/SafeCollector.kt
index 8761058e..fec0ee96 100644
--- a/kotlinx-coroutines-core/common/src/flow/internal/SafeCollector.kt
+++ b/kotlinx-coroutines-core/common/src/flow/internal/SafeCollector.kt
@@ -77,8 +77,11 @@ internal class SafeCollector<T>(
*/
if (emissionParentJob !== collectJob) {
error(
- "Flow invariant is violated: emission from another coroutine is detected (child of $emissionParentJob, expected child of $collectJob). " +
- "FlowCollector is not thread-safe and concurrent emissions are prohibited. To mitigate this restriction please use 'channelFlow' builder instead of 'flow'"
+ "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'"
)
}
@@ -91,8 +94,10 @@ internal class SafeCollector<T>(
}
if (result != collectContextSize) {
error(
- "Flow invariant is violated: flow was collected in $collectContext, but emission happened in $currentContext. " +
- "Please refer to 'flow' documentation or use 'flowOn' instead"
+ "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"
)
}
}
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 e6a299ef..a35e8481 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-basic-03.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-basic-03.kt
@@ -8,12 +8,10 @@ package kotlinx.coroutines.guide.basic03
import kotlinx.coroutines.*
fun main() = runBlocking {
-//sampleStart
val job = GlobalScope.launch { // launch a new coroutine and keep a reference to its Job
delay(1000L)
println("World!")
}
println("Hello,")
job.join() // wait until child coroutine completes
-//sampleEnd
}
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 a348ef4a..56e785fb 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-basic-07.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-basic-07.kt
@@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.basic07
import kotlinx.coroutines.*
fun main() = runBlocking {
-//sampleStart
GlobalScope.launch {
repeat(1000) { i ->
println("I'm sleeping $i ...")
@@ -16,5 +15,4 @@ fun main() = runBlocking {
}
}
delay(1300L) // just quit after delay
-//sampleEnd
}
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 e44b7033..ebf5171e 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-01.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-01.kt
@@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.cancel01
import kotlinx.coroutines.*
fun main() = runBlocking {
-//sampleStart
val job = launch {
repeat(1000) { i ->
println("job: I'm sleeping $i ...")
@@ -20,5 +19,4 @@ fun main() = runBlocking {
job.cancel() // cancels the job
job.join() // waits for job's completion
println("main: Now I can quit.")
-//sampleEnd
}
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 518c0be5..e3127b41 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-02.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-02.kt
@@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.cancel02
import kotlinx.coroutines.*
fun main() = runBlocking {
-//sampleStart
val startTime = currentTimeMillis()
val job = launch(Dispatchers.Default) {
var nextPrintTime = startTime
@@ -25,5 +24,4 @@ fun main() = runBlocking {
println("main: I'm tired of waiting!")
job.cancelAndJoin() // cancels the job and waits for its completion
println("main: Now I can quit.")
-//sampleEnd
}
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 8c1e3f83..d47ecd9d 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-03.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-03.kt
@@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.cancel03
import kotlinx.coroutines.*
fun main() = runBlocking {
-//sampleStart
val startTime = currentTimeMillis()
val job = launch(Dispatchers.Default) {
var nextPrintTime = startTime
@@ -25,5 +24,4 @@ fun main() = runBlocking {
println("main: I'm tired of waiting!")
job.cancelAndJoin() // cancels the job and waits for its completion
println("main: Now I can quit.")
-//sampleEnd
}
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 002521e5..45c97851 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-04.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-04.kt
@@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.cancel04
import kotlinx.coroutines.*
fun main() = runBlocking {
-//sampleStart
val job = launch {
try {
repeat(1000) { i ->
@@ -23,5 +22,4 @@ fun main() = runBlocking {
println("main: I'm tired of waiting!")
job.cancelAndJoin() // cancels the job and waits for its completion
println("main: Now I can quit.")
-//sampleEnd
}
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 5c7debb6..9f2cac1c 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-05.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-05.kt
@@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.cancel05
import kotlinx.coroutines.*
fun main() = runBlocking {
-//sampleStart
val job = launch {
try {
repeat(1000) { i ->
@@ -27,5 +26,4 @@ fun main() = runBlocking {
println("main: I'm tired of waiting!")
job.cancelAndJoin() // cancels the job and waits for its completion
println("main: Now I can quit.")
-//sampleEnd
}
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 299ceb27..f06d1187 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-06.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-06.kt
@@ -8,12 +8,10 @@ package kotlinx.coroutines.guide.cancel06
import kotlinx.coroutines.*
fun main() = runBlocking {
-//sampleStart
withTimeout(1300L) {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
}
-//sampleEnd
}
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 1116f913..e2880c91 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-07.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-07.kt
@@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.cancel07
import kotlinx.coroutines.*
fun main() = runBlocking {
-//sampleStart
val result = withTimeoutOrNull(1300L) {
repeat(1000) { i ->
println("I'm sleeping $i ...")
@@ -17,5 +16,4 @@ fun main() = runBlocking {
"Done" // will get cancelled before it produces this result
}
println("Result is $result")
-//sampleEnd
}
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 d3ab53b7..36c6db31 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-01.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-01.kt
@@ -9,7 +9,6 @@ import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
-//sampleStart
val channel = Channel<Int>()
launch {
// this might be heavy CPU-consuming computation or async logic, we'll just send five squares
@@ -18,5 +17,4 @@ fun main() = runBlocking {
// here we print five received integers:
repeat(5) { println(channel.receive()) }
println("Done!")
-//sampleEnd
}
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 9ab469f8..59f5a768 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-02.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-02.kt
@@ -9,7 +9,6 @@ import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
-//sampleStart
val channel = Channel<Int>()
launch {
for (x in 1..5) channel.send(x * x)
@@ -18,5 +17,4 @@ fun main() = runBlocking {
// here we print received values using `for` loop (until the channel is closed)
for (y in channel) println(y)
println("Done!")
-//sampleEnd
}
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 c6550b4d..5c9cfb18 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-03.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-03.kt
@@ -13,9 +13,7 @@ fun CoroutineScope.produceSquares(): ReceiveChannel<Int> = produce {
}
fun main() = runBlocking {
-//sampleStart
val squares = produceSquares()
squares.consumeEach { println(it) }
println("Done!")
-//sampleEnd
}
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 02ac7bb0..4eb6c37d 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-04.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-04.kt
@@ -9,13 +9,11 @@ import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
-//sampleStart
val numbers = produceNumbers() // produces integers from 1 and on
val squares = square(numbers) // squares integers
for (i in 1..5) println(squares.receive()) // print first five
println("Done!") // we are done
coroutineContext.cancelChildren() // cancel children coroutines
-//sampleEnd
}
fun CoroutineScope.produceNumbers() = produce<Int> {
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 625b52c7..8b80764a 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-05.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-05.kt
@@ -9,7 +9,6 @@ import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
-//sampleStart
var cur = numbersFrom(2)
for (i in 1..10) {
val prime = cur.receive()
@@ -17,7 +16,6 @@ fun main() = runBlocking {
cur = filter(cur, prime)
}
coroutineContext.cancelChildren() // cancel all children to let main finish
-//sampleEnd
}
fun CoroutineScope.numbersFrom(start: Int) = produce<Int> {
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 b88a1b00..452e056d 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-06.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-06.kt
@@ -9,12 +9,10 @@ import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking<Unit> {
-//sampleStart
val producer = produceNumbers()
repeat(5) { launchProcessor(it, producer) }
delay(950)
producer.cancel() // cancel producer coroutine and thus kill them all
-//sampleEnd
}
fun CoroutineScope.produceNumbers() = produce<Int> {
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 04872967..9fc852e5 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-07.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-07.kt
@@ -9,7 +9,6 @@ import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
-//sampleStart
val channel = Channel<String>()
launch { sendString(channel, "foo", 200L) }
launch { sendString(channel, "BAR!", 500L) }
@@ -17,7 +16,6 @@ fun main() = runBlocking {
println(channel.receive())
}
coroutineContext.cancelChildren() // cancel all children to let main finish
-//sampleEnd
}
suspend fun sendString(channel: SendChannel<String>, s: String, time: Long) {
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 6c559809..c9916d41 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-08.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-08.kt
@@ -9,7 +9,6 @@ import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking<Unit> {
-//sampleStart
val channel = Channel<Int>(4) // create buffered channel
val sender = launch { // launch sender coroutine
repeat(10) {
@@ -20,5 +19,4 @@ fun main() = runBlocking<Unit> {
// don't receive anything... just wait....
delay(1000)
sender.cancel() // cancel sender coroutine
-//sampleEnd
}
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 ae9d95c7..fb293257 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-09.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-09.kt
@@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.channel09
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
-//sampleStart
data class Ball(var hits: Int)
fun main() = runBlocking {
@@ -28,4 +27,3 @@ suspend fun player(name: String, table: Channel<Ball>) {
table.send(ball) // send the ball back
}
}
-//sampleEnd
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 5dfb7700..ab9ef608 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-compose-01.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-compose-01.kt
@@ -9,14 +9,12 @@ import kotlinx.coroutines.*
import kotlin.system.*
fun main() = runBlocking<Unit> {
-//sampleStart
val time = measureTimeMillis {
val one = doSomethingUsefulOne()
val two = doSomethingUsefulTwo()
println("The answer is ${one + two}")
}
println("Completed in $time ms")
-//sampleEnd
}
suspend fun doSomethingUsefulOne(): Int {
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 d78e5141..9e46c6c4 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-compose-02.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-compose-02.kt
@@ -9,14 +9,12 @@ import kotlinx.coroutines.*
import kotlin.system.*
fun main() = runBlocking<Unit> {
-//sampleStart
val time = measureTimeMillis {
val one = async { doSomethingUsefulOne() }
val two = async { doSomethingUsefulTwo() }
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
-//sampleEnd
}
suspend fun doSomethingUsefulOne(): Int {
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 aa6dd6f7..1dc2fd9b 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-compose-03.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-compose-03.kt
@@ -9,7 +9,6 @@ import kotlinx.coroutines.*
import kotlin.system.*
fun main() = runBlocking<Unit> {
-//sampleStart
val time = measureTimeMillis {
val one = async(start = CoroutineStart.LAZY) { doSomethingUsefulOne() }
val two = async(start = CoroutineStart.LAZY) { doSomethingUsefulTwo() }
@@ -19,7 +18,6 @@ fun main() = runBlocking<Unit> {
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
-//sampleEnd
}
suspend fun doSomethingUsefulOne(): Int {
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 ea6860e0..ad0b0214 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-compose-04.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-compose-04.kt
@@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.compose04
import kotlinx.coroutines.*
import kotlin.system.*
-//sampleStart
// note that we don't have `runBlocking` to the right of `main` in this example
fun main() {
val time = measureTimeMillis {
@@ -23,7 +22,6 @@ fun main() {
}
println("Completed in $time ms")
}
-//sampleEnd
fun somethingUsefulOneAsync() = GlobalScope.async {
doSomethingUsefulOne()
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 e3c5403d..e02f33e0 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-compose-05.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-compose-05.kt
@@ -9,12 +9,10 @@ import kotlinx.coroutines.*
import kotlin.system.*
fun main() = runBlocking<Unit> {
-//sampleStart
val time = measureTimeMillis {
println("The answer is ${concurrentSum()}")
}
println("Completed in $time ms")
-//sampleEnd
}
suspend fun concurrentSum(): Int = coroutineScope {
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 13534c76..c3a9f5af 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-context-01.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-01.kt
@@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.context01
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> {
-//sampleStart
launch { // context of the parent, main runBlocking coroutine
println("main runBlocking : I'm working in thread ${Thread.currentThread().name}")
}
@@ -21,5 +20,4 @@ fun main() = runBlocking<Unit> {
launch(newSingleThreadContext("MyOwnThread")) { // will get its own new thread
println("newSingleThreadContext: I'm working in thread ${Thread.currentThread().name}")
}
-//sampleEnd
}
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 d7be586a..d1ec85fa 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-context-02.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-02.kt
@@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.context02
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> {
-//sampleStart
launch(Dispatchers.Unconfined) { // not confined -- will work with main thread
println("Unconfined : I'm working in thread ${Thread.currentThread().name}")
delay(500)
@@ -19,5 +18,4 @@ fun main() = runBlocking<Unit> {
delay(1000)
println("main runBlocking: After delay in thread ${Thread.currentThread().name}")
}
-//sampleEnd
}
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 a26d3a08..e52976d0 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-context-03.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-03.kt
@@ -10,7 +10,6 @@ import kotlinx.coroutines.*
fun log(msg: String) = println("[${Thread.currentThread().name}] $msg")
fun main() = runBlocking<Unit> {
-//sampleStart
val a = async {
log("I'm computing a piece of the answer")
6
@@ -20,5 +19,4 @@ fun main() = runBlocking<Unit> {
7
}
log("The answer is ${a.await() * b.await()}")
-//sampleEnd
}
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 55cfecc2..b4a8a3f8 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-context-04.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-04.kt
@@ -10,7 +10,6 @@ import kotlinx.coroutines.*
fun log(msg: String) = println("[${Thread.currentThread().name}] $msg")
fun main() {
-//sampleStart
newSingleThreadContext("Ctx1").use { ctx1 ->
newSingleThreadContext("Ctx2").use { ctx2 ->
runBlocking(ctx1) {
@@ -22,5 +21,4 @@ fun main() {
}
}
}
-//sampleEnd
}
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 d014f563..338e3c9d 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-context-05.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-05.kt
@@ -8,7 +8,5 @@ package kotlinx.coroutines.guide.context05
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> {
-//sampleStart
println("My job is ${coroutineContext[Job]}")
-//sampleEnd
}
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 563d418c..b37b06b8 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-context-06.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-06.kt
@@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.context06
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> {
-//sampleStart
// launch a coroutine to process some kind of incoming request
val request = launch {
// it spawns two other jobs, one with GlobalScope
@@ -29,5 +28,4 @@ fun main() = runBlocking<Unit> {
request.cancel() // cancel processing of the request
delay(1000) // delay a second to see what happens
println("main: Who has survived request cancellation?")
-//sampleEnd
}
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 27bff49b..825f572a 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-context-07.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-07.kt
@@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.context07
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> {
-//sampleStart
// launch a coroutine to process some kind of incoming request
val request = launch {
repeat(3) { i -> // launch a few children jobs
@@ -21,5 +20,4 @@ fun main() = runBlocking<Unit> {
}
request.join() // wait for completion of the request, including all its children
println("Now processing of the request is complete")
-//sampleEnd
}
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 2a278d29..1083d77d 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-context-08.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-08.kt
@@ -10,7 +10,6 @@ import kotlinx.coroutines.*
fun log(msg: String) = println("[${Thread.currentThread().name}] $msg")
fun main() = runBlocking(CoroutineName("main")) {
-//sampleStart
log("Started main coroutine")
// run two background value computations
val v1 = async(CoroutineName("v1coroutine")) {
@@ -24,5 +23,4 @@ fun main() = runBlocking(CoroutineName("main")) {
6
}
log("The answer for v1 / v2 = ${v1.await() / v2.await()}")
-//sampleEnd
}
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 b72041b3..386e5254 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-context-09.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-09.kt
@@ -8,9 +8,7 @@ package kotlinx.coroutines.guide.context09
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> {
-//sampleStart
launch(Dispatchers.Default + CoroutineName("test")) {
println("I'm working in thread ${Thread.currentThread().name}")
}
-//sampleEnd
}
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 bce5f143..3dde4467 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-context-10.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-10.kt
@@ -28,7 +28,6 @@ class Activity : CoroutineScope by CoroutineScope(Dispatchers.Default) {
} // class Activity ends
fun main() = runBlocking<Unit> {
-//sampleStart
val activity = Activity()
activity.doSomething() // run test function
println("Launched coroutines")
@@ -36,5 +35,4 @@ fun main() = runBlocking<Unit> {
println("Destroying activity!")
activity.destroy() // cancels all coroutines
delay(1000) // visually confirm that they don't work
-//sampleEnd
}
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 a26d8c6a..4a50d86c 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-context-11.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-11.kt
@@ -10,7 +10,6 @@ import kotlinx.coroutines.*
val threadLocal = ThreadLocal<String?>() // declare thread-local variable
fun main() = runBlocking<Unit> {
-//sampleStart
threadLocal.set("main")
println("Pre-main, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'")
val job = launch(Dispatchers.Default + threadLocal.asContextElement(value = "launch")) {
@@ -20,5 +19,4 @@ fun main() = runBlocking<Unit> {
}
job.join()
println("Post-main, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'")
-//sampleEnd
}
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 80da1dff..818ab285 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-02.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-02.kt
@@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.exceptions02
import kotlinx.coroutines.*
fun main() = runBlocking {
-//sampleStart
val handler = CoroutineExceptionHandler { _, exception ->
println("Caught $exception")
}
@@ -19,5 +18,4 @@ fun main() = runBlocking {
throw ArithmeticException() // Nothing will be printed, relying on user to call deferred.await()
}
joinAll(job, deferred)
-//sampleEnd
}
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 eadbb9bf..2b1e8e62 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-03.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-03.kt
@@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.exceptions03
import kotlinx.coroutines.*
fun main() = runBlocking {
-//sampleStart
val job = launch {
val child = launch {
try {
@@ -25,5 +24,4 @@ fun main() = runBlocking {
println("Parent is not cancelled")
}
job.join()
-//sampleEnd
}
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 e741d39e..02024ce2 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-04.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-04.kt
@@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.exceptions04
import kotlinx.coroutines.*
fun main() = runBlocking {
-//sampleStart
val handler = CoroutineExceptionHandler { _, exception ->
println("Caught $exception")
}
@@ -31,5 +30,4 @@ fun main() = runBlocking {
}
}
job.join()
-//sampleEnd
}
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 0faf8d4f..636c4a1f 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-06.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-06.kt
@@ -9,7 +9,6 @@ import kotlinx.coroutines.*
import java.io.*
fun main() = runBlocking {
-//sampleStart
val handler = CoroutineExceptionHandler { _, exception ->
println("Caught original $exception")
}
@@ -29,5 +28,4 @@ fun main() = runBlocking {
}
}
job.join()
-//sampleEnd
}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-01.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-01.kt
new file mode 100644
index 00000000..020f458b
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-01.kt
@@ -0,0 +1,12 @@
+/*
+ * 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.flow01
+
+fun foo(): List<Int> = listOf(1, 2, 3)
+
+fun main() {
+ foo().forEach { value -> println(value) }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-02.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-02.kt
new file mode 100644
index 00000000..66fc1639
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-02.kt
@@ -0,0 +1,17 @@
+/*
+ * 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.flow02
+
+fun foo(): Sequence<Int> = sequence { // sequence builder
+ for (i in 1..3) {
+ Thread.sleep(100) // pretend we are computing it
+ yield(i) // yield next value
+ }
+}
+
+fun main() {
+ foo().forEach { value -> println(value) }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-03.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-03.kt
new file mode 100644
index 00000000..393a0fa3
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-03.kt
@@ -0,0 +1,17 @@
+/*
+ * 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.flow03
+
+import kotlinx.coroutines.*
+
+suspend fun foo(): List<Int> {
+ delay(1000) // pretend we are doing something asynchronous here
+ return listOf(1, 2, 3)
+}
+
+fun main() = runBlocking<Unit> {
+ foo().forEach { value -> println(value) }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-04.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-04.kt
new file mode 100644
index 00000000..9a3c05cd
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-04.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.flow04
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun foo(): Flow<Int> = flow { // flow builder
+ for (i in 1..3) {
+ delay(100) // pretend we are doing something useful here
+ emit(i) // emit next value
+ }
+}
+
+fun main() = runBlocking<Unit> {
+ // Launch a concurrent coroutine to see that the main thread is not blocked
+ launch {
+ for (k in 1..3) {
+ println("I'm not blocked $k")
+ delay(100)
+ }
+ }
+ // Collect the flow
+ foo().collect { value -> println(value) }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-05.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-05.kt
new file mode 100644
index 00000000..c1e05e2e
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-05.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.flow05
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun foo(): Flow<Int> = flow {
+ println("Flow started")
+ for (i in 1..3) {
+ delay(100)
+ emit(i)
+ }
+}
+
+fun main() = runBlocking<Unit> {
+ println("Calling foo...")
+ val flow = foo()
+ println("Calling collect...")
+ flow.collect { value -> println(value) }
+ println("Calling collect again...")
+ flow.collect { value -> println(value) }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-06.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-06.kt
new file mode 100644
index 00000000..1926983d
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-06.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.flow06
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun foo(): Flow<Int> = flow {
+ for (i in 1..3) {
+ delay(100)
+ println("Emitting $i")
+ emit(i)
+ }
+}
+
+fun main() = runBlocking<Unit> {
+ withTimeoutOrNull(250) { // Timeout after 250ms
+ foo().collect { value -> println(value) }
+ }
+ println("Done")
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-07.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-07.kt
new file mode 100644
index 00000000..47ecf20b
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-07.kt
@@ -0,0 +1,14 @@
+/*
+ * 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.flow07
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun main() = runBlocking<Unit> {
+ // Convert an integer range to a flow
+ (1..3).asFlow().collect { value -> println(value) }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-08.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-08.kt
new file mode 100644
index 00000000..96aa19c1
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-08.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.
+ */
+
+// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit.
+package kotlinx.coroutines.guide.flow08
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+suspend fun performRequest(request: Int): String {
+ delay(1000) // imitate long-running asynchronous work
+ return "response $request"
+}
+
+fun main() = runBlocking<Unit> {
+ (1..3).asFlow() // a flow of requests
+ .map { request -> performRequest(request) }
+ .collect { response -> println(response) }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-09.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-09.kt
new file mode 100644
index 00000000..4af29d93
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-09.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.flow09
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+suspend fun performRequest(request: Int): String {
+ delay(1000) // imitate long-running asynchronous work
+ return "response $request"
+}
+
+fun main() = runBlocking<Unit> {
+ (1..3).asFlow() // a flow of requests
+ .transform { request ->
+ emit("Making request $request")
+ emit(performRequest(request))
+ }
+ .collect { response -> println(response) }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-10.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-10.kt
new file mode 100644
index 00000000..47602dab
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-10.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.flow10
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun numbers(): Flow<Int> = flow {
+ try {
+ emit(1)
+ emit(2)
+ println("This line will not execute")
+ emit(3)
+ } finally {
+ println("Finally in numbers")
+ }
+}
+
+fun main() = runBlocking<Unit> {
+ numbers()
+ .take(2) // take only the first two
+ .collect { value -> println(value) }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-11.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-11.kt
new file mode 100644
index 00000000..a9740062
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-11.kt
@@ -0,0 +1,16 @@
+/*
+ * 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.flow11
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun main() = runBlocking<Unit> {
+ val sum = (1..5).asFlow()
+ .map { it * it } // squares of numbers from 1 to 5
+ .reduce { a, b -> a + b } // sum them (terminal operator)
+ println(sum)
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-12.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-12.kt
new file mode 100644
index 00000000..24dc4267
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-12.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.flow12
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun main() = runBlocking<Unit> {
+ (1..5).asFlow()
+ .filter {
+ println("Filter $it")
+ it % 2 == 0
+ }
+ .map {
+ println("Map $it")
+ "string $it"
+ }.collect {
+ println("Collect $it")
+ }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-13.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-13.kt
new file mode 100644
index 00000000..5d45946c
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-13.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.flow13
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun log(msg: String) = println("[${Thread.currentThread().name}] $msg")
+
+fun foo(): Flow<Int> = flow {
+ log("Started foo flow")
+ for (i in 1..3) {
+ emit(i)
+ }
+}
+
+fun main() = runBlocking<Unit> {
+ foo().collect { value -> log("Collected $value") }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-14.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-14.kt
new file mode 100644
index 00000000..6996abcd
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-14.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.flow14
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun foo(): Flow<Int> = flow {
+ // WRONG way to change context for CPU-consuming code in flow builder
+ kotlinx.coroutines.withContext(Dispatchers.Default) {
+ for (i in 1..3) {
+ Thread.sleep(100) // pretend we are computing it in CPU-consuming way
+ emit(i) // emit next value
+ }
+ }
+}
+
+fun main() = runBlocking<Unit> {
+ foo().collect { value -> println(value) }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-15.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-15.kt
new file mode 100644
index 00000000..3c1b10a9
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-15.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.flow15
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun log(msg: String) = println("[${Thread.currentThread().name}] $msg")
+
+fun foo(): Flow<Int> = flow {
+ for (i in 1..3) {
+ Thread.sleep(100) // pretend we are computing it in CPU-consuming way
+ log("Emitting $i")
+ emit(i) // emit next value
+ }
+}.flowOn(Dispatchers.Default) // RIGHT way to change context for CPU-consuming code in flow builder
+
+fun main() = runBlocking<Unit> {
+ foo().collect { value ->
+ log("Collected $value")
+ }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-16.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-16.kt
new file mode 100644
index 00000000..0698e1bf
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-16.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.flow16
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+import kotlin.system.*
+
+fun foo(): Flow<Int> = flow {
+ for (i in 1..3) {
+ delay(100) // pretend we are asynchronously waiting 100 ms
+ emit(i) // emit next value
+ }
+}
+
+fun main() = runBlocking<Unit> {
+ val time = measureTimeMillis {
+ foo().collect { value ->
+ delay(300) // pretend we are processing it for 300 ms
+ println(value)
+ }
+ }
+ println("Collected in $time ms")
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-17.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-17.kt
new file mode 100644
index 00000000..86de59a2
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-17.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.flow17
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+import kotlin.system.*
+
+fun foo(): Flow<Int> = flow {
+ for (i in 1..3) {
+ delay(100) // pretend we are asynchronously waiting 100 ms
+ emit(i) // emit next value
+ }
+}
+
+fun main() = runBlocking<Unit> {
+ val time = measureTimeMillis {
+ foo()
+ .buffer() // buffer emissions, don't wait
+ .collect { value ->
+ delay(300) // pretend we are processing it for 300 ms
+ println(value)
+ }
+ }
+ println("Collected in $time ms")
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-18.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-18.kt
new file mode 100644
index 00000000..597ff783
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-18.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.flow18
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+import kotlin.system.*
+
+fun foo(): Flow<Int> = flow {
+ for (i in 1..3) {
+ delay(100) // pretend we are asynchronously waiting 100 ms
+ emit(i) // emit next value
+ }
+}
+
+fun main() = runBlocking<Unit> {
+ val time = measureTimeMillis {
+ foo()
+ .conflate() // conflate emissions, don't process each one
+ .collect { value ->
+ delay(300) // pretend we are processing it for 300 ms
+ println(value)
+ }
+ }
+ println("Collected in $time ms")
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-19.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-19.kt
new file mode 100644
index 00000000..eff3d8c0
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-19.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.flow19
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+import kotlin.system.*
+
+fun foo(): Flow<Int> = flow {
+ for (i in 1..3) {
+ delay(100) // pretend we are asynchronously waiting 100 ms
+ emit(i) // emit next value
+ }
+}
+
+fun main() = runBlocking<Unit> {
+ val time = measureTimeMillis {
+ foo()
+ .collectLatest { value -> // cancel & restart on the latest value
+ println("Collecting $value")
+ delay(300) // pretend we are processing it for 300 ms
+ println("Done $value")
+ }
+ }
+ println("Collected in $time ms")
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-20.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-20.kt
new file mode 100644
index 00000000..0cc3df45
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-20.kt
@@ -0,0 +1,16 @@
+/*
+ * 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.flow20
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun main() = runBlocking<Unit> {
+ val nums = (1..3).asFlow() // numbers 1..3
+ val strs = flowOf("one", "two", "three") // strings
+ nums.zip(strs) { a, b -> "$a -> $b" } // compose a single string
+ .collect { println(it) } // collect and print
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-21.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-21.kt
new file mode 100644
index 00000000..5bf0e870
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-21.kt
@@ -0,0 +1,19 @@
+/*
+ * 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.flow21
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun main() = runBlocking<Unit> {
+ val nums = (1..3).asFlow().onEach { delay(300) } // numbers 1..3 every 300 ms
+ val strs = flowOf("one", "two", "three").onEach { delay(400) } // strings every 400 ms
+ val startTime = currentTimeMillis() // remember the start time
+ nums.zip(strs) { a, b -> "$a -> $b" } // compose a single string with "zip"
+ .collect { value -> // collect and print
+ println("$value at ${currentTimeMillis() - startTime} ms from start")
+ }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-22.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-22.kt
new file mode 100644
index 00000000..cd8f8b0c
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-22.kt
@@ -0,0 +1,19 @@
+/*
+ * 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.flow22
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun main() = runBlocking<Unit> {
+ val nums = (1..3).asFlow().onEach { delay(300) } // numbers 1..3 every 300 ms
+ val strs = flowOf("one", "two", "three").onEach { delay(400) } // strings every 400 ms
+ val startTime = currentTimeMillis() // remember the start time
+ nums.combine(strs) { a, b -> "$a -> $b" } // compose a single string with "combine"
+ .collect { value -> // collect and print
+ println("$value at ${currentTimeMillis() - startTime} ms from start")
+ }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-23.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-23.kt
new file mode 100644
index 00000000..742452e4
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-23.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.flow23
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun requestFlow(i: Int): Flow<String> = flow {
+ emit("$i: First")
+ delay(500) // wait 500 ms
+ emit("$i: Second")
+}
+
+fun main() = runBlocking<Unit> {
+ val startTime = currentTimeMillis() // remember the start time
+ (1..3).asFlow().onEach { delay(100) } // a number every 100 ms
+ .flatMapConcat { requestFlow(it) }
+ .collect { value -> // collect and print
+ println("$value at ${currentTimeMillis() - startTime} ms from start")
+ }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-24.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-24.kt
new file mode 100644
index 00000000..32047a93
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-24.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.flow24
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun requestFlow(i: Int): Flow<String> = flow {
+ emit("$i: First")
+ delay(500) // wait 500 ms
+ emit("$i: Second")
+}
+
+fun main() = runBlocking<Unit> {
+ val startTime = currentTimeMillis() // remember the start time
+ (1..3).asFlow().onEach { delay(100) } // a number every 100 ms
+ .flatMapMerge { requestFlow(it) }
+ .collect { value -> // collect and print
+ println("$value at ${currentTimeMillis() - startTime} ms from start")
+ }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-25.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-25.kt
new file mode 100644
index 00000000..09455309
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-25.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.flow25
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun requestFlow(i: Int): Flow<String> = flow {
+ emit("$i: First")
+ delay(500) // wait 500 ms
+ emit("$i: Second")
+}
+
+fun main() = runBlocking<Unit> {
+ val startTime = currentTimeMillis() // remember the start time
+ (1..3).asFlow().onEach { delay(100) } // a number every 100 ms
+ .flatMapLatest { requestFlow(it) }
+ .collect { value -> // collect and print
+ println("$value at ${currentTimeMillis() - startTime} ms from start")
+ }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-26.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-26.kt
new file mode 100644
index 00000000..037e2530
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-26.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.flow26
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun foo(): Flow<Int> = flow {
+ for (i in 1..3) {
+ println("Emitting $i")
+ emit(i) // emit next value
+ }
+}
+
+fun main() = runBlocking<Unit> {
+ try {
+ foo().collect { value ->
+ println(value)
+ check(value <= 1) { "Collected $value" }
+ }
+ } catch (e: Throwable) {
+ println("Caught $e")
+ }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-27.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-27.kt
new file mode 100644
index 00000000..6117cf5c
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-27.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.flow27
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun foo(): Flow<String> =
+ flow {
+ for (i in 1..3) {
+ println("Emitting $i")
+ emit(i) // emit next value
+ }
+ }
+ .map { value ->
+ check(value <= 1) { "Crashed on $value" }
+ "string $value"
+ }
+
+fun main() = runBlocking<Unit> {
+ try {
+ foo().collect { value -> println(value) }
+ } catch (e: Throwable) {
+ println("Caught $e")
+ }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-28.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-28.kt
new file mode 100644
index 00000000..15acb796
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-28.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.flow28
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun foo(): Flow<String> =
+ flow {
+ for (i in 1..3) {
+ println("Emitting $i")
+ emit(i) // emit next value
+ }
+ }
+ .map { value ->
+ check(value <= 1) { "Crashed on $value" }
+ "string $value"
+ }
+
+fun main() = runBlocking<Unit> {
+ foo()
+ .catch { e -> emit("Caught $e") } // emit on exception
+ .collect { value -> println(value) }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-29.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-29.kt
new file mode 100644
index 00000000..c0497df7
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-29.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.flow29
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun foo(): Flow<Int> = flow {
+ for (i in 1..3) {
+ println("Emitting $i")
+ emit(i)
+ }
+}
+
+fun main() = runBlocking<Unit> {
+ foo()
+ .catch { e -> println("Caught $e") } // does not catch downstream exceptions
+ .collect { value ->
+ check(value <= 1) { "Collected $value" }
+ println(value)
+ }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-30.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-30.kt
new file mode 100644
index 00000000..5035efe2
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-30.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.flow30
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun foo(): Flow<Int> = flow {
+ for (i in 1..3) {
+ println("Emitting $i")
+ emit(i)
+ }
+}
+
+fun main() = runBlocking<Unit> {
+ foo()
+ .onEach { value ->
+ check(value <= 1) { "Collected $value" }
+ println(value)
+ }
+ .catch { e -> println("Caught $e") }
+ .collect()
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-31.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-31.kt
new file mode 100644
index 00000000..dfa43db5
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-31.kt
@@ -0,0 +1,19 @@
+/*
+ * 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.flow31
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun foo(): Flow<Int> = (1..3).asFlow()
+
+fun main() = runBlocking<Unit> {
+ try {
+ foo().collect { value -> println(value) }
+ } finally {
+ println("Done")
+ }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-32.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-32.kt
new file mode 100644
index 00000000..f541ab57
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-32.kt
@@ -0,0 +1,17 @@
+/*
+ * 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.flow32
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun foo(): Flow<Int> = (1..3).asFlow()
+
+fun main() = runBlocking<Unit> {
+ foo()
+ .onCompletion { println("Done") }
+ .collect { value -> println(value) }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-33.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-33.kt
new file mode 100644
index 00000000..1e291412
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-33.kt
@@ -0,0 +1,21 @@
+/*
+ * 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.flow33
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun foo(): Flow<Int> = flow {
+ emit(1)
+ throw RuntimeException()
+}
+
+fun main() = runBlocking<Unit> {
+ foo()
+ .onCompletion { cause -> if (cause != null) println("Flow completed exceptionally") }
+ .catch { cause -> println("Caught exception") }
+ .collect { value -> println(value) }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-34.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-34.kt
new file mode 100644
index 00000000..df2cad20
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-34.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.
+ */
+
+// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit.
+package kotlinx.coroutines.guide.flow34
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+fun foo(): Flow<Int> = (1..3).asFlow()
+
+fun main() = runBlocking<Unit> {
+ foo()
+ .onCompletion { cause -> println("Flow completed with $cause") }
+ .collect { value ->
+ check(value <= 1) { "Collected $value" }
+ println(value)
+ }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-35.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-35.kt
new file mode 100644
index 00000000..a7c6bd21
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-35.kt
@@ -0,0 +1,19 @@
+/*
+ * 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.flow35
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+// Imitate a flow of events
+fun events(): Flow<Int> = (1..3).asFlow().onEach { delay(100) }
+
+fun main() = runBlocking<Unit> {
+ events()
+ .onEach { event -> println("Event: $event") }
+ .collect() // <--- Collecting the flow waits
+ println("Done")
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-36.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-36.kt
new file mode 100644
index 00000000..9c5a57bf
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-36.kt
@@ -0,0 +1,19 @@
+/*
+ * 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.flow36
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.*
+
+// Imitate a flow of events
+fun events(): Flow<Int> = (1..3).asFlow().onEach { delay(100) }
+
+fun main() = runBlocking<Unit> {
+ events()
+ .onEach { event -> println("Event: $event") }
+ .launchIn(this) // <--- Launching the flow in a separate coroutine
+ println("Done")
+}
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 24beb566..1939e720 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-select-01.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-select-01.kt
@@ -35,12 +35,10 @@ suspend fun selectFizzBuzz(fizz: ReceiveChannel<String>, buzz: ReceiveChannel<St
}
fun main() = runBlocking<Unit> {
-//sampleStart
val fizz = fizz()
val buzz = buzz()
repeat(7) {
selectFizzBuzz(fizz, buzz)
}
coroutineContext.cancelChildren() // cancel fizz & buzz coroutines
-//sampleEnd
}
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 c763f81b..0e510151 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-select-02.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-select-02.kt
@@ -26,7 +26,6 @@ suspend fun selectAorB(a: ReceiveChannel<String>, b: ReceiveChannel<String>): St
}
fun main() = runBlocking<Unit> {
-//sampleStart
val a = produce<String> {
repeat(4) { send("Hello $it") }
}
@@ -37,5 +36,4 @@ fun main() = runBlocking<Unit> {
println(selectAorB(a, b))
}
coroutineContext.cancelChildren()
-//sampleEnd
}
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 1837aae6..42422378 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-select-03.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-select-03.kt
@@ -20,7 +20,6 @@ fun CoroutineScope.produceNumbers(side: SendChannel<Int>) = produce<Int> {
}
fun main() = runBlocking<Unit> {
-//sampleStart
val side = Channel<Int>() // allocate side channel
launch { // this is a very fast consumer for the side channel
side.consumeEach { println("Side channel has $it") }
@@ -31,5 +30,4 @@ fun main() = runBlocking<Unit> {
}
println("Done consuming")
coroutineContext.cancelChildren()
-//sampleEnd
}
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 1c7818d3..2db51702 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-select-04.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-select-04.kt
@@ -20,7 +20,6 @@ fun CoroutineScope.asyncStringsList(): List<Deferred<String>> {
}
fun main() = runBlocking<Unit> {
-//sampleStart
val list = asyncStringsList()
val result = select<String> {
list.withIndex().forEach { (index, deferred) ->
@@ -32,5 +31,4 @@ fun main() = runBlocking<Unit> {
println(result)
val countActive = list.count { it.isActive }
println("$countActive coroutines are still active")
-//sampleEnd
}
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 6cda3821..e03be9da 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-select-05.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-select-05.kt
@@ -36,7 +36,6 @@ fun CoroutineScope.asyncString(str: String, time: Long) = async {
}
fun main() = runBlocking<Unit> {
-//sampleStart
val chan = Channel<Deferred<String>>() // the channel for test
launch { // launch printing coroutine
for (s in switchMapDeferreds(chan))
@@ -52,5 +51,4 @@ fun main() = runBlocking<Unit> {
delay(1000) // give it time to process
chan.close() // close the channel ...
delay(500) // and wait some time to let it finish
-//sampleEnd
}
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 e0aeeb30..bd710a49 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-sync-01.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-sync-01.kt
@@ -23,7 +23,6 @@ suspend fun massiveRun(action: suspend () -> Unit) {
println("Completed ${n * k} actions in $time ms")
}
-//sampleStart
var counter = 0
fun main() = runBlocking {
@@ -34,4 +33,3 @@ fun main() = runBlocking {
}
println("Counter = $counter")
}
-//sampleEnd
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 84376f7f..81312372 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-sync-02.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-sync-02.kt
@@ -23,7 +23,6 @@ suspend fun massiveRun(action: suspend () -> Unit) {
println("Completed ${n * k} actions in $time ms")
}
-//sampleStart
@Volatile // in Kotlin `volatile` is an annotation
var counter = 0
@@ -35,4 +34,3 @@ fun main() = runBlocking {
}
println("Counter = $counter")
}
-//sampleEnd
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 a2e503f6..1baf849a 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-sync-03.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-sync-03.kt
@@ -24,7 +24,6 @@ suspend fun massiveRun(action: suspend () -> Unit) {
println("Completed ${n * k} actions in $time ms")
}
-//sampleStart
var counter = AtomicInteger()
fun main() = runBlocking {
@@ -35,4 +34,3 @@ fun main() = runBlocking {
}
println("Counter = $counter")
}
-//sampleEnd
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 cdf1f3c8..e16a8113 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-sync-04.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-sync-04.kt
@@ -23,7 +23,6 @@ suspend fun massiveRun(action: suspend () -> Unit) {
println("Completed ${n * k} actions in $time ms")
}
-//sampleStart
val counterContext = newSingleThreadContext("CounterContext")
var counter = 0
@@ -38,4 +37,3 @@ fun main() = runBlocking {
}
println("Counter = $counter")
}
-//sampleEnd
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 72dd1f78..d022961b 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-sync-05.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-sync-05.kt
@@ -23,7 +23,6 @@ suspend fun massiveRun(action: suspend () -> Unit) {
println("Completed ${n * k} actions in $time ms")
}
-//sampleStart
val counterContext = newSingleThreadContext("CounterContext")
var counter = 0
@@ -36,4 +35,3 @@ fun main() = runBlocking {
}
println("Counter = $counter")
}
-//sampleEnd
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 7a401e99..fe08f049 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-sync-06.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-sync-06.kt
@@ -24,7 +24,6 @@ suspend fun massiveRun(action: suspend () -> Unit) {
println("Completed ${n * k} actions in $time ms")
}
-//sampleStart
val mutex = Mutex()
var counter = 0
@@ -39,4 +38,3 @@ fun main() = runBlocking {
}
println("Counter = $counter")
}
-//sampleEnd
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 0ef8a2ab..49f07017 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/example-sync-07.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/example-sync-07.kt
@@ -40,7 +40,6 @@ fun CoroutineScope.counterActor() = actor<CounterMsg> {
}
}
-//sampleStart
fun main() = runBlocking {
val counter = counterActor() // create the actor
withContext(Dispatchers.Default) {
@@ -54,4 +53,3 @@ fun main() = runBlocking {
println("Counter = ${response.await()}")
counter.close() // shutdown the actor
}
-//sampleEnd
diff --git a/kotlinx-coroutines-core/jvm/test/guide/test/FlowGuideTest.kt b/kotlinx-coroutines-core/jvm/test/guide/test/FlowGuideTest.kt
new file mode 100644
index 00000000..0353c54e
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/guide/test/FlowGuideTest.kt
@@ -0,0 +1,381 @@
+// 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(
+ "1",
+ "2",
+ "3"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow02() {
+ test("KotlinxCoroutinesGuideFlow02") { kotlinx.coroutines.guide.flow02.main() }.verifyLines(
+ "1",
+ "2",
+ "3"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow03() {
+ test("KotlinxCoroutinesGuideFlow03") { kotlinx.coroutines.guide.flow03.main() }.verifyLines(
+ "1",
+ "2",
+ "3"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow04() {
+ test("KotlinxCoroutinesGuideFlow04") { kotlinx.coroutines.guide.flow04.main() }.verifyLines(
+ "I'm not blocked 1",
+ "1",
+ "I'm not blocked 2",
+ "2",
+ "I'm not blocked 3",
+ "3"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow05() {
+ test("KotlinxCoroutinesGuideFlow05") { kotlinx.coroutines.guide.flow05.main() }.verifyLines(
+ "Calling foo...",
+ "Calling collect...",
+ "Flow started",
+ "1",
+ "2",
+ "3",
+ "Calling collect again...",
+ "Flow started",
+ "1",
+ "2",
+ "3"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow06() {
+ test("KotlinxCoroutinesGuideFlow06") { kotlinx.coroutines.guide.flow06.main() }.verifyLines(
+ "Emitting 1",
+ "1",
+ "Emitting 2",
+ "2",
+ "Done"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow07() {
+ test("KotlinxCoroutinesGuideFlow07") { kotlinx.coroutines.guide.flow07.main() }.verifyLines(
+ "1",
+ "2",
+ "3"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow08() {
+ test("KotlinxCoroutinesGuideFlow08") { kotlinx.coroutines.guide.flow08.main() }.verifyLines(
+ "response 1",
+ "response 2",
+ "response 3"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow09() {
+ test("KotlinxCoroutinesGuideFlow09") { kotlinx.coroutines.guide.flow09.main() }.verifyLines(
+ "Making request 1",
+ "response 1",
+ "Making request 2",
+ "response 2",
+ "Making request 3",
+ "response 3"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow10() {
+ test("KotlinxCoroutinesGuideFlow10") { kotlinx.coroutines.guide.flow10.main() }.verifyLines(
+ "1",
+ "2",
+ "Finally in numbers"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow11() {
+ test("KotlinxCoroutinesGuideFlow11") { kotlinx.coroutines.guide.flow11.main() }.verifyLines(
+ "55"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow12() {
+ test("KotlinxCoroutinesGuideFlow12") { kotlinx.coroutines.guide.flow12.main() }.verifyLines(
+ "Filter 1",
+ "Filter 2",
+ "Map 2",
+ "Collect string 2",
+ "Filter 3",
+ "Filter 4",
+ "Map 4",
+ "Collect string 4",
+ "Filter 5"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow13() {
+ test("KotlinxCoroutinesGuideFlow13") { kotlinx.coroutines.guide.flow13.main() }.verifyLinesFlexibleThread(
+ "[main @coroutine#1] Started foo flow",
+ "[main @coroutine#1] Collected 1",
+ "[main @coroutine#1] Collected 2",
+ "[main @coroutine#1] Collected 3"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow14() {
+ test("KotlinxCoroutinesGuideFlow14") { kotlinx.coroutines.guide.flow14.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 ..."
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow15() {
+ test("KotlinxCoroutinesGuideFlow15") { kotlinx.coroutines.guide.flow15.main() }.verifyLinesFlexibleThread(
+ "[DefaultDispatcher-worker-1 @coroutine#2] Emitting 1",
+ "[main @coroutine#1] Collected 1",
+ "[DefaultDispatcher-worker-1 @coroutine#2] Emitting 2",
+ "[main @coroutine#1] Collected 2",
+ "[DefaultDispatcher-worker-1 @coroutine#2] Emitting 3",
+ "[main @coroutine#1] Collected 3"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow16() {
+ test("KotlinxCoroutinesGuideFlow16") { kotlinx.coroutines.guide.flow16.main() }.verifyLinesArbitraryTime(
+ "1",
+ "2",
+ "3",
+ "Collected in 1220 ms"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow17() {
+ test("KotlinxCoroutinesGuideFlow17") { kotlinx.coroutines.guide.flow17.main() }.verifyLinesArbitraryTime(
+ "1",
+ "2",
+ "3",
+ "Collected in 1071 ms"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow18() {
+ test("KotlinxCoroutinesGuideFlow18") { kotlinx.coroutines.guide.flow18.main() }.verifyLinesArbitraryTime(
+ "1",
+ "3",
+ "Collected in 758 ms"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow19() {
+ test("KotlinxCoroutinesGuideFlow19") { kotlinx.coroutines.guide.flow19.main() }.verifyLinesArbitraryTime(
+ "Collecting 1",
+ "Collecting 2",
+ "Collecting 3",
+ "Done 3",
+ "Collected in 741 ms"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow20() {
+ test("KotlinxCoroutinesGuideFlow20") { kotlinx.coroutines.guide.flow20.main() }.verifyLines(
+ "1 -> one",
+ "2 -> two",
+ "3 -> three"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow21() {
+ test("KotlinxCoroutinesGuideFlow21") { kotlinx.coroutines.guide.flow21.main() }.verifyLinesArbitraryTime(
+ "1 -> one at 437 ms from start",
+ "2 -> two at 837 ms from start",
+ "3 -> three at 1243 ms from start"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow22() {
+ test("KotlinxCoroutinesGuideFlow22") { kotlinx.coroutines.guide.flow22.main() }.verifyLinesArbitraryTime(
+ "1 -> one at 452 ms from start",
+ "2 -> one at 651 ms from start",
+ "2 -> two at 854 ms from start",
+ "3 -> two at 952 ms from start",
+ "3 -> three at 1256 ms from start"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow23() {
+ test("KotlinxCoroutinesGuideFlow23") { kotlinx.coroutines.guide.flow23.main() }.verifyLinesArbitraryTime(
+ "1: First at 121 ms from start",
+ "1: Second at 622 ms from start",
+ "2: First at 727 ms from start",
+ "2: Second at 1227 ms from start",
+ "3: First at 1328 ms from start",
+ "3: Second at 1829 ms from start"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow24() {
+ test("KotlinxCoroutinesGuideFlow24") { kotlinx.coroutines.guide.flow24.main() }.verifyLinesArbitraryTime(
+ "1: First at 136 ms from start",
+ "2: First at 231 ms from start",
+ "3: First at 333 ms from start",
+ "1: Second at 639 ms from start",
+ "2: Second at 732 ms from start",
+ "3: Second at 833 ms from start"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow25() {
+ test("KotlinxCoroutinesGuideFlow25") { kotlinx.coroutines.guide.flow25.main() }.verifyLinesArbitraryTime(
+ "1: First at 142 ms from start",
+ "2: First at 322 ms from start",
+ "3: First at 425 ms from start",
+ "3: Second at 931 ms from start"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow26() {
+ test("KotlinxCoroutinesGuideFlow26") { kotlinx.coroutines.guide.flow26.main() }.verifyLines(
+ "Emitting 1",
+ "1",
+ "Emitting 2",
+ "2",
+ "Caught java.lang.IllegalStateException: Collected 2"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow27() {
+ test("KotlinxCoroutinesGuideFlow27") { kotlinx.coroutines.guide.flow27.main() }.verifyLines(
+ "Emitting 1",
+ "string 1",
+ "Emitting 2",
+ "Caught java.lang.IllegalStateException: Crashed on 2"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow28() {
+ test("KotlinxCoroutinesGuideFlow28") { kotlinx.coroutines.guide.flow28.main() }.verifyLines(
+ "Emitting 1",
+ "string 1",
+ "Emitting 2",
+ "Caught java.lang.IllegalStateException: Crashed on 2"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow29() {
+ test("KotlinxCoroutinesGuideFlow29") { kotlinx.coroutines.guide.flow29.main() }.verifyExceptions(
+ "Emitting 1",
+ "1",
+ "Emitting 2",
+ "Exception in thread \"main\" java.lang.IllegalStateException: Collected 2",
+ " at ..."
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow30() {
+ test("KotlinxCoroutinesGuideFlow30") { kotlinx.coroutines.guide.flow30.main() }.verifyExceptions(
+ "Emitting 1",
+ "1",
+ "Emitting 2",
+ "Caught java.lang.IllegalStateException: Collected 2"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow31() {
+ test("KotlinxCoroutinesGuideFlow31") { kotlinx.coroutines.guide.flow31.main() }.verifyLines(
+ "1",
+ "2",
+ "3",
+ "Done"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow32() {
+ test("KotlinxCoroutinesGuideFlow32") { kotlinx.coroutines.guide.flow32.main() }.verifyLines(
+ "1",
+ "2",
+ "3",
+ "Done"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow33() {
+ test("KotlinxCoroutinesGuideFlow33") { kotlinx.coroutines.guide.flow33.main() }.verifyLines(
+ "1",
+ "Flow completed exceptionally",
+ "Caught exception"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow34() {
+ test("KotlinxCoroutinesGuideFlow34") { kotlinx.coroutines.guide.flow34.main() }.verifyExceptions(
+ "1",
+ "Flow completed with null",
+ "Exception in thread \"main\" java.lang.IllegalStateException: Collected 2"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow35() {
+ test("KotlinxCoroutinesGuideFlow35") { kotlinx.coroutines.guide.flow35.main() }.verifyLines(
+ "Event: 1",
+ "Event: 2",
+ "Event: 3",
+ "Done"
+ )
+ }
+
+ @Test
+ fun testKotlinxCoroutinesGuideFlow36() {
+ test("KotlinxCoroutinesGuideFlow36") { kotlinx.coroutines.guide.flow36.main() }.verifyLines(
+ "Done",
+ "Event: 1",
+ "Event: 2",
+ "Event: 3"
+ )
+ }
+}
diff --git a/kotlinx-coroutines-core/jvm/test/guide/test/TestUtil.kt b/kotlinx-coroutines-core/jvm/test/guide/test/TestUtil.kt
index 83150e10..c72b60bd 100644
--- a/kotlinx-coroutines-core/jvm/test/guide/test/TestUtil.kt
+++ b/kotlinx-coroutines-core/jvm/test/guide/test/TestUtil.kt
@@ -132,6 +132,7 @@ private fun sanitize(s: String, mode: SanitizeMode): String {
res = res.replace(Regex("DefaultDispatcher-worker-[0-9]+"), "DefaultDispatcher")
res = res.replace(Regex("RxComputationThreadPool-[0-9]+"), "RxComputationThreadPool")
res = res.replace(Regex("Test( worker)?"), "main")
+ res = res.replace(Regex("@[0-9a-f]+"), "") // drop hex address
}
SanitizeMode.NONE -> {}
}
@@ -180,8 +181,17 @@ fun List<String>.verifyLinesStartUnordered(vararg expected: String) = verify {
}
fun List<String>.verifyExceptions(vararg expected: String) {
- val actual = filter { !it.startsWith("\tat ") }
-
+ val original = this
+ val actual = ArrayList<String>().apply {
+ var except = false
+ for (line in original) {
+ when {
+ !except && line.startsWith("\tat") -> except = true
+ except && !line.startsWith("\t") && !line.startsWith("Caused by: ") -> except = false
+ }
+ if (!except) add(line)
+ }
+ }
val n = minOf(actual.size, expected.size)
for (i in 0 until n) {
val exp = sanitize(expected[i], SanitizeMode.FLEXIBLE_THREAD)