Skip to content

Commit 540d862

Browse files
authored
Merge pull request #2708 from Kotlin/version-1.5.0
Version 1.5.0
2 parents 0fcd1d8 + 894df7d commit 540d862

File tree

61 files changed

+349
-225
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+349
-225
lines changed

CHANGES.md

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

3+
## Version 1.5.0
4+
5+
Note that this is a full changelog relative to 1.4.3 version. Changelog relative to 1.5.0-RC can be found in the end.
6+
7+
### Channels API
8+
9+
* Major channels API rework (#330, #974). Existing `offer`, `poll`, and `sendBlocking` methods are deprecated, internal `receiveCatching` and `onReceiveCatching` removed, `receiveOrNull` and `onReceiveOrNull` are completely deprecated. Previously deprecated `SendChannel.isFull` declaration is removed. Channel operators deprecated with `ERROR` are now `HIDDEN`.
10+
* New methods `receiveCatching`, `onReceiveCatching` `trySend`, `tryReceive`, and `trySendBlocking` along with the new result type `ChannelResult` are introduced. They provide better type safety, are less error-prone, and have a consistent future-proof naming scheme. The full rationale behind this change can be found [here](https://github.com/Kotlin/kotlinx.coroutines/issues/974#issuecomment-806569582).
11+
* `BroadcastChannel` and `ConflatedBroadcastChannel` are marked as `ObsoleteCoroutinesApi` in the favor or `SharedFlow` and `StateFlow`. The migration scheme can be found in their documentation. These classes will be deprecated in the next major release.
12+
* `callbackFlow` and `channelFlow` are promoted to stable API.
13+
14+
### Reactive integrations
15+
16+
* All existing API in modules `kotlinx-coroutines-rx2`, `kotlinx-coroutines-rx3`, `kotlinx-coroutines-reactive`, `kotlinx-coroutines-reactor`, and `kotlinx-coroutines-jdk9` were revisited and promoted to stable (#2545).
17+
* `publish` is no longer allowed to emit `null` values (#2646).
18+
* Misleading `awaitSingleOr*` functions on `Publisher` type are deprecated (#2591).
19+
* `MaybeSource.await` is deprecated in the favor of `awaitSingle`, additional lint functions for `Mono` are added in order to prevent ambiguous `Publisher` usages (#2628, #1587).
20+
* `ContextView` support in `kotlinx-coroutines-reactor` (#2575).
21+
* All reactive builders no longer ignore inner cancellation exceptions preventing their completion (#2262, #2646).
22+
* `MaybeSource.collect` and `Maybe.collect` properly finish when they are completed without a value (#2617).
23+
* All exceptions are now consistently handled according to reactive specification, whether they are considered 'fatal' or not by reactive frameworks (#2646).
24+
25+
### Other improvements
26+
27+
* `Flow.last` and `Flow.lastOrNull` operators (#2246).
28+
* `Flow.runningFold` operator (#2641).
29+
* `CoroutinesTimeout` rule for JUnit5 (#2197).
30+
* Internals of `Job` and `AbstractCoroutine` was reworked, resulting in smaller code size, less memory footprint, and better performance (#2513, #2512).
31+
* `CancellationException` from Kotlin standard library is used for cancellation on Koltin/JS and Kotlin/Native (#2638).
32+
* Introduced new `DelicateCoroutineApi` annotation that warns users about potential target API pitfalls and suggests studying API's documentation first. The only delicate API right now is `GlobalScope` (#2637).
33+
* Fixed bug introduced in `1.4.3` when `kotlinx-coroutines-core.jar` triggered IDEA debugger failure (#2619).
34+
* Fixed memory leak of `ChildHandlerNode` with reusable continuations (#2564).
35+
* Various documentation improvements (#2555, #2589, #2592, #2583, #2437, #2616, #2633, #2560).
36+
37+
### Changelog relative to version 1.5.0-RC
38+
39+
* Fail-fast during `emitAll` called from cancelled `onCompletion` operator (#2700).
40+
* Flows returned by `stateIn`/`shareIn` keep strong reference to sharing job (#2557).
41+
* Rename internal `TimeSource` to `AbstractTimeSource` due to import issues (#2691).
42+
* Reverted the change that triggered IDEA coroutines debugger crash (#2695, reverted #2291).
43+
* `watchosX64` target support for Kotlin/Native (#2524).
44+
* Various documentation fixes and improvements.
45+
346
## Version 1.5.0-RC
447

548
### Channels API

README.md

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

33
[![official JetBrains project](https://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)](https://www.apache.org/licenses/LICENSE-2.0)
5-
[![Download](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.5.0-RC)](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.5.0-RC/pom)
5+
[![Download](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.5.0)](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.5.0/pom)
66
[![Kotlin](https://img.shields.io/badge/kotlin-1.5.0-blue.svg?logo=kotlin)](http://kotlinlang.org)
77
[![Slack channel](https://img.shields.io/badge/chat-slack-green.svg?logo=slack)](https://kotlinlang.slack.com/messages/coroutines/)
88

@@ -83,7 +83,7 @@ Add dependencies (you can also add other modules that you need):
8383
<dependency>
8484
<groupId>org.jetbrains.kotlinx</groupId>
8585
<artifactId>kotlinx-coroutines-core</artifactId>
86-
<version>1.5.0-RC</version>
86+
<version>1.5.0</version>
8787
</dependency>
8888
```
8989

@@ -101,7 +101,7 @@ Add dependencies (you can also add other modules that you need):
101101

102102
```groovy
103103
dependencies {
104-
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-RC'
104+
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
105105
}
106106
```
107107

@@ -127,7 +127,7 @@ Add dependencies (you can also add other modules that you need):
127127

128128
```groovy
129129
dependencies {
130-
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-RC")
130+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0")
131131
}
132132
```
133133

@@ -147,7 +147,7 @@ Add [`kotlinx-coroutines-android`](ui/kotlinx-coroutines-android)
147147
module as a dependency when using `kotlinx.coroutines` on Android:
148148

149149
```groovy
150-
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0-RC'
150+
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0'
151151
```
152152

153153
This gives you access to the Android [Dispatchers.Main]
@@ -180,7 +180,7 @@ In common code that should get compiled for different platforms, you can add a d
180180
```groovy
181181
commonMain {
182182
dependencies {
183-
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-RC")
183+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0")
184184
}
185185
}
186186
```
@@ -192,7 +192,7 @@ Platform-specific dependencies are recommended to be used only for non-multiplat
192192
#### JS
193193

194194
Kotlin/JS version of `kotlinx.coroutines` is published as
195-
[`kotlinx-coroutines-core-js`](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core-js/1.5.0-RC/jar)
195+
[`kotlinx-coroutines-core-js`](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core-js/1.5.0/jar)
196196
(follow the link to get the dependency declaration snippet) and as [`kotlinx-coroutines-core`](https://www.npmjs.com/package/kotlinx-coroutines-core) NPM package.
197197

198198
#### Native

docs/images/after.png

134 KB
Loading

docs/images/before.png

61.8 KB
Loading

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#
44

55
# Kotlin
6-
version=1.5.0-RC-SNAPSHOT
6+
version=1.5.0-SNAPSHOT
77
group=org.jetbrains.kotlinx
88
kotlin_version=1.5.0
99

gradle/compile-native-multiplatform.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ kotlin {
2424
addTarget(presets.watchosArm32)
2525
addTarget(presets.watchosArm64)
2626
addTarget(presets.watchosX86)
27+
addTarget(presets.watchosX64)
2728
}
2829

2930
sourceSets {

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

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -302,9 +302,18 @@ public interface ReceiveChannel<out E> {
302302
* * Its name was not aligned with the rest of the API and tried to mimic Java's queue instead.
303303
*
304304
* See https://github.com/Kotlin/kotlinx.coroutines/issues/974 for more context.
305+
*
306+
* ### Replacement note
307+
*
308+
* The replacement `tryReceive().getOrNull()` is a default that ignores all close exceptions and
309+
* proceeds with `null`, while `poll` throws an exception if the channel was closed with an exception.
310+
* Replacement with the very same 'poll' semantics is `tryReceive().onClosed { if (it != null) throw it }.getOrNull()`
305311
*/
306-
@Deprecated(level = DeprecationLevel.WARNING,
307-
message = "Deprecated in the favour of 'tryReceive'",
312+
@Deprecated(
313+
level = DeprecationLevel.WARNING,
314+
message = "Deprecated in the favour of 'tryReceive'. " +
315+
"Please note that the provided replacement does not rethrow channel's close cause as 'poll' did, " +
316+
"for the precise replacement please refer to the 'poll' documentation",
308317
replaceWith = ReplaceWith("tryReceive().getOrNull()")
309318
) // Warning since 1.5.0
310319
public fun poll(): E? {
@@ -322,12 +331,18 @@ public interface ReceiveChannel<out E> {
322331
* - Was throwing if the channel has failed even though its signature may suggest it returns 'null'
323332
* - It didn't really belong to core channel API and can be exposed as an extension instead.
324333
*
325-
* @suppress doc
334+
* ### Replacement note
335+
*
336+
* The replacement `receiveCatching().getOrNull()` is a safe default that ignores all close exceptions and
337+
* proceeds with `null`, while `receiveOrNull` throws an exception if the channel was closed with an exception.
338+
* Replacement with the very same `receiveOrNull` semantics is `receiveCatching().onClosed { if (it != null) throw it }.getOrNull()`.
326339
*/
327340
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
328341
@LowPriorityInOverloadResolution
329342
@Deprecated(
330-
message = "Deprecated in favor of receiveCatching",
343+
message = "Deprecated in favor of 'receiveCatching'. " +
344+
"Please note that the provided replacement does not rethrow channel's close cause as 'receiveOrNull' did, " +
345+
"for the detailed replacement please refer to the 'receiveOrNull' documentation",
331346
level = DeprecationLevel.ERROR,
332347
replaceWith = ReplaceWith("receiveCatching().getOrNull()")
333348
) // Warning since 1.3.0, error in 1.5.0, will be hidden in 1.6.0

kotlinx-coroutines-core/common/src/flow/Channels.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public suspend fun <T> FlowCollector<T>.emitAll(channel: ReceiveChannel<T>): Uni
3030
emitAllImpl(channel, consume = true)
3131

3232
private suspend fun <T> FlowCollector<T>.emitAllImpl(channel: ReceiveChannel<T>, consume: Boolean) {
33+
ensureActive()
3334
// Manually inlined "consumeEach" implementation that does not use iterator but works via "receiveCatching".
3435
// It has smaller and more efficient spilled state which also allows to implement a manual kludge to
3536
// fix retention of the last emitted value.

kotlinx-coroutines-core/common/src/flow/operators/Emitters.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,15 @@ public fun <T> Flow<T>.onEmpty(
194194
}
195195
}
196196

197-
private class ThrowingCollector(private val e: Throwable) : FlowCollector<Any?> {
197+
/*
198+
* 'emitAll' methods call this to fail-fast before starting to collect
199+
* their sources (that may not have any elements for a long time).
200+
*/
201+
internal fun FlowCollector<*>.ensureActive() {
202+
if (this is ThrowingCollector) throw e
203+
}
204+
205+
internal class ThrowingCollector(@JvmField val e: Throwable) : FlowCollector<Any?> {
198206
override suspend fun emit(value: Any?) {
199207
throw e
200208
}

kotlinx-coroutines-core/common/src/flow/operators/Share.kt

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,8 @@ public fun <T> Flow<T>.shareIn(
144144
onBufferOverflow = config.onBufferOverflow
145145
)
146146
@Suppress("UNCHECKED_CAST")
147-
scope.launchSharing(config.context, config.upstream, shared, started, NO_VALUE as T)
148-
return shared.asSharedFlow()
147+
val job = scope.launchSharing(config.context, config.upstream, shared, started, NO_VALUE as T)
148+
return ReadonlySharedFlow(shared, job)
149149
}
150150

151151
private class SharingConfig<T>(
@@ -197,7 +197,7 @@ private fun <T> CoroutineScope.launchSharing(
197197
shared: MutableSharedFlow<T>,
198198
started: SharingStarted,
199199
initialValue: T
200-
) {
200+
): Job =
201201
launch(context) { // the single coroutine to rule the sharing
202202
// Optimize common built-in started strategies
203203
when {
@@ -230,7 +230,6 @@ private fun <T> CoroutineScope.launchSharing(
230230
}
231231
}
232232
}
233-
}
234233

235234
// -------------------------------- stateIn --------------------------------
236235

@@ -303,8 +302,8 @@ public fun <T> Flow<T>.stateIn(
303302
): StateFlow<T> {
304303
val config = configureSharing(1)
305304
val state = MutableStateFlow(initialValue)
306-
scope.launchSharing(config.context, config.upstream, state, started, initialValue)
307-
return state.asStateFlow()
305+
val job = scope.launchSharing(config.context, config.upstream, state, started, initialValue)
306+
return ReadonlyStateFlow(state, job)
308307
}
309308

310309
/**
@@ -332,7 +331,7 @@ private fun <T> CoroutineScope.launchSharingDeferred(
332331
upstream.collect { value ->
333332
state?.let { it.value = value } ?: run {
334333
state = MutableStateFlow(value).also {
335-
result.complete(it.asStateFlow())
334+
result.complete(ReadonlyStateFlow(it, coroutineContext.job))
336335
}
337336
}
338337
}
@@ -351,23 +350,27 @@ private fun <T> CoroutineScope.launchSharingDeferred(
351350
* Represents this mutable shared flow as a read-only shared flow.
352351
*/
353352
public fun <T> MutableSharedFlow<T>.asSharedFlow(): SharedFlow<T> =
354-
ReadonlySharedFlow(this)
353+
ReadonlySharedFlow(this, null)
355354

356355
/**
357356
* Represents this mutable state flow as a read-only state flow.
358357
*/
359358
public fun <T> MutableStateFlow<T>.asStateFlow(): StateFlow<T> =
360-
ReadonlyStateFlow(this)
359+
ReadonlyStateFlow(this, null)
361360

362361
private class ReadonlySharedFlow<T>(
363-
flow: SharedFlow<T>
362+
flow: SharedFlow<T>,
363+
@Suppress("unused")
364+
private val job: Job? // keeps a strong reference to the job (if present)
364365
) : SharedFlow<T> by flow, CancellableFlow<T>, FusibleFlow<T> {
365366
override fun fuse(context: CoroutineContext, capacity: Int, onBufferOverflow: BufferOverflow) =
366367
fuseSharedFlow(context, capacity, onBufferOverflow)
367368
}
368369

369370
private class ReadonlyStateFlow<T>(
370-
flow: StateFlow<T>
371+
flow: StateFlow<T>,
372+
@Suppress("unused")
373+
private val job: Job? // keeps a strong reference to the job (if present)
371374
) : StateFlow<T> by flow, CancellableFlow<T>, FusibleFlow<T> {
372375
override fun fuse(context: CoroutineContext, capacity: Int, onBufferOverflow: BufferOverflow) =
373376
fuseStateFlow(context, capacity, onBufferOverflow)

kotlinx-coroutines-core/common/src/flow/terminal/Collect.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,5 +127,7 @@ public suspend fun <T> Flow<T>.collectLatest(action: suspend (value: T) -> Unit)
127127
* Collects all the values from the given [flow] and emits them to the collector.
128128
* It is a shorthand for `flow.collect { value -> emit(value) }`.
129129
*/
130-
@BuilderInference
131-
public suspend inline fun <T> FlowCollector<T>.emitAll(flow: Flow<T>): Unit = flow.collect(this)
130+
public suspend fun <T> FlowCollector<T>.emitAll(flow: Flow<T>) {
131+
ensureActive()
132+
flow.collect(this)
133+
}

kotlinx-coroutines-core/common/test/flow/operators/OnCompletionTest.kt

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
/*
2-
* Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
2+
* Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

55
package kotlinx.coroutines.flow
66

77
import kotlinx.coroutines.*
8+
import kotlinx.coroutines.channels.*
89
import kotlinx.coroutines.flow.internal.*
910
import kotlin.test.*
1011

@@ -290,4 +291,24 @@ class OnCompletionTest : TestBase() {
290291
val expected = (1..5).toList() + (-1)
291292
assertEquals(expected, result)
292293
}
294+
295+
@Test
296+
fun testCancelledEmitAllFlow() = runTest {
297+
// emitAll does not call 'collect' on onCompletion collector
298+
// if the target flow is empty
299+
flowOf(1, 2, 3)
300+
.onCompletion { emitAll(MutableSharedFlow()) }
301+
.take(1)
302+
.collect()
303+
}
304+
305+
@Test
306+
fun testCancelledEmitAllChannel() = runTest {
307+
// emitAll does not call 'collect' on onCompletion collector
308+
// if the target channel is empty
309+
flowOf(1, 2, 3)
310+
.onCompletion { emitAll(Channel()) }
311+
.take(1)
312+
.collect()
313+
}
293314
}

kotlinx-coroutines-core/jvm/src/TimeSource.kt renamed to kotlinx-coroutines-core/jvm/src/AbstractTimeSource.kt

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,21 @@ package kotlinx.coroutines
1010
import java.util.concurrent.locks.*
1111
import kotlin.internal.InlineOnly
1212

13-
internal interface TimeSource {
14-
fun currentTimeMillis(): Long
15-
fun nanoTime(): Long
16-
fun wrapTask(block: Runnable): Runnable
17-
fun trackTask()
18-
fun unTrackTask()
19-
fun registerTimeLoopThread()
20-
fun unregisterTimeLoopThread()
21-
fun parkNanos(blocker: Any, nanos: Long) // should return immediately when nanos <= 0
22-
fun unpark(thread: Thread)
13+
internal abstract class AbstractTimeSource {
14+
abstract fun currentTimeMillis(): Long
15+
abstract fun nanoTime(): Long
16+
abstract fun wrapTask(block: Runnable): Runnable
17+
abstract fun trackTask()
18+
abstract fun unTrackTask()
19+
abstract fun registerTimeLoopThread()
20+
abstract fun unregisterTimeLoopThread()
21+
abstract fun parkNanos(blocker: Any, nanos: Long) // should return immediately when nanos <= 0
22+
abstract fun unpark(thread: Thread)
2323
}
2424

2525
// For tests only
2626
// @JvmField: Don't use JvmField here to enable R8 optimizations via "assumenosideeffects"
27-
internal var timeSource: TimeSource? = null
27+
internal var timeSource: AbstractTimeSource? = null
2828

2929
@InlineOnly
3030
internal inline fun currentTimeMillis(): Long =

0 commit comments

Comments
 (0)