blob: 55808a463325142517edb9310d5062f8474cd379 [file] [log] [blame] [view]
hadihariri7db55532018-09-15 10:35:08 +02001<!--- INCLUDE .*/example-([a-z]+)-([0-9a-z]+)\.kt
2/*
3 * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
4 */
5
6// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit.
Roman Elizarov0950dfa2018-07-13 10:33:25 +03007package kotlinx.coroutines.guide.$$1$$2
hadihariri7db55532018-09-15 10:35:08 +02008
Roman Elizarov0950dfa2018-07-13 10:33:25 +03009import kotlinx.coroutines.*
hadihariri7db55532018-09-15 10:35:08 +020010-->
11<!--- KNIT ../core/kotlinx-coroutines-core/test/guide/.*\.kt -->
12<!--- TEST_OUT ../core/kotlinx-coroutines-core/test/guide/test/SharedStateGuideTest.kt
13// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit.
Roman Elizarov0950dfa2018-07-13 10:33:25 +030014package kotlinx.coroutines.guide.test
hadihariri7db55532018-09-15 10:35:08 +020015
16import org.junit.Test
17
18class SharedStateGuideTest {
19-->
20## Table of contents
21
22<!--- TOC -->
23
24* [Shared mutable state and concurrency](#shared-mutable-state-and-concurrency)
25 * [The problem](#the-problem)
26 * [Volatiles are of no help](#volatiles-are-of-no-help)
27 * [Thread-safe data structures](#thread-safe-data-structures)
28 * [Thread confinement fine-grained](#thread-confinement-fine-grained)
29 * [Thread confinement coarse-grained](#thread-confinement-coarse-grained)
30 * [Mutual exclusion](#mutual-exclusion)
31 * [Actors](#actors)
32
33<!--- END_TOC -->
34
35## Shared mutable state and concurrency
36
37Coroutines can be executed concurrently using a multi-threaded dispatcher like the [Dispatchers.Default]. It presents
38all the usual concurrency problems. The main problem being synchronization of access to **shared mutable state**.
39Some solutions to this problem in the land of coroutines are similar to the solutions in the multi-threaded world,
40but others are unique.
41
42### The problem
43
44Let us launch a hundred coroutines all doing the same action thousand times.
45We'll also measure their completion time for further comparisons:
46
47<!--- INCLUDE .*/example-sync-03.kt
48import java.util.concurrent.atomic.*
49-->
50
51<!--- INCLUDE .*/example-sync-06.kt
Roman Elizarov0950dfa2018-07-13 10:33:25 +030052import kotlinx.coroutines.sync.*
hadihariri7db55532018-09-15 10:35:08 +020053-->
54
55<!--- INCLUDE .*/example-sync-07.kt
Roman Elizarov0950dfa2018-07-13 10:33:25 +030056import kotlinx.coroutines.channels.*
hadihariri7db55532018-09-15 10:35:08 +020057-->
58
59<!--- INCLUDE .*/example-sync-([0-9a-z]+).kt
60import kotlin.system.*
Roman Elizarov0950dfa2018-07-13 10:33:25 +030061import kotlin.coroutines.*
hadihariri7db55532018-09-15 10:35:08 +020062-->
63
Alexander Prendotacbeef102018-09-27 18:42:04 +030064<div class="sample" markdown="1" theme="idea" data-highlight-only>
65
hadihariri7db55532018-09-15 10:35:08 +020066```kotlin
67suspend fun CoroutineScope.massiveRun(action: suspend () -> Unit) {
68 val n = 100 // number of coroutines to launch
69 val k = 1000 // times an action is repeated by each coroutine
70 val time = measureTimeMillis {
71 val jobs = List(n) {
72 launch {
73 repeat(k) { action() }
74 }
75 }
76 jobs.forEach { it.join() }
77 }
78 println("Completed ${n * k} actions in $time ms")
79}
80```
81
Alexander Prendotacbeef102018-09-27 18:42:04 +030082</div>
83
hadihariri7db55532018-09-15 10:35:08 +020084<!--- INCLUDE .*/example-sync-([0-9a-z]+).kt -->
85
86We start with a very simple action that increments a shared mutable variable using
87multi-threaded [Dispatchers.Default] that is used in [GlobalScope].
88
Alexander Prendotacbeef102018-09-27 18:42:04 +030089<div class="sample" markdown="1" theme="idea" data-highlight-only>
90
hadihariri7db55532018-09-15 10:35:08 +020091```kotlin
92var counter = 0
93
94fun main(args: Array<String>) = runBlocking<Unit> {
95 GlobalScope.massiveRun {
96 counter++
97 }
98 println("Counter = $counter")
99}
100```
101
Alexander Prendotacbeef102018-09-27 18:42:04 +0300102</div>
103
hadihariri7db55532018-09-15 10:35:08 +0200104> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-sync-01.kt)
105
106<!--- TEST LINES_START
107Completed 100000 actions in
108Counter =
109-->
110
111What does it print at the end? It is highly unlikely to ever print "Counter = 100000", because a thousand coroutines
112increment the `counter` concurrently from multiple threads without any synchronization.
113
114> Note: if you have an old system with 2 or fewer CPUs, then you _will_ consistently see 100000, because
115the thread pool is running in only one thread in this case. To reproduce the problem you'll need to make the
116following change:
117
Alexander Prendotacbeef102018-09-27 18:42:04 +0300118<div class="sample" markdown="1" theme="idea" data-highlight-only>
119
hadihariri7db55532018-09-15 10:35:08 +0200120```kotlin
121val mtContext = newFixedThreadPoolContext(2, "mtPool") // explicitly define context with two threads
122var counter = 0
123
124fun main(args: Array<String>) = runBlocking<Unit> {
125 CoroutineScope(mtContext).massiveRun { // use it instead of Dispatchers.Default in this sample and below
126 counter++
127 }
128 println("Counter = $counter")
129}
130```
131
Alexander Prendotacbeef102018-09-27 18:42:04 +0300132</div>
133
hadihariri7db55532018-09-15 10:35:08 +0200134> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-sync-01b.kt)
135
136<!--- TEST LINES_START
137Completed 100000 actions in
138Counter =
139-->
140
141### Volatiles are of no help
142
143There is common misconception that making a variable `volatile` solves concurrency problem. Let us try it:
144
Alexander Prendotacbeef102018-09-27 18:42:04 +0300145<div class="sample" markdown="1" theme="idea" data-highlight-only>
146
hadihariri7db55532018-09-15 10:35:08 +0200147```kotlin
148@Volatile // in Kotlin `volatile` is an annotation
149var counter = 0
150
151fun main(args: Array<String>) = runBlocking<Unit> {
152 GlobalScope.massiveRun {
153 counter++
154 }
155 println("Counter = $counter")
156}
157```
158
Alexander Prendotacbeef102018-09-27 18:42:04 +0300159</div>
160
hadihariri7db55532018-09-15 10:35:08 +0200161> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-sync-02.kt)
162
163<!--- TEST LINES_START
164Completed 100000 actions in
165Counter =
166-->
167
168This code works slower, but we still don't get "Counter = 100000" at the end, because volatile variables guarantee
169linearizable (this is a technical term for "atomic") reads and writes to the corresponding variable, but
170do not provide atomicity of larger actions (increment in our case).
171
172### Thread-safe data structures
173
174The general solution that works both for threads and for coroutines is to use a thread-safe (aka synchronized,
175linearizable, or atomic) data structure that provides all the necessarily synchronization for the corresponding
176operations that needs to be performed on a shared state.
177In the case of a simple counter we can use `AtomicInteger` class which has atomic `incrementAndGet` operations:
178
Alexander Prendotacbeef102018-09-27 18:42:04 +0300179<div class="sample" markdown="1" theme="idea" data-highlight-only>
180
hadihariri7db55532018-09-15 10:35:08 +0200181```kotlin
182var counter = AtomicInteger()
183
184fun main(args: Array<String>) = runBlocking<Unit> {
185 GlobalScope.massiveRun {
186 counter.incrementAndGet()
187 }
188 println("Counter = ${counter.get()}")
189}
190```
191
Alexander Prendotacbeef102018-09-27 18:42:04 +0300192</div>
193
hadihariri7db55532018-09-15 10:35:08 +0200194> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-sync-03.kt)
195
196<!--- TEST ARBITRARY_TIME
197Completed 100000 actions in xxx ms
198Counter = 100000
199-->
200
201This is the fastest solution for this particular problem. It works for plain counters, collections, queues and other
202standard data structures and basic operations on them. However, it does not easily scale to complex
203state or to complex operations that do not have ready-to-use thread-safe implementations.
204
205### Thread confinement fine-grained
206
207_Thread confinement_ is an approach to the problem of shared mutable state where all access to the particular shared
208state is confined to a single thread. It is typically used in UI applications, where all UI state is confined to
209the single event-dispatch/application thread. It is easy to apply with coroutines by using a
210single-threaded context.
211
Alexander Prendotacbeef102018-09-27 18:42:04 +0300212<div class="sample" markdown="1" theme="idea" data-highlight-only>
213
hadihariri7db55532018-09-15 10:35:08 +0200214```kotlin
215val counterContext = newSingleThreadContext("CounterContext")
216var counter = 0
217
218fun main(args: Array<String>) = runBlocking<Unit> {
219 GlobalScope.massiveRun { // run each coroutine with DefaultDispathcer
220 withContext(counterContext) { // but confine each increment to the single-threaded context
221 counter++
222 }
223 }
224 println("Counter = $counter")
225}
226```
227
Alexander Prendotacbeef102018-09-27 18:42:04 +0300228</div>
229
hadihariri7db55532018-09-15 10:35:08 +0200230> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-sync-04.kt)
231
232<!--- TEST ARBITRARY_TIME
233Completed 100000 actions in xxx ms
234Counter = 100000
235-->
236
237This code works very slowly, because it does _fine-grained_ thread-confinement. Each individual increment switches
238from multi-threaded [Dispatchers.Default] context to the single-threaded context using [withContext] block.
239
240### Thread confinement coarse-grained
241
242In practice, thread confinement is performed in large chunks, e.g. big pieces of state-updating business logic
243are confined to the single thread. The following example does it like that, running each coroutine in
244the single-threaded context to start with.
245Here we use [CoroutineScope()] function to convert coroutine context reference to [CoroutineScope]:
246
Alexander Prendotacbeef102018-09-27 18:42:04 +0300247<div class="sample" markdown="1" theme="idea" data-highlight-only>
248
hadihariri7db55532018-09-15 10:35:08 +0200249```kotlin
250val counterContext = newSingleThreadContext("CounterContext")
251var counter = 0
252
253fun main(args: Array<String>) = runBlocking<Unit> {
254 CoroutineScope(counterContext).massiveRun { // run each coroutine in the single-threaded context
255 counter++
256 }
257 println("Counter = $counter")
258}
259```
260
Alexander Prendotacbeef102018-09-27 18:42:04 +0300261</div>
262
hadihariri7db55532018-09-15 10:35:08 +0200263> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-sync-05.kt)
264
265<!--- TEST ARBITRARY_TIME
266Completed 100000 actions in xxx ms
267Counter = 100000
268-->
269
270This now works much faster and produces correct result.
271
272### Mutual exclusion
273
274Mutual exclusion solution to the problem is to protect all modifications of the shared state with a _critical section_
275that is never executed concurrently. In a blocking world you'd typically use `synchronized` or `ReentrantLock` for that.
276Coroutine's alternative is called [Mutex]. It has [lock][Mutex.lock] and [unlock][Mutex.unlock] functions to
277delimit a critical section. The key difference is that `Mutex.lock()` is a suspending function. It does not block a thread.
278
279There is also [withLock] extension function that conveniently represents
280`mutex.lock(); try { ... } finally { mutex.unlock() }` pattern:
281
Alexander Prendotacbeef102018-09-27 18:42:04 +0300282<div class="sample" markdown="1" theme="idea" data-highlight-only>
283
hadihariri7db55532018-09-15 10:35:08 +0200284```kotlin
285val mutex = Mutex()
286var counter = 0
287
288fun main(args: Array<String>) = runBlocking<Unit> {
289 GlobalScope.massiveRun {
290 mutex.withLock {
291 counter++
292 }
293 }
294 println("Counter = $counter")
295}
296```
297
Alexander Prendotacbeef102018-09-27 18:42:04 +0300298</div>
299
hadihariri7db55532018-09-15 10:35:08 +0200300> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-sync-06.kt)
301
302<!--- TEST ARBITRARY_TIME
303Completed 100000 actions in xxx ms
304Counter = 100000
305-->
306
307The locking in this example is fine-grained, so it pays the price. However, it is a good choice for some situations
308where you absolutely must modify some shared state periodically, but there is no natural thread that this state
309is confined to.
310
311### Actors
312
313An [actor](https://en.wikipedia.org/wiki/Actor_model) is an entity made up of a combination of a coroutine, the state that is confined and encapsulated into this coroutine,
314and a channel to communicate with other coroutines. A simple actor can be written as a function,
315but an actor with a complex state is better suited for a class.
316
317There is an [actor] coroutine builder that conveniently combines actor's mailbox channel into its
318scope to receive messages from and combines the send channel into the resulting job object, so that a
319single reference to the actor can be carried around as its handle.
320
321The first step of using an actor is to define a class of messages that an actor is going to process.
322Kotlin's [sealed classes](https://kotlinlang.org/docs/reference/sealed-classes.html) are well suited for that purpose.
323We define `CounterMsg` sealed class with `IncCounter` message to increment a counter and `GetCounter` message
324to get its value. The later needs to send a response. A [CompletableDeferred] communication
325primitive, that represents a single value that will be known (communicated) in the future,
326is used here for that purpose.
327
Alexander Prendotacbeef102018-09-27 18:42:04 +0300328<div class="sample" markdown="1" theme="idea" data-highlight-only>
329
hadihariri7db55532018-09-15 10:35:08 +0200330```kotlin
331// Message types for counterActor
332sealed class CounterMsg
333object IncCounter : CounterMsg() // one-way message to increment counter
334class GetCounter(val response: CompletableDeferred<Int>) : CounterMsg() // a request with reply
335```
336
Alexander Prendotacbeef102018-09-27 18:42:04 +0300337</div>
338
hadihariri7db55532018-09-15 10:35:08 +0200339Then we define a function that launches an actor using an [actor] coroutine builder:
340
Alexander Prendotacbeef102018-09-27 18:42:04 +0300341<div class="sample" markdown="1" theme="idea" data-highlight-only>
342
hadihariri7db55532018-09-15 10:35:08 +0200343```kotlin
344// This function launches a new counter actor
345fun CoroutineScope.counterActor() = actor<CounterMsg> {
346 var counter = 0 // actor state
347 for (msg in channel) { // iterate over incoming messages
348 when (msg) {
349 is IncCounter -> counter++
350 is GetCounter -> msg.response.complete(counter)
351 }
352 }
353}
354```
355
Alexander Prendotacbeef102018-09-27 18:42:04 +0300356</div>
357
hadihariri7db55532018-09-15 10:35:08 +0200358The main code is straightforward:
359
Alexander Prendotacbeef102018-09-27 18:42:04 +0300360<div class="sample" markdown="1" theme="idea" data-highlight-only>
361
hadihariri7db55532018-09-15 10:35:08 +0200362```kotlin
363fun main(args: Array<String>) = runBlocking<Unit> {
364 val counter = counterActor() // create the actor
365 GlobalScope.massiveRun {
366 counter.send(IncCounter)
367 }
368 // send a message to get a counter value from an actor
369 val response = CompletableDeferred<Int>()
370 counter.send(GetCounter(response))
371 println("Counter = ${response.await()}")
372 counter.close() // shutdown the actor
373}
374```
375
Alexander Prendotacbeef102018-09-27 18:42:04 +0300376</div>
377
hadihariri7db55532018-09-15 10:35:08 +0200378> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-sync-07.kt)
379
380<!--- TEST ARBITRARY_TIME
381Completed 100000 actions in xxx ms
382Counter = 100000
383-->
384
385It does not matter (for correctness) what context the actor itself is executed in. An actor is
386a coroutine and a coroutine is executed sequentially, so confinement of the state to the specific coroutine
387works as a solution to the problem of shared mutable state. Indeed, actors may modify their own private state, but can only affect each other through messages (avoiding the need for any locks).
388
389Actor is more efficient than locking under load, because in this case it always has work to do and it does not
390have to switch to a different context at all.
391
392> Note, that an [actor] coroutine builder is a dual of [produce] coroutine builder. An actor is associated
393 with the channel that it receives messages from, while a producer is associated with the channel that it
394 sends elements to.
Roman Elizarov99c28aa2018-09-23 18:42:36 +0300395
396<!--- MODULE kotlinx-coroutines-core -->
Roman Elizarov0950dfa2018-07-13 10:33:25 +0300397<!--- INDEX kotlinx.coroutines -->
398[Dispatchers.Default]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html
399[GlobalScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/index.html
400[withContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-context.html
401[CoroutineScope()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope.html
402[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html
403[CompletableDeferred]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-completable-deferred/index.html
404<!--- INDEX kotlinx.coroutines.sync -->
405[Mutex]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/-mutex/index.html
406[Mutex.lock]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/-mutex/lock.html
407[Mutex.unlock]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/-mutex/unlock.html
408[withLock]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/with-lock.html
409<!--- INDEX kotlinx.coroutines.channels -->
410[actor]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/actor.html
411[produce]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/produce.html
Roman Elizarov99c28aa2018-09-23 18:42:36 +0300412<!--- END -->