Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
elizarov committed Jul 3, 2020
2 parents 634b907 + f4fb204 commit 3744f8e
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 77 deletions.
2 changes: 1 addition & 1 deletion docs/coroutine-context-and-dispatchers.md
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ fun main() = runBlocking<Unit> {
In this example we launch a new coroutine in a background thread pool using [Dispatchers.Default], so
it works on a different thread from the thread pool, but it still has the value of the thread local variable
that we specified using `threadLocal.asContextElement(value = "launch")`,
no matter on what thread the coroutine is executed.
no matter which thread the coroutine is executed on.
Thus, the output (with [debug](#debugging-coroutines-and-threads)) is:

```text
Expand Down
76 changes: 38 additions & 38 deletions docs/exception-handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ coroutine throw an exception.
Coroutine builders come in two flavors: propagating exceptions automatically ([launch] and [actor]) or
exposing them to users ([async] and [produce]).
When these builders are used to create a _root_ coroutine, that is not a _child_ of another coroutine,
the former builder treat exceptions as **uncaught** exceptions, similar to Java's `Thread.uncaughtExceptionHandler`,
the former builders treat exceptions as **uncaught** exceptions, similar to Java's `Thread.uncaughtExceptionHandler`,
while the latter are relying on the user to consume the final
exception, for example via [await][Deferred.await] or [receive][ReceiveChannel.receive]
([produce] and [receive][ReceiveChannel.receive] are covered later in [Channels](https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/channels.md) section).
Expand Down Expand Up @@ -246,7 +246,7 @@ CoroutineExceptionHandler got java.lang.ArithmeticException

### Exceptions aggregation

When multiple children of a coroutine fail with an exception the
When multiple children of a coroutine fail with an exception, the
general rule is "the first exception wins", so the first exception gets handled.
All additional exceptions that happen after the first one are attached to the first exception as suppressed ones.

Expand Down Expand Up @@ -296,8 +296,8 @@ CoroutineExceptionHandler got java.io.IOException with suppressed [java.lang.Ari

<!--- TEST-->

> Note, this mechanism currently works only on Java version 1.7+.
Limitation on JS and Native is temporary and will be fixed in the future.
> Note that this mechanism currently only works on Java version 1.7+.
The JS and Native restrictions are temporary and will be lifted in the future.

Cancellation exceptions are transparent and are unwrapped by default:

Expand Down Expand Up @@ -353,14 +353,14 @@ A good example of such a requirement is a UI component with the job defined in i
have failed, it is not always necessary to cancel (effectively kill) the whole UI component,
but if UI component is destroyed (and its job is cancelled), then it is necessary to fail all child jobs as their results are no longer needed.

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.
Another example is a server process that spawns multiple child jobs and needs to _supervise_
their execution, tracking their failures and only restarting the failed ones.

#### Supervision job

For these purposes [SupervisorJob][SupervisorJob()] can be used.
The [SupervisorJob][SupervisorJob()] can be used for these purposes.
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:
only downwards. This can easily be demonstrated using the following example:

<div class="sample" markdown="1" theme="idea" data-highlight-only>

Expand All @@ -372,24 +372,24 @@ fun main() = runBlocking {
with(CoroutineScope(coroutineContext + supervisor)) {
// launch the first child -- its exception is ignored for this example (don't do this in practice!)
val firstChild = launch(CoroutineExceptionHandler { _, _ -> }) {
println("First child is failing")
throw AssertionError("First child is cancelled")
println("The first child is failing")
throw AssertionError("The first child is cancelled")
}
// launch the second child
val secondChild = launch {
firstChild.join()
// Cancellation of the first child is not propagated to the second child
println("First child is cancelled: ${firstChild.isCancelled}, but second one is still active")
println("The first child is cancelled: ${firstChild.isCancelled}, but the second one is still active")
try {
delay(Long.MAX_VALUE)
} finally {
// But cancellation of the supervisor is propagated
println("Second child is cancelled because supervisor is cancelled")
println("The second child is cancelled because the supervisor was cancelled")
}
}
// wait until the first child fails & completes
firstChild.join()
println("Cancelling supervisor")
println("Cancelling the supervisor")
supervisor.cancel()
secondChild.join()
}
Expand All @@ -403,18 +403,18 @@ fun main() = runBlocking {
The output of this code is:

```text
First child is failing
First child is cancelled: true, but second one is still active
Cancelling supervisor
Second child is cancelled because supervisor is cancelled
The first child is failing
The first child is cancelled: true, but the second one is still active
Cancelling the supervisor
The second child is cancelled because the supervisor was cancelled
```
<!--- TEST-->


#### Supervision scope

For _scoped_ concurrency [supervisorScope] can be used instead of [coroutineScope] for the same purpose. It propagates cancellation
in one direction only and cancels all children only if it has failed itself. It also waits for all children before completion
Instead of [coroutineScope], we can use [supervisorScope] for _scoped_ concurrency. It propagates the cancellation
in one direction only and cancels all its children only if it failed itself. It also waits for all children before completion
just like [coroutineScope] does.

<div class="sample" markdown="1" theme="idea" data-highlight-only>
Expand All @@ -428,19 +428,19 @@ fun main() = runBlocking {
supervisorScope {
val child = launch {
try {
println("Child is sleeping")
println("The child is sleeping")
delay(Long.MAX_VALUE)
} finally {
println("Child is cancelled")
println("The child is cancelled")
}
}
// Give our child a chance to execute and print using yield
yield()
println("Throwing exception from scope")
println("Throwing an exception from the scope")
throw AssertionError()
}
} catch(e: AssertionError) {
println("Caught assertion error")
println("Caught an assertion error")
}
}
```
Expand All @@ -452,21 +452,21 @@ fun main() = runBlocking {
The output of this code is:

```text
Child is sleeping
Throwing exception from scope
Child is cancelled
Caught assertion error
The child is sleeping
Throwing an exception from the scope
The child is cancelled
Caught an assertion error
```
<!--- TEST-->

#### 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 mechanism.
This difference comes from the fact that child's failure is not propagated to the parent.
It means that coroutines launched directly inside [supervisorScope] _do_ use the [CoroutineExceptionHandler]
Every child should handle its exceptions by itself via the exception handling mechanism.
This difference comes from the fact that child's failure does not propagate to the parent.
It means that coroutines launched directly inside the [supervisorScope] _do_ use the [CoroutineExceptionHandler]
that is installed in their scope in the same way as root coroutines do
(see [CoroutineExceptionHandler](#coroutineexceptionhandler) section for details).
(see the [CoroutineExceptionHandler](#coroutineexceptionhandler) section for details).

<div class="sample" markdown="1" theme="idea" data-highlight-only>

Expand All @@ -480,12 +480,12 @@ fun main() = runBlocking {
}
supervisorScope {
val child = launch(handler) {
println("Child throws an exception")
println("The child throws an exception")
throw AssertionError()
}
println("Scope is completing")
println("The scope is completing")
}
println("Scope is completed")
println("The scope is completed")
}
```

Expand All @@ -496,10 +496,10 @@ fun main() = runBlocking {
The output of this code is:

```text
Scope is completing
Child throws an exception
The scope is completing
The child throws an exception
CoroutineExceptionHandler got java.lang.AssertionError
Scope is completed
The scope is completed
```
<!--- TEST-->

Expand All @@ -517,8 +517,8 @@ Scope is completed
[runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html
[SupervisorJob()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-supervisor-job.html
[Job()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job.html
[supervisorScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/supervisor-scope.html
[coroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html
[supervisorScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/supervisor-scope.html
<!--- INDEX kotlinx.coroutines.channels -->
[actor]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/actor.html
[produce]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/produce.html
Expand Down
4 changes: 2 additions & 2 deletions integration/kotlinx-coroutines-jdk8/src/future/Future.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import java.util.function.*
import kotlin.coroutines.*

/**
* Starts new coroutine and returns its result as an implementation of [CompletableFuture].
* Starts a new coroutine and returns its result as an implementation of [CompletableFuture].
* The running coroutine is cancelled when the resulting future is cancelled or otherwise completed.
*
* Coroutine context is inherited from a [CoroutineScope], additional context elements can be specified with [context] argument.
* The coroutine context is inherited from a [CoroutineScope], additional context elements can be specified with the [context] argument.
* If the context does not have any dispatcher nor any other [ContinuationInterceptor], then [Dispatchers.Default] is used.
* The parent job is inherited from a [CoroutineScope] as well, but it can also be overridden
* with corresponding [context] element.
Expand Down
10 changes: 5 additions & 5 deletions kotlinx-coroutines-core/jvm/test/guide/example-supervision-01.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,24 @@ fun main() = runBlocking {
with(CoroutineScope(coroutineContext + supervisor)) {
// launch the first child -- its exception is ignored for this example (don't do this in practice!)
val firstChild = launch(CoroutineExceptionHandler { _, _ -> }) {
println("First child is failing")
throw AssertionError("First child is cancelled")
println("The first child is failing")
throw AssertionError("The first child is cancelled")
}
// launch the second child
val secondChild = launch {
firstChild.join()
// Cancellation of the first child is not propagated to the second child
println("First child is cancelled: ${firstChild.isCancelled}, but second one is still active")
println("The first child is cancelled: ${firstChild.isCancelled}, but the second one is still active")
try {
delay(Long.MAX_VALUE)
} finally {
// But cancellation of the supervisor is propagated
println("Second child is cancelled because supervisor is cancelled")
println("The second child is cancelled because the supervisor was cancelled")
}
}
// wait until the first child fails & completes
firstChild.join()
println("Cancelling supervisor")
println("Cancelling the supervisor")
supervisor.cancel()
secondChild.join()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@ fun main() = runBlocking {
supervisorScope {
val child = launch {
try {
println("Child is sleeping")
println("The child is sleeping")
delay(Long.MAX_VALUE)
} finally {
println("Child is cancelled")
println("The child is cancelled")
}
}
// Give our child a chance to execute and print using yield
yield()
println("Throwing exception from scope")
println("Throwing an exception from the scope")
throw AssertionError()
}
} catch(e: AssertionError) {
println("Caught assertion error")
println("Caught an assertion error")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ fun main() = runBlocking {
}
supervisorScope {
val child = launch(handler) {
println("Child throws an exception")
println("The child throws an exception")
throw AssertionError()
}
println("Scope is completing")
println("The scope is completing")
}
println("Scope is completed")
println("The scope is completed")
}
22 changes: 11 additions & 11 deletions kotlinx-coroutines-core/jvm/test/guide/test/ExceptionsGuideTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -63,30 +63,30 @@ class ExceptionsGuideTest {
@Test
fun testExampleSupervision01() {
test("ExampleSupervision01") { kotlinx.coroutines.guide.exampleSupervision01.main() }.verifyLines(
"First child is failing",
"First child is cancelled: true, but second one is still active",
"Cancelling supervisor",
"Second child is cancelled because supervisor is cancelled"
"The first child is failing",
"The first child is cancelled: true, but the second one is still active",
"Cancelling the supervisor",
"The second child is cancelled because the supervisor was cancelled"
)
}

@Test
fun testExampleSupervision02() {
test("ExampleSupervision02") { kotlinx.coroutines.guide.exampleSupervision02.main() }.verifyLines(
"Child is sleeping",
"Throwing exception from scope",
"Child is cancelled",
"Caught assertion error"
"The child is sleeping",
"Throwing an exception from the scope",
"The child is cancelled",
"Caught an assertion error"
)
}

@Test
fun testExampleSupervision03() {
test("ExampleSupervision03") { kotlinx.coroutines.guide.exampleSupervision03.main() }.verifyLines(
"Scope is completing",
"Child throws an exception",
"The scope is completing",
"The child throws an exception",
"CoroutineExceptionHandler got java.lang.AssertionError",
"Scope is completed"
"The scope is completed"
)
}
}
6 changes: 3 additions & 3 deletions reactive/kotlinx-coroutines-reactive/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ Coroutine builders:

| **Name** | **Result** | **Scope** | **Description**
| --------------- | ----------------------------- | ---------------- | ---------------
| [publish] | `Publisher` | [ProducerScope] | Cold reactive publisher that starts coroutine on subscribe
| [publish] | `Publisher` | [ProducerScope] | Cold reactive publisher that starts the coroutine on subscribe

Integration with [Flow]:

| **Name** | **Result** | **Description**
| --------------- | -------------- | ---------------
| [Publisher.asFlow] | `Flow` | Converts the given publisher to flow
| [Flow.asPublisher] | `Publisher` | Converts the given flow to the TCK-compliant publisher
| [Publisher.asFlow] | `Flow` | Converts the given publisher to a flow
| [Flow.asPublisher] | `Publisher` | Converts the given flow to a TCK-compliant publisher

If these adapters are used along with `kotlinx-coroutines-reactor` in the classpath, then Reactor's `Context` is properly
propagated as coroutine context element (`ReactorContext`) and vice versa.
Expand Down
20 changes: 10 additions & 10 deletions reactive/kotlinx-coroutines-reactor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,29 @@ Coroutine builders:

| **Name** | **Result** | **Scope** | **Description**
| --------------- | ------------| ---------------- | ---------------
| [mono] | `Mono` | [CoroutineScope] | Cold mono that starts coroutine on subscribe
| [flux] | `Flux` | [CoroutineScope] | Cold flux that starts coroutine on subscribe
| [mono] | `Mono` | [CoroutineScope] | A cold Mono that starts the coroutine on subscription
| [flux] | `Flux` | [CoroutineScope] | A cold Flux that starts the coroutine on subscription

Note that `Mono` and `Flux` are a subclass of [Reactive Streams](https://www.reactive-streams.org)
`Publisher` and extensions for it are covered by
Note that `Mono` and `Flux` are subclasses of [Reactive Streams](https://www.reactive-streams.org)'
`Publisher` and extensions for it are covered by the
[kotlinx-coroutines-reactive](../kotlinx-coroutines-reactive) module.

Integration with [Flow]:

| **Name** | **Result** | **Description**
| --------------- | -------------- | ---------------
| [Flow.asFlux] | `Flux` | Converts the given flow to the TCK-compliant Flux.
| [Flow.asFlux] | `Flux` | Converts the given flow to a TCK-compliant Flux.

This adapter is integrated with Reactor's `Context` and coroutines [ReactorContext].
This adapter is integrated with Reactor's `Context` and coroutines' [ReactorContext].

Conversion functions:

| **Name** | **Description**
| -------- | ---------------
| [Job.asMono][kotlinx.coroutines.Job.asMono] | Converts job to hot mono
| [Deferred.asMono][kotlinx.coroutines.Deferred.asMono] | Converts deferred value to hot mono
| [ReceiveChannel.asFlux][kotlinx.coroutines.channels.ReceiveChannel.asFlux] | Converts streaming channel to hot flux
| [Scheduler.asCoroutineDispatcher][reactor.core.scheduler.Scheduler.asCoroutineDispatcher] | Converts scheduler to [CoroutineDispatcher]
| [Job.asMono][kotlinx.coroutines.Job.asMono] | Converts a job to a hot Mono
| [Deferred.asMono][kotlinx.coroutines.Deferred.asMono] | Converts a deferred value to a hot Mono
| [ReceiveChannel.asFlux][kotlinx.coroutines.channels.ReceiveChannel.asFlux] | Converts a streaming channel to a hot Flux
| [Scheduler.asCoroutineDispatcher][reactor.core.scheduler.Scheduler.asCoroutineDispatcher] | Converts a scheduler to a [CoroutineDispatcher]

<!--- MODULE kotlinx-coroutines-core -->
<!--- INDEX kotlinx.coroutines -->
Expand Down

0 comments on commit 3744f8e

Please sign in to comment.