Supervisors guide
diff --git a/docs/exception-handling.md b/docs/exception-handling.md
index e2772e3..8888c56 100644
--- a/docs/exception-handling.md
+++ b/docs/exception-handling.md
@@ -26,6 +26,10 @@
* [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)
<!--- END_TOC -->
@@ -344,6 +348,165 @@
```
<!--- TEST-->
+## Supervision
+
+As we have studied before, cancellation is a bidirectional relationship propagating through the whole
+coroutines hierarchy. But what if unidirectional cancellation is required?
+
+Good example of such requirement can be a UI component with the job defined in its scope. If any of UI's child task
+has 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 children jobs as their result is no longer required.
+
+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
+
+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:
+
+<!--- INCLUDE
+import kotlin.coroutines.experimental.*
+-->
+
+<div class="sample" markdown="1" theme="idea" data-highlight-only>
+
+```kotlin
+fun main(args: Array<String>) = runBlocking {
+ val supervisor = SupervisorJob()
+ with(CoroutineScope(coroutineContext + supervisor)) {
+ // launch the first child -- its exception is ignored for this example (don't do this in practise!)
+ val firstChild = launch(CoroutineExceptionHandler { _, _ -> }) {
+ println("First child is failing")
+ throw AssertionError("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")
+ try {
+ delay(Long.MAX_VALUE)
+ } finally {
+ // But cancellation of the supervisor is propagated
+ println("Second child is cancelled because supervisor is cancelled")
+ }
+ }
+ // wait until the first child fails & completes
+ firstChild.join()
+ println("Cancelling supervisor")
+ supervisor.cancel()
+ secondChild.join()
+ }
+}
+```
+
+</div>
+
+> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-supervision-01.kt)
+
+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
+```
+<!--- TEST-->
+
+
+### 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
+just like [coroutineScope] does.
+
+<!--- INCLUDE
+import kotlin.coroutines.experimental.*
+-->
+
+<div class="sample" markdown="1" theme="idea" data-highlight-only>
+
+```kotlin
+fun main(args: Array<String>) = runBlocking {
+ try {
+ supervisorScope {
+ val child = launch {
+ try {
+ println("Child is sleeping")
+ delay(Long.MAX_VALUE)
+ } finally {
+ println("Child is cancelled")
+ }
+ }
+ // Give our child a chance to execute and print using yield
+ yield()
+ println("Throwing exception from scope")
+ throw AssertionError()
+ }
+ } catch(e: AssertionError) {
+ println("Caught assertion error")
+ }
+}
+```
+
+</div>
+
+> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-supervision-02.kt)
+
+The output of this code is:
+
+```text
+Child is sleeping
+Throwing exception from scope
+Child is cancelled
+Caught 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 mechanisms.
+This difference comes from the fact that child's failure is not propagated to the parent.
+
+<!--- INCLUDE
+import kotlin.coroutines.experimental.*
+-->
+
+<div class="sample" markdown="1" theme="idea" data-highlight-only>
+
+```kotlin
+fun main(args: Array<String>) = runBlocking {
+ val handler = CoroutineExceptionHandler { _, exception ->
+ println("Caught $exception")
+ }
+ supervisorScope {
+ val child = launch(handler) {
+ println("Child throws an exception")
+ throw AssertionError()
+ }
+ println("Scope is completing")
+ }
+ println("Scope is completed")
+}
+```
+
+</div>
+
+> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-supervision-03.kt)
+
+The output of this code is:
+
+```text
+Scope is completing
+Child throws an exception
+Caught java.lang.AssertionError
+Scope is completed
+```
+<!--- TEST-->
+
<!--- MODULE kotlinx-coroutines-core -->
<!--- INDEX kotlinx.coroutines.experimental -->
[CancellationException]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-cancellation-exception/index.html
@@ -353,6 +516,10 @@
[async]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/async.html
[Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-job/cancel.html
[runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/run-blocking.html
+[SupervisorJob()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-supervisor-job.html
+[Job()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-job.html
+[supervisorScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/supervisor-scope.html
+[coroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/coroutine-scope.html
<!--- INDEX kotlinx.coroutines.experimental.channels -->
[actor]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/actor.html
[produce]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/produce.html