Skip to content

Commit 0dffcfd

Browse files
committed
Merge 'develop' into project-structure
# Conflicts: # reactive/kotlinx-coroutines-rx2/src/test/kotlin/guide/test/GuideReactiveTest.kt
2 parents a9687a3 + 167ca63 commit 0dffcfd

File tree

22 files changed

+237
-64
lines changed

22 files changed

+237
-64
lines changed

CHANGES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Change log for kotlinx.coroutines
22

3+
## Version 0.23.4
4+
5+
* Recompiled with Kotlin 1.2.51 to solve broken metadata problem (see [KT-24944](https://youtrack.jetbrains.com/issue/KT-24944)).
6+
37
## Version 0.23.3
48

59
* Kotlin 1.2.50.

README.md

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
[![official JetBrains project](http://jb.gg/badges/official.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
44
[![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0)
5-
[![Download](https://api.bintray.com/packages/kotlin/kotlinx/kotlinx.coroutines/images/download.svg?version=0.23.3) ](https://bintray.com/kotlin/kotlinx/kotlinx.coroutines/0.23.3)
5+
[![Download](https://api.bintray.com/packages/kotlin/kotlinx/kotlinx.coroutines/images/download.svg?version=0.23.4) ](https://bintray.com/kotlin/kotlinx/kotlinx.coroutines/0.23.4)
66

77
Library support for Kotlin coroutines in
88
[Kotlin/JVM](core/README.md) and
99
[Kotlin/JS](js/README.md).
10-
This is a companion version for Kotlin 1.2.50 release.
10+
This is a companion version for Kotlin 1.2.51 release.
1111

1212
```kotlin
1313
launch {
@@ -64,15 +64,15 @@ Add dependencies (you can also add other modules that you need):
6464
<dependency>
6565
<groupId>org.jetbrains.kotlinx</groupId>
6666
<artifactId>kotlinx-coroutines-core</artifactId>
67-
<version>0.23.3</version>
67+
<version>0.23.4</version>
6868
</dependency>
6969
```
7070

7171
And make sure that you use the latest Kotlin version:
7272

7373
```xml
7474
<properties>
75-
<kotlin.version>1.2.50</kotlin.version>
75+
<kotlin.version>1.2.51</kotlin.version>
7676
</properties>
7777
```
7878

@@ -81,14 +81,14 @@ And make sure that you use the latest Kotlin version:
8181
Add dependencies (you can also add other modules that you need):
8282

8383
```groovy
84-
compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.23.3'
84+
compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.23.4'
8585
```
8686

8787
And make sure that you use the latest Kotlin version:
8888

8989
```groovy
9090
buildscript {
91-
ext.kotlin_version = '1.2.50'
91+
ext.kotlin_version = '1.2.51'
9292
}
9393
```
9494

@@ -105,6 +105,20 @@ repository {
105105
Use `org.jetbrains.kotlinx:kotlinx-coroutines-core-js:<version>` artifact in your Gradle/Maven dependencies
106106
or install [`kotlinx-coroutines-core`](https://www.npmjs.com/package/kotlinx-coroutines-core) package via NPM.
107107

108+
### Android
109+
110+
Add [`kotlinx-coroutines-android`](ui/kotlinx-coroutines-android)
111+
module as dependency when using `kotlinx.coroutines` on Android:
112+
113+
```groovy
114+
compile 'org.jetbrains.kotlinx:kotlinx-coroutines-android:0.23.4'
115+
```
116+
117+
This gives you access to Android [UI](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-android/kotlinx.coroutines.experimental.android/-u-i.html)
118+
coroutine dispatcher and also makes sure that in case of crashed coroutine with unhandled exception this
119+
exception is logged before crashing Android application, similarly to the way uncaught exceptions in
120+
threads are handled by Android runtime.
121+
108122
### ProGuard
109123

110124
In obfuscated code, fields with different types can have the same names,

build.gradle

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ allprojects {
4141
if (useKotlinSnapshot) {
4242
kotlin_version = '1.2-SNAPSHOT'
4343
}
44+
45+
repositories {
46+
jcenter()
47+
maven { url "https://kotlin.bintray.com/kotlin-dev" }
48+
maven { url "https://kotlin.bintray.com/kotlinx" }
49+
}
4450
}
4551

4652

common/kotlinx-coroutines-core-common/src/channels/ArrayBroadcastChannel.kt

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,14 @@ class ArrayBroadcastChannel<E>(
4444
require(capacity >= 1) { "ArrayBroadcastChannel capacity must be at least 1, but $capacity was specified" }
4545
}
4646

47+
/*
48+
* Writes to buffer are guarded by bufferLock, but reads from buffer are concurrent with writes
49+
* - Write element to buffer then write "tail" (volatile)
50+
* - Read "tail" (volatile), then read element from buffer
51+
* So read/writes to buffer need not be volatile
52+
*/
4753
private val bufferLock = ReentrantLock()
48-
private val buffer = arrayOfNulls<Any?>(capacity) // guarded by bufferLock
54+
private val buffer = arrayOfNulls<Any?>(capacity)
4955

5056
// head & tail are Long (64 bits) and we assume that they never wrap around
5157
// head, tail, and size are guarded by bufferLock
@@ -56,14 +62,7 @@ class ArrayBroadcastChannel<E>(
5662
@Volatile
5763
private var size: Int = 0
5864

59-
/*
60-
Writes to buffer are guarded by bufferLock, but reads from buffer are concurrent with writes
61-
- Write element to buffer then write "tail" (volatile)
62-
- Read "tail" (volatile), then read element from buffer
63-
So read/writes to buffer need not be volatile
64-
*/
65-
66-
private val subs = subscriberList<Subscriber<E>>()
65+
private val subscribers = subscriberList<Subscriber<E>>()
6766

6867
override val isBufferAlwaysFull: Boolean get() = false
6968
override val isBufferFull: Boolean get() = size >= capacity
@@ -81,7 +80,7 @@ class ArrayBroadcastChannel<E>(
8180

8281
public override fun cancel(cause: Throwable?): Boolean =
8382
close(cause).also {
84-
for (sub in subs) sub.cancel(cause)
83+
for (sub in subscribers) sub.cancel(cause)
8584
}
8685

8786
// result is `OFFER_SUCCESS | OFFER_FAILED | Closed`
@@ -96,7 +95,7 @@ class ArrayBroadcastChannel<E>(
9695
this.size = size + 1
9796
this.tail = tail + 1
9897
}
99-
// if offered successfully, then check subs outside of lock
98+
// if offered successfully, then check subscribers outside of lock
10099
checkSubOffers()
101100
return OFFER_SUCCESS
102101
}
@@ -117,7 +116,7 @@ class ArrayBroadcastChannel<E>(
117116
this.size = size + 1
118117
this.tail = tail + 1
119118
}
120-
// if offered successfully, then check subs outside of lock
119+
// if offered successfully, then check subscribers outside of lock
121120
checkSubOffers()
122121
return OFFER_SUCCESS
123122
}
@@ -126,7 +125,7 @@ class ArrayBroadcastChannel<E>(
126125
var updated = false
127126
var hasSubs = false
128127
@Suppress("LoopToCallChain") // must invoke `checkOffer` on every sub
129-
for (sub in subs) {
128+
for (sub in subscribers) {
130129
hasSubs = true
131130
if (sub.checkOffer()) updated = true
132131
}
@@ -142,12 +141,12 @@ class ArrayBroadcastChannel<E>(
142141
bufferLock.withLock {
143142
if (addSub != null) {
144143
addSub.subHead = tail // start from last element
145-
val wasEmpty = subs.isEmpty()
146-
subs.add(addSub)
144+
val wasEmpty = subscribers.isEmpty()
145+
subscribers.add(addSub)
147146
if (!wasEmpty) return // no need to update when adding second and etc sub
148147
}
149148
if (removeSub != null) {
150-
subs.remove(removeSub)
149+
subscribers.remove(removeSub)
151150
if (head != removeSub.subHead) return // no need to update
152151
}
153152
val minHead = computeMinHead()
@@ -190,7 +189,7 @@ class ArrayBroadcastChannel<E>(
190189

191190
private fun computeMinHead(): Long {
192191
var minHead = Long.MAX_VALUE
193-
for (sub in subs)
192+
for (sub in subscribers)
194193
minHead = minHead.coerceAtMost(sub.subHead) // volatile (atomic) reads of subHead
195194
return minHead
196195
}

common/kotlinx-coroutines-core-common/test/channels/ArrayBroadcastChannelTest.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,30 @@ import kotlin.coroutines.experimental.*
2121
import kotlin.test.*
2222

2323
class ArrayBroadcastChannelTest : TestBase() {
24+
25+
@Test
26+
fun testConcurrentModification() = runTest {
27+
val channel = ArrayBroadcastChannel<Int>(1)
28+
val s1 = channel.openSubscription()
29+
val s2 = channel.openSubscription()
30+
31+
val job1 = launch(Unconfined, CoroutineStart.UNDISPATCHED) {
32+
expect(1)
33+
s1.receive()
34+
s1.cancel()
35+
}
36+
37+
val job2 = launch(Unconfined, CoroutineStart.UNDISPATCHED) {
38+
expect(2)
39+
s2.receive()
40+
}
41+
42+
expect(3)
43+
channel.send(1)
44+
joinAll(job1, job2)
45+
finish(4)
46+
}
47+
2448
@Test
2549
fun testBasic() = runTest {
2650
expect(1)

common/kotlinx-coroutines-core-common/test/channels/ConflatedBroadcastChannelTest.kt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,29 @@ import kotlin.test.*
2222

2323
class ConflatedBroadcastChannelTest : TestBase() {
2424

25+
@Test
26+
fun testConcurrentModification() = runTest {
27+
val channel = ConflatedBroadcastChannel<Int>()
28+
val s1 = channel.openSubscription()
29+
val s2 = channel.openSubscription()
30+
31+
val job1 = launch(Unconfined, CoroutineStart.UNDISPATCHED) {
32+
expect(1)
33+
s1.receive()
34+
s1.cancel()
35+
}
36+
37+
val job2 = launch(Unconfined, CoroutineStart.UNDISPATCHED) {
38+
expect(2)
39+
s2.receive()
40+
}
41+
42+
expect(3)
43+
channel.send(1)
44+
joinAll(job1, job2)
45+
finish(4)
46+
}
47+
2548
@Test
2649
fun testBasicScenario() = runTest {
2750
expect(1)

core/kotlinx-coroutines-core/test/guide/example-channel-06.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ fun produceNumbers() = produce<Int> {
2929
}
3030

3131
fun launchProcessor(id: Int, channel: ReceiveChannel<Int>) = launch {
32-
channel.consumeEach {
33-
println("Processor #$id received $it")
32+
for (msg in channel) {
33+
println("Processor #$id received $msg")
3434
}
3535
}
3636

coroutines-guide.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1531,8 +1531,8 @@ received number:
15311531

15321532
```kotlin
15331533
fun launchProcessor(id: Int, channel: ReceiveChannel<Int>) = launch {
1534-
channel.consumeEach {
1535-
println("Processor #$id received $it")
1534+
for (msg in channel) {
1535+
println("Processor #$id received $msg")
15361536
}
15371537
}
15381538
```
@@ -1571,6 +1571,11 @@ Processor #3 received 10
15711571
Note, that cancelling a producer coroutine closes its channel, thus eventually terminating iteration
15721572
over the channel that processor coroutines are doing.
15731573

1574+
Also, pay attention to how we explicitly iterate over channel with `for` loop to perform fan-out in `launchProcessor` code.
1575+
Unlike `consumeEach`, this `for` loop pattern is perfectly safe to use from multiple coroutines. If one of the processor
1576+
coroutines fails, then others would still be processing the channel, while a processor that is written via `consumeEach`
1577+
always consumes (cancels) the underlying channel on its normal or abnormal termination.
1578+
15741579
### Fan-in
15751580

15761581
Multiple coroutines may send to the same channel.

gradle.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
version = 0.23.3-SNAPSHOT
1+
version = 0.23.4-SNAPSHOT
22
group = org.jetbrains.kotlinx
33

4-
kotlin_version = 1.2.50
4+
kotlin_version = 1.2.51
55
kotlin_native_version = 0.7-dev-1436
66
junit_version = 4.12
77
atomicFU_version = 0.10.3

gradle/compile-common.gradle

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,3 @@ dependencies {
1010
testCompile "org.jetbrains.kotlin:kotlin-test-common:$kotlin_version"
1111
testCompile "org.jetbrains.kotlin:kotlin-test-annotations-common:$kotlin_version"
1212
}
13-
14-
repositories {
15-
jcenter()
16-
maven { url "https://kotlin.bintray.com/kotlinx" }
17-
}

0 commit comments

Comments
 (0)