diff --git a/CHANGES.md b/CHANGES.md
index e6dd931db3..2c1beec875 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,12 @@
# Change log for kotlinx.coroutines
+## Version 1.0.0
+
+ * All Kotlin dependencies updated to 1.3 release version.
+ * Fixed potential memory leak in `HandlerDispatcher.scheduleResumeAfterDelay`, thanks @cbeyls.
+ * `yield` support for `Unconfined` and immediate dispatchers (#737).
+ * Various documentation improvements.
+
## Version 1.0.0-RC1
* Coroutines API is updated to Kotlin 1.3.
diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md
index 1b393ee573..b7bcf0c4cf 100644
--- a/COMPATIBILITY.md
+++ b/COMPATIBILITY.md
@@ -20,6 +20,6 @@ In order to migrate `kotlinx.coroutines` to `1.0.0`, follow these steps:
1. Update `kotlinx.coroutines` to `0.30.2` version.
2. Inspect compiler warnings about deprecated API and migrate it to a proposed alternative. Most of deprecated API has a corresponding replacement which can be applied from IDEA with quickfix.
-3. Update Kotlin version to `1.3.0` or to the latest `1.3.0-rc` and `kotlinx.coroutines` to version `0.30.2-eap13`. Then just get rid of `experimental` suffix in all imports.
+3. Update Kotlin version to `1.3.0` and `kotlinx.coroutines` to version `0.30.2-eap13`. Then just get rid of `experimental` suffix in all imports.
4. Update `kotlinx.coroutines` to version `1.0.0` or to the corresponding release candidate of it).
\ No newline at end of file
diff --git a/README.md b/README.md
index 3856e6201a..8cc87c8062 100644
--- a/README.md
+++ b/README.md
@@ -2,10 +2,10 @@
[](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
[](http://www.apache.org/licenses/LICENSE-2.0)
-[ ](https://bintray.com/kotlin/kotlinx/kotlinx.coroutines/1.0.0-RC1)
+[ ](https://bintray.com/kotlin/kotlinx/kotlinx.coroutines/1.0.0)
Library support for Kotlin coroutines with [multiplatform](#multiplatform) support.
-This is a companion version for Kotlin `1.3.0-rc-146` release.
+This is a companion version for Kotlin `1.3.0` release.
**NOTE**: `0.30.2` was the last release with Kotlin 1.2 and experimental coroutines.
See [COMPATIBILITY.md](COMPATIBILITY.md) for details of migration onto the stable Kotlin 1.3 coroutines.
@@ -69,7 +69,7 @@ Add dependencies (you can also add other modules that you need):
org.jetbrains.kotlinx
kotlinx-coroutines-core
- 1.0.0-RC1
+ 1.0.0
```
@@ -77,19 +77,17 @@ And make sure that you use the latest Kotlin version:
```xml
- 1.3.0-rc-146
+ 1.3.0
```
-While Kotlin 1.3 is still in release candidate status, in order to depend on it you should add eap repository: `https://dl.bintray.com/kotlin/kotlin-eap`.
-
### Gradle
Add dependencies (you can also add other modules that you need):
```groovy
dependencies {
- implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.0-RC1'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.0'
}
```
@@ -97,7 +95,7 @@ And make sure that you use the latest Kotlin version:
```groovy
buildscript {
- ext.kotlin_version = '1.3.0-rc-146'
+ ext.kotlin_version = '1.3.0'
}
```
@@ -115,7 +113,7 @@ Add dependencies (you can also add other modules that you need):
```groovy
dependencies {
- implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.0-RC1")
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.0")
}
```
@@ -123,19 +121,11 @@ And make sure that you use the latest Kotlin version:
```groovy
plugins {
- kotlin("jvm") version "1.3.0-rc-146"
+ kotlin("jvm") version "1.3.0"
}
```
Make sure that you have either `jcenter()` or `mavenCentral()` in the list of repositories.
-For Kotlin EAP builds you also may need `kotlin-eap` repository:
-
-```
-repository {
- jcenter()
- maven { url "https://kotlin.bintray.com/kotlin-eap" }
-}
-```
### Multiplatform
@@ -151,7 +141,7 @@ Add [`kotlinx-coroutines-android`](ui/kotlinx-coroutines-android)
module as dependency when using `kotlinx.coroutines` on Android:
```groovy
-implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.0-RC1'
+implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.0'
```
This gives you access to Android [Dispatchers.Main](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-android/kotlinx.coroutines.android/kotlinx.coroutines.-dispatchers/index.html)
coroutine dispatcher and also makes sure that in case of crashed coroutine with unhandled exception this
diff --git a/build.gradle b/build.gradle
index ce77c0ad4a..9013e17cdc 100644
--- a/build.gradle
+++ b/build.gradle
@@ -52,8 +52,17 @@ allprojects {
kotlin_version = '1.2-SNAPSHOT'
}
+ def name = it.name
repositories {
+ /*
+ * google should be first in the repository list because some of the play services
+ * transitive dependencies was removed from jcenter, thus breaking gradle dependency resolution
+ */
+ if (name == "kotlinx-coroutines-play-services") {
+ google()
+ }
jcenter()
+ maven { url "https://kotlin.bintray.com/kotlin-dev" }
maven { url "https://kotlin.bintray.com/kotlin-eap" }
maven { url "https://kotlin.bintray.com/kotlinx" }
}
diff --git a/common/kotlinx-coroutines-core-common/src/Dispatched.kt b/common/kotlinx-coroutines-core-common/src/Dispatched.kt
index 32ee51f0a2..d7f7a97717 100644
--- a/common/kotlinx-coroutines-core-common/src/Dispatched.kt
+++ b/common/kotlinx-coroutines-core-common/src/Dispatched.kt
@@ -21,26 +21,40 @@ internal object UndispatchedEventLoop {
@JvmField
internal val threadLocalEventLoop = CommonThreadLocal { EventLoop() }
- inline fun execute(continuation: DispatchedContinuation<*>, contState: Any?, mode: Int, block: () -> Unit) {
+ /**
+ * Executes given [block] as part of current event loop, updating related to block [continuation]
+ * mode and state if continuation is not resumed immediately.
+ * [doYield] indicates whether current continuation is yielding (to provide fast-path if event-loop is empty).
+ * Returns `true` if execution of continuation was queued (trampolined) or `false` otherwise.
+ */
+ inline fun execute(continuation: DispatchedContinuation<*>, contState: Any?, mode: Int,
+ doYield: Boolean = false, block: () -> Unit) : Boolean {
val eventLoop = threadLocalEventLoop.get()
if (eventLoop.isActive) {
+ // If we are yielding and queue is empty, we can bail out as part of fast path
+ if (doYield && eventLoop.queue.isEmpty) {
+ return false
+ }
+
continuation._state = contState
continuation.resumeMode = mode
eventLoop.queue.addLast(continuation)
- return
+ return true
}
runEventLoop(eventLoop, block)
+ return false
}
- fun resumeUndispatched(task: DispatchedTask<*>) {
+ fun resumeUndispatched(task: DispatchedTask<*>): Boolean {
val eventLoop = threadLocalEventLoop.get()
if (eventLoop.isActive) {
eventLoop.queue.addLast(task)
- return
+ return true
}
runEventLoop(eventLoop, { task.resume(task.delegate, MODE_UNDISPATCHED) })
+ return false
}
inline fun runEventLoop(eventLoop: EventLoop, block: () -> Unit) {
@@ -227,6 +241,11 @@ internal interface DispatchedTask : Runnable {
}
}
+internal fun DispatchedContinuation.yieldUndispatched(): Boolean =
+ UndispatchedEventLoop.execute(this, Unit, MODE_CANCELLABLE, doYield = true) {
+ run()
+ }
+
internal fun DispatchedTask.dispatch(mode: Int = MODE_CANCELLABLE) {
val delegate = this.delegate
if (mode.isDispatchedMode && delegate is DispatchedContinuation<*> && mode.isCancellableMode == resumeMode.isCancellableMode) {
diff --git a/common/kotlinx-coroutines-core-common/src/Yield.kt b/common/kotlinx-coroutines-core-common/src/Yield.kt
index 632dcba0b0..78ab27fb87 100644
--- a/common/kotlinx-coroutines-core-common/src/Yield.kt
+++ b/common/kotlinx-coroutines-core-common/src/Yield.kt
@@ -19,7 +19,9 @@ public suspend fun yield(): Unit = suspendCoroutineUninterceptedOrReturn sc@ { u
val context = uCont.context
context.checkCompletion()
val cont = uCont.intercepted() as? DispatchedContinuation ?: return@sc Unit
- if (!cont.dispatcher.isDispatchNeeded(context)) return@sc Unit
+ if (!cont.dispatcher.isDispatchNeeded(context)) {
+ return@sc if (cont.yieldUndispatched()) COROUTINE_SUSPENDED else Unit
+ }
cont.dispatchYield(Unit)
COROUTINE_SUSPENDED
}
diff --git a/common/kotlinx-coroutines-core-common/src/channels/Channel.kt b/common/kotlinx-coroutines-core-common/src/channels/Channel.kt
index 81dc89c7ac..8cf8572aba 100644
--- a/common/kotlinx-coroutines-core-common/src/channels/Channel.kt
+++ b/common/kotlinx-coroutines-core-common/src/channels/Channel.kt
@@ -210,10 +210,9 @@ public interface ReceiveChannel {
* **Note: This is an obsolete api.**
* This function will be replaced with `receiveOrClosed: ReceiveResult` and
* extension `suspend fun ReceiveChannel.receiveOrNull(): E?`
+ * It is obsolete because it does not distinguish closed channel and null elements.
*/
- @ExperimentalCoroutinesApi
@ObsoleteCoroutinesApi
- @Deprecated(level = DeprecationLevel.WARNING, message = "This method does not distinguish closed channel and null elements")
public suspend fun receiveOrNull(): E?
/**
diff --git a/common/kotlinx-coroutines-core-common/src/internal/ArrayQueue.kt b/common/kotlinx-coroutines-core-common/src/internal/ArrayQueue.kt
index a6bf8f6180..5cfb8e8df1 100644
--- a/common/kotlinx-coroutines-core-common/src/internal/ArrayQueue.kt
+++ b/common/kotlinx-coroutines-core-common/src/internal/ArrayQueue.kt
@@ -8,6 +8,7 @@ internal class ArrayQueue {
private var elements = arrayOfNulls(16)
private var head = 0
private var tail = 0
+ val isEmpty: Boolean get() = head == tail
public fun addLast(element: T) {
elements[tail] = element
diff --git a/common/kotlinx-coroutines-core-common/test/UnconfinedTest.kt b/common/kotlinx-coroutines-core-common/test/UnconfinedTest.kt
index 8866057a09..f37c35657c 100644
--- a/common/kotlinx-coroutines-core-common/test/UnconfinedTest.kt
+++ b/common/kotlinx-coroutines-core-common/test/UnconfinedTest.kt
@@ -54,7 +54,7 @@ class UnconfinedTest : TestBase() {
}
@Test
- fun enterMultipleTimes() = runTest {
+ fun testEnterMultipleTimes() = runTest {
launch(Unconfined) {
expect(1)
}
@@ -70,5 +70,46 @@ class UnconfinedTest : TestBase() {
finish(4)
}
+ @Test
+ fun testYield() = runTest {
+ expect(1)
+ launch(Dispatchers.Unconfined) {
+ expect(2)
+ yield()
+ launch {
+ expect(4)
+ }
+ expect(3)
+ yield()
+ expect(5)
+ }.join()
+
+ finish(6)
+ }
+
+ @Test
+ fun testCancellationWihYields() = runTest {
+ expect(1)
+ GlobalScope.launch(Dispatchers.Unconfined) {
+ val job = coroutineContext[Job]!!
+ expect(2)
+ yield()
+ GlobalScope.launch(Dispatchers.Unconfined) {
+ expect(4)
+ job.cancel()
+ expect(5)
+ }
+ expect(3)
+
+ try {
+ yield()
+ } finally {
+ expect(6)
+ }
+ }
+
+ finish(7)
+ }
+
class TestException : Throwable()
}
diff --git a/core/kotlinx-coroutines-core/test/UnconfinedConcurrentStressTest.kt b/core/kotlinx-coroutines-core/test/UnconfinedConcurrentStressTest.kt
index 4fe1fd84aa..03088006f1 100644
--- a/core/kotlinx-coroutines-core/test/UnconfinedConcurrentStressTest.kt
+++ b/core/kotlinx-coroutines-core/test/UnconfinedConcurrentStressTest.kt
@@ -19,9 +19,9 @@ class UnconfinedConcurrentStressTest : TestBase() {
executor.close()
}
- @Test(timeout = 10_000L)
+ @Test
fun testConcurrent() = runTest {
- val iterations = 10_000 * stressTestMultiplier
+ val iterations = 1_000 * stressTestMultiplier
val startBarrier = CyclicBarrier(threads + 1)
val finishLatch = CountDownLatch(threads)
diff --git a/gradle.properties b/gradle.properties
index 775676ae54..9240b25cb7 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,12 +1,12 @@
# Kotlin
-version=1.0.0-RC1-SNAPSHOT
+version=1.0.0-SNAPSHOT
group=org.jetbrains.kotlinx
-kotlin_version=1.3.0-rc-146
-kotlin_native_version=1.3.0-rc-146
+kotlin_version=1.3.0
+kotlin_native_version=1.3.0-rc-208
# Dependencies
junit_version=4.12
-atomicFU_version=0.11.11
+atomicFU_version=0.11.12
html_version=0.6.8
lincheck_version=1.9
dokka_version=0.9.16-rdev-2-mpp-hacks
diff --git a/integration/kotlinx-coroutines-play-services/build.gradle b/integration/kotlinx-coroutines-play-services/build.gradle
index 44eec3bf2a..51cce3f278 100644
--- a/integration/kotlinx-coroutines-play-services/build.gradle
+++ b/integration/kotlinx-coroutines-play-services/build.gradle
@@ -9,10 +9,6 @@ import java.util.zip.ZipFile
ext.tasks_version = '15.0.1'
-repositories {
- google()
-}
-
def attr = Attribute.of("artifactType", String.class)
configurations {
aar {
diff --git a/native/README.md b/native/README.md
index ce0dfa135e..55a5019281 100644
--- a/native/README.md
+++ b/native/README.md
@@ -42,7 +42,7 @@ repositories {
}
dependencies {
- implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core-native:1.0.0-RC1'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core-native:1.0.0'
}
sourceSets {
diff --git a/ui/coroutines-guide-ui.md b/ui/coroutines-guide-ui.md
index c2c8232291..f8818cf7ff 100644
--- a/ui/coroutines-guide-ui.md
+++ b/ui/coroutines-guide-ui.md
@@ -165,7 +165,7 @@ Add dependencies on `kotlinx-coroutines-android` module to the `dependencies { .
`app/build.gradle` file:
```groovy
-compile "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.0-RC1"
+compile "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.0"
```
You can clone [kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) project from GitHub onto your
diff --git a/ui/kotlinx-coroutines-android/animation-app/gradle.properties b/ui/kotlinx-coroutines-android/animation-app/gradle.properties
index c76bcf9f19..1d37dbb738 100644
--- a/ui/kotlinx-coroutines-android/animation-app/gradle.properties
+++ b/ui/kotlinx-coroutines-android/animation-app/gradle.properties
@@ -18,6 +18,6 @@ org.gradle.jvmargs=-Xmx1536m
kotlin.coroutines=enable
-kotlin_version=1.3.0-rc-146
-coroutines_version=1.0.0-RC1
+kotlin_version=1.3.0
+coroutines_version=1.0.0
diff --git a/ui/kotlinx-coroutines-android/example-app/gradle.properties b/ui/kotlinx-coroutines-android/example-app/gradle.properties
index c76bcf9f19..1d37dbb738 100644
--- a/ui/kotlinx-coroutines-android/example-app/gradle.properties
+++ b/ui/kotlinx-coroutines-android/example-app/gradle.properties
@@ -18,6 +18,6 @@ org.gradle.jvmargs=-Xmx1536m
kotlin.coroutines=enable
-kotlin_version=1.3.0-rc-146
-coroutines_version=1.0.0-RC1
+kotlin_version=1.3.0
+coroutines_version=1.0.0
diff --git a/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt b/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt
index 2181b1cda4..be5185df83 100644
--- a/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt
+++ b/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt
@@ -120,9 +120,11 @@ internal class HandlerContext private constructor(
}
override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation) {
- handler.postDelayed({
+ val block = Runnable {
with(continuation) { resumeUndispatched(Unit) }
- }, timeMillis.coerceAtMost(MAX_DELAY))
+ }
+ handler.postDelayed(block, timeMillis.coerceAtMost(MAX_DELAY))
+ continuation.invokeOnCancellation { handler.removeCallbacks(block) }
}
override fun invokeOnTimeout(timeMillis: Long, block: Runnable): DisposableHandle {