Minimize cut-and-pasted code between JS and JVM
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/AbstractContinuation.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/AbstractContinuation.kt
similarity index 95%
rename from core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/AbstractContinuation.kt
rename to common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/AbstractContinuation.kt
index f747f02..67a7fc6 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/AbstractContinuation.kt
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/AbstractContinuation.kt
@@ -16,10 +16,9 @@
package kotlinx.coroutines.experimental
-import kotlinx.atomicfu.atomic
-import kotlinx.atomicfu.loop
-import kotlin.coroutines.experimental.Continuation
-import kotlin.coroutines.experimental.intrinsics.COROUTINE_SUSPENDED
+import kotlinx.atomicfu.*
+import kotlin.coroutines.experimental.*
+import kotlin.coroutines.experimental.intrinsics.*
private const val UNDECIDED = 0
private const val SUSPENDED = 1
diff --git a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Annotations.common.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Annotations.common.kt
new file mode 100644
index 0000000..a0ea9b3
--- /dev/null
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Annotations.common.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2016-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// NOTE: We are defining them in a special internalAnnotations package because they would break
+// user code that uses kotlinx.coroutines library otherwise, see https://youtrack.jetbrains.com/issue/KT-23727
+package kotlinx.coroutines.experimental.internalAnnotations
+
+@Target(AnnotationTarget.FILE)
+internal expect annotation class JvmName(val name: String)
+
+@Target(AnnotationTarget.FILE)
+internal expect annotation class JvmMultifileClass()
+
+internal expect annotation class JvmField()
+
+internal expect annotation class Volatile()
diff --git a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Builders.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Builders.common.kt
similarity index 74%
rename from js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Builders.kt
rename to common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Builders.common.kt
index 896031e..5c6d1f2 100644
--- a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Builders.kt
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Builders.common.kt
@@ -14,15 +14,18 @@
* limitations under the License.
*/
-// :todo: Remove after transition to Kotlin 1.2.30+
-@file:Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
+@file:JvmMultifileClass
+@file:JvmName("BuildersKt")
package kotlinx.coroutines.experimental
+import kotlinx.coroutines.experimental.internalAnnotations.*
import kotlinx.coroutines.experimental.intrinsics.*
import kotlin.coroutines.experimental.*
import kotlin.coroutines.experimental.intrinsics.*
+// --------------- basic coroutine builders ---------------
+
/**
* Launches new coroutine without blocking current thread and returns a reference to the coroutine as a [Job].
* The coroutine is cancelled when the resulting job is [cancelled][Job.cancel].
@@ -54,7 +57,7 @@
* @param onCompletion optional completion handler for the coroutine (see [Job.invokeOnCompletion]).
* @param block the coroutine code.
*/
-public actual fun launch(
+public fun launch(
context: CoroutineContext = DefaultDispatcher,
start: CoroutineStart = CoroutineStart.DEFAULT,
parent: Job? = null,
@@ -69,6 +72,31 @@
coroutine.start(start, coroutine, block)
return coroutine
}
+/** @suppress **Deprecated**: Binary compatibility */
+@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN)
+public fun launch(
+ context: CoroutineContext = DefaultDispatcher,
+ start: CoroutineStart = CoroutineStart.DEFAULT,
+ parent: Job? = null,
+ block: suspend CoroutineScope.() -> Unit
+): Job = launch(context, start, parent, block = block)
+
+/** @suppress **Deprecated**: Binary compatibility */
+@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN)
+public fun launch(
+ context: CoroutineContext = DefaultDispatcher,
+ start: CoroutineStart = CoroutineStart.DEFAULT,
+ block: suspend CoroutineScope.() -> Unit
+): Job =
+ launch(context, start, block = block)
+
+/**
+ * @suppress **Deprecated**: Use `start = CoroutineStart.XXX` parameter
+ */
+@Deprecated(message = "Use `start = CoroutineStart.XXX` parameter",
+ replaceWith = ReplaceWith("launch(context, if (start) CoroutineStart.DEFAULT else CoroutineStart.LAZY, block)"))
+public fun launch(context: CoroutineContext, start: Boolean, block: suspend CoroutineScope.() -> Unit): Job =
+ launch(context, if (start) CoroutineStart.DEFAULT else CoroutineStart.LAZY, block = block)
/**
* Calls the specified suspending block with a given coroutine context, suspends until it completes, and returns
@@ -86,7 +114,7 @@
* Other options can be specified via `start` parameter. See [CoroutineStart] for details.
* A value of [CoroutineStart.LAZY] is not supported and produces [IllegalArgumentException].
*/
-public actual suspend fun <T> withContext(
+public suspend fun <T> withContext(
context: CoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend () -> T
@@ -111,17 +139,34 @@
val completion = RunCompletion(
context = newContext,
delegate = cont,
- resumeMode = if (start == CoroutineStart.ATOMIC) MODE_ATOMIC_DEFAULT else MODE_CANCELLABLE)
+ resumeMode = if (start == CoroutineStart.ATOMIC) MODE_ATOMIC_DEFAULT else MODE_CANCELLABLE
+ )
completion.initParentJobInternal(newContext[Job]) // attach to job
+ @Suppress("DEPRECATION")
start(block, completion)
completion.getResult()
}
+/** @suppress **Deprecated**: Renamed to [withContext]. */
+@Deprecated(message = "Renamed to `withContext`", level=DeprecationLevel.WARNING,
+ replaceWith = ReplaceWith("withContext(context, start, block)"))
+public suspend fun <T> run(
+ context: CoroutineContext,
+ start: CoroutineStart = CoroutineStart.DEFAULT,
+ block: suspend () -> T
+): T =
+ withContext(context, start, block)
+
+/** @suppress **Deprecated** */
+@Deprecated(message = "It is here for binary compatibility only", level=DeprecationLevel.HIDDEN)
+public suspend fun <T> run(context: CoroutineContext, block: suspend () -> T): T =
+ withContext(context, start = CoroutineStart.ATOMIC, block = block)
+
// --------------- implementation ---------------
private open class StandaloneCoroutine(
- private val parentContext: CoroutineContext,
- active: Boolean
+ private val parentContext: CoroutineContext,
+ active: Boolean
) : AbstractCoroutine<Unit>(parentContext, active) {
override fun hasOnFinishingHandler(update: Any?) = update is CompletedExceptionally
override fun onFinishingInternal(update: Any?) {
@@ -131,8 +176,8 @@
}
private class LazyStandaloneCoroutine(
- parentContext: CoroutineContext,
- private val block: suspend CoroutineScope.() -> Unit
+ parentContext: CoroutineContext,
+ private val block: suspend CoroutineScope.() -> Unit
) : StandaloneCoroutine(parentContext, active = false) {
override fun onStart() {
block.startCoroutineCancellable(this, this)
@@ -144,12 +189,9 @@
continuation: Continuation<T>
) : Continuation<T> by continuation
-
@Suppress("UNCHECKED_CAST")
private class RunCompletion<in T>(
override val context: CoroutineContext,
delegate: Continuation<T>,
resumeMode: Int
) : AbstractContinuation<T>(delegate, resumeMode)
-
-
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CancellableContinuation.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CancellableContinuation.kt
similarity index 88%
rename from core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CancellableContinuation.kt
rename to common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CancellableContinuation.kt
index bba910f..3b4ff8a 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CancellableContinuation.kt
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CancellableContinuation.kt
@@ -14,16 +14,12 @@
* limitations under the License.
*/
-// :todo: Remove after transition to Kotlin 1.2.30+
-@file:Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
-
package kotlinx.coroutines.experimental
-import kotlinx.coroutines.experimental.internal.LockFreeLinkedListNode
-import kotlin.coroutines.experimental.Continuation
-import kotlin.coroutines.experimental.CoroutineContext
-import kotlin.coroutines.experimental.intrinsics.suspendCoroutineOrReturn
-import kotlin.coroutines.experimental.suspendCoroutine
+import kotlinx.coroutines.experimental.internal.*
+import kotlinx.coroutines.experimental.internalAnnotations.*
+import kotlin.coroutines.experimental.*
+import kotlin.coroutines.experimental.intrinsics.*
// --------------- cancellable continuations ---------------
@@ -61,24 +57,24 @@
*
* ```
*/
-public actual interface CancellableContinuation<in T> : Continuation<T>, Job {
+public interface CancellableContinuation<in T> : Continuation<T>, Job {
/**
* Returns `true` when this continuation is active -- it has not completed or cancelled yet.
*/
- public actual override val isActive: Boolean
+ public override val isActive: Boolean
/**
* Returns `true` when this continuation has completed for any reason. A continuation
* that was cancelled is also considered complete.
*/
- public actual override val isCompleted: Boolean
+ public override val isCompleted: Boolean
/**
* Returns `true` if this continuation was [cancelled][cancel].
*
* It implies that [isActive] is `false` and [isCompleted] is `true`.
*/
- public actual override val isCancelled: Boolean
+ public override val isCancelled: Boolean
/**
* Tries to resume this continuation with a given value and returns non-null object token if it was successful,
@@ -90,7 +86,7 @@
*
* @suppress **This is unstable API and it is subject to change.**
*/
- public actual fun tryResume(value: T, idempotent: Any? = null): Any?
+ public fun tryResume(value: T, idempotent: Any? = null): Any?
/**
* Tries to resume this continuation with a given exception and returns non-null object token if it was successful,
@@ -106,20 +102,20 @@
*
* @suppress **This is unstable API and it is subject to change.**
*/
- public actual fun completeResume(token: Any)
+ public fun completeResume(token: Any)
/**
* Makes this continuation cancellable. Use it with `holdCancellability` optional parameter to
* [suspendCancellableCoroutine] function. It throws [IllegalStateException] if invoked more than once.
*/
- public actual fun initCancellability()
+ public fun initCancellability()
/**
* Cancels this continuation with an optional cancellation [cause]. The result is `true` if this continuation was
* cancelled as a result of this invocation and `false` otherwise.
*/
@Suppress("DEFAULT_VALUE_NOT_ALLOWED_IN_OVERRIDE")
- public actual override fun cancel(cause: Throwable? = null): Boolean
+ public override fun cancel(cause: Throwable? = null): Boolean
/**
* Registers handler that is **synchronously** invoked once on completion of this continuation.
@@ -135,7 +131,7 @@
* Installed [handler] should not throw any exceptions. If it does, they will get caught,
* wrapped into [CompletionHandlerException], and rethrown, potentially causing crash of unrelated code.
*/
- public actual override fun invokeOnCompletion(handler: CompletionHandler): DisposableHandle
+ public override fun invokeOnCompletion(handler: CompletionHandler): DisposableHandle
/**
* Resumes this continuation with a given [value] in the invoker thread without going though
@@ -143,7 +139,7 @@
* This function is designed to be used only by the [CoroutineDispatcher] implementations themselves.
* **It should not be used in general code**.
*/
- public actual fun CoroutineDispatcher.resumeUndispatched(value: T)
+ public fun CoroutineDispatcher.resumeUndispatched(value: T)
/**
* Resumes this continuation with a given [exception] in the invoker thread without going though
@@ -151,7 +147,7 @@
* This function is designed to be used only by the [CoroutineDispatcher] implementations themselves.
* **It should not be used in general code**.
*/
- public actual fun CoroutineDispatcher.resumeUndispatchedWithException(exception: Throwable)
+ public fun CoroutineDispatcher.resumeUndispatchedWithException(exception: Throwable)
}
/**
@@ -163,7 +159,7 @@
*
* See [suspendAtomicCancellableCoroutine] for suspending functions that need *atomic cancellation*.
*/
-public actual inline suspend fun <T> suspendCancellableCoroutine(
+public suspend inline fun <T> suspendCancellableCoroutine(
holdCancellability: Boolean = false,
crossinline block: (CancellableContinuation<T>) -> Unit
): T =
@@ -172,7 +168,7 @@
if (!holdCancellability) cancellable.initCancellability()
block(cancellable)
cancellable.getResult()
-}
+ }
/**
* Suspends coroutine similar to [suspendCancellableCoroutine], but with *atomic cancellation*.
@@ -182,7 +178,7 @@
* continue to execute even after it was cancelled from the same thread in the case when the continuation
* was already resumed and was posted for execution to the thread's queue.
*/
-public actual inline suspend fun <T> suspendAtomicCancellableCoroutine(
+public suspend inline fun <T> suspendAtomicCancellableCoroutine(
holdCancellability: Boolean = false,
crossinline block: (CancellableContinuation<T>) -> Unit
): T =
@@ -198,15 +194,15 @@
* @suppress **This is unstable API and it is subject to change.**
*/
public fun CancellableContinuation<*>.removeOnCancel(node: LockFreeLinkedListNode): DisposableHandle =
- invokeOnCompletion(handler = RemoveOnCancel(this, node))
+ invokeOnCompletion(handler = RemoveOnCancel(this, node).asHandler)
// --------------- implementation details ---------------
private class RemoveOnCancel(
cont: CancellableContinuation<*>,
- val node: LockFreeLinkedListNode
+ @JvmField val node: LockFreeLinkedListNode
) : JobNode<CancellableContinuation<*>>(cont) {
- override fun invoke(reason: Throwable?) {
+ override fun invoke(cause: Throwable?) {
if (job.isCancelled)
node.remove()
}
@@ -283,12 +279,16 @@
override fun nameString(): String =
"CancellableContinuation(${delegate.toDebugString()})"
+
+ // todo: This workaround for KT-21968, should be removed in the future
+ public override fun cancel(cause: Throwable?): Boolean =
+ super.cancel(cause)
}
private class CompletedIdempotentResult(
@JvmField val idempotentResume: Any?,
@JvmField val result: Any?,
- @JvmField val token: JobSupport.Incomplete
+ @JvmField val token: Incomplete
) {
override fun toString(): String = "CompletedIdempotentResult[$result]"
}
diff --git a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonBuilders.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonBuilders.kt
deleted file mode 100644
index 7522850..0000000
--- a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonBuilders.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2016-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package kotlinx.coroutines.experimental
-
-import kotlin.coroutines.experimental.*
-
-@Suppress("EXPECTED_DECLARATION_WITH_DEFAULT_PARAMETER")
-public expect fun launch(
- context: CoroutineContext = DefaultDispatcher,
- start: CoroutineStart = CoroutineStart.DEFAULT,
- parent: Job? = null,
- onCompletion: CompletionHandler? = null,
- block: suspend CoroutineScope.() -> Unit
-): Job
-
-@Suppress("EXPECTED_DECLARATION_WITH_DEFAULT_PARAMETER")
-public expect suspend fun <T> withContext(
- context: CoroutineContext,
- start: CoroutineStart = CoroutineStart.DEFAULT,
- block: suspend () -> T
-): T
diff --git a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonCancellableContinuation.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonCancellableContinuation.kt
deleted file mode 100644
index 663cc58..0000000
--- a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonCancellableContinuation.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2016-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// :todo: Remove after transition to Kotlin 1.2.30+
-@file:Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
-
-package kotlinx.coroutines.experimental
-
-import kotlin.coroutines.experimental.Continuation
-
-public expect interface CancellableContinuation<in T> : Continuation<T> {
- public val isActive: Boolean
- public val isCompleted: Boolean
- public val isCancelled: Boolean
- @Suppress("EXPECTED_DECLARATION_WITH_DEFAULT_PARAMETER")
- public fun tryResume(value: T, idempotent: Any? = null): Any?
- public fun completeResume(token: Any)
- public fun initCancellability()
- @Suppress("EXPECTED_DECLARATION_WITH_DEFAULT_PARAMETER")
- public actual fun cancel(cause: Throwable? = null): Boolean
- public fun invokeOnCompletion(handler: CompletionHandler): DisposableHandle
- public fun CoroutineDispatcher.resumeUndispatched(value: T)
- public fun CoroutineDispatcher.resumeUndispatchedWithException(exception: Throwable)
-}
-
-@Suppress("EXPECTED_DECLARATION_WITH_DEFAULT_PARAMETER")
-public expect suspend fun <T> suspendCancellableCoroutine(
- holdCancellability: Boolean = false,
- block: (CancellableContinuation<T>) -> Unit
-): T
-
-@Suppress("EXPECTED_DECLARATION_WITH_DEFAULT_PARAMETER")
-public expect suspend fun <T> suspendAtomicCancellableCoroutine(
- holdCancellability: Boolean = false,
- block: (CancellableContinuation<T>) -> Unit
-): T
diff --git a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonCompletableDeferred.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonCompletableDeferred.kt
deleted file mode 100644
index c24c4a1..0000000
--- a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonCompletableDeferred.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2016-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package kotlinx.coroutines.experimental
-
-public expect interface CompletableDeferred<T> : Deferred<T> {
- public fun complete(value: T): Boolean
- public fun completeExceptionally(exception: Throwable): Boolean
-}
-
-@Suppress("FunctionName", "EXPECTED_DECLARATION_WITH_DEFAULT_PARAMETER")
-public expect fun <T> CompletableDeferred(parent: Job? = null): CompletableDeferred<T>
-
-@Suppress("FunctionName", "EXPECTED_DECLARATION_WITH_DEFAULT_PARAMETER")
-public expect fun <T> CompletableDeferred(value: T): CompletableDeferred<T>
diff --git a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonCoroutineDispatcher.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonCoroutineDispatcher.kt
deleted file mode 100644
index e184307..0000000
--- a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonCoroutineDispatcher.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2016-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package kotlinx.coroutines.experimental
-
-import kotlin.coroutines.experimental.*
-
-public expect abstract class CoroutineDispatcher constructor() : AbstractCoroutineContextElement, ContinuationInterceptor {
- public open fun isDispatchNeeded(context: CoroutineContext): Boolean
- public abstract fun dispatch(context: CoroutineContext, block: Runnable)
- public override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T>
-}
-
-public expect interface Runnable {
- public fun run()
-}
-
-@Suppress("PropertyName")
-public expect val DefaultDispatcher: CoroutineDispatcher
diff --git a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonCoroutineExceptionHandler.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonCoroutineExceptionHandler.kt
deleted file mode 100644
index 2141027..0000000
--- a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonCoroutineExceptionHandler.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2016-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package kotlinx.coroutines.experimental
-
-import kotlin.coroutines.experimental.CoroutineContext
-
-public expect fun handleCoroutineException(context: CoroutineContext, exception: Throwable)
-
-public expect interface CoroutineExceptionHandler : CoroutineContext.Element {
- public companion object Key : CoroutineContext.Key<CoroutineExceptionHandler>
- public fun handleException(context: CoroutineContext, exception: Throwable)
-}
-
-@Suppress("FunctionName")
-public expect fun CoroutineExceptionHandler(handler: (CoroutineContext, Throwable) -> Unit): CoroutineExceptionHandler
diff --git a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonDeferred.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonDeferred.kt
deleted file mode 100644
index 3c6786c..0000000
--- a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonDeferred.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2016-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package kotlinx.coroutines.experimental
-
-import kotlin.coroutines.experimental.*
-
-public expect interface Deferred<out T> : Job {
- public val isCompletedExceptionally: Boolean
- public suspend fun await(): T
- public fun getCompleted(): T
- public fun getCompletionExceptionOrNull(): Throwable?
-}
-
-@Suppress("EXPECTED_DECLARATION_WITH_DEFAULT_PARAMETER")
-public expect fun <T> async(
- context: CoroutineContext = DefaultDispatcher,
- start: CoroutineStart = CoroutineStart.DEFAULT,
- parent: Job? = null,
- onCompletion: CompletionHandler? = null,
- block: suspend CoroutineScope.() -> T
-): Deferred<T>
\ No newline at end of file
diff --git a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonJob.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonJob.kt
deleted file mode 100644
index 5fb9b84..0000000
--- a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonJob.kt
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright 2016-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// :todo: Remove after transition to Kotlin 1.2.30+
-@file:Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
-
-package kotlinx.coroutines.experimental
-
-import kotlin.coroutines.experimental.CoroutineContext
-
-expect public interface Job : CoroutineContext.Element {
- public companion object Key : CoroutineContext.Key<Job>
- public val isActive: Boolean
- public val isCompleted: Boolean
- public val isCancelled: Boolean
- public fun getCancellationException(): CancellationException
- public fun start(): Boolean
- @Suppress("EXPECTED_DECLARATION_WITH_DEFAULT_PARAMETER")
- public fun cancel(cause: Throwable? = null): Boolean
- public val children: Sequence<Job>
- @Deprecated(message = "Start child coroutine with 'parent' parameter", level = DeprecationLevel.WARNING)
- public fun attachChild(child: Job): DisposableHandle
- public suspend fun join()
- @Suppress("EXPECTED_DECLARATION_WITH_DEFAULT_PARAMETER")
- public actual fun invokeOnCompletion(
- onCancelling: Boolean = false,
- invokeImmediately: Boolean = true,
- handler: CompletionHandler): DisposableHandle
-}
-
-@Suppress("FunctionName", "EXPECTED_DECLARATION_WITH_DEFAULT_PARAMETER")
-public expect fun Job(parent: Job? = null): Job
-
-public expect interface DisposableHandle {
- public fun dispose()
-}
-
-public expect val CoroutineContext.isActive: Boolean
-@Suppress("EXPECTED_DECLARATION_WITH_DEFAULT_PARAMETER")
-public expect fun CoroutineContext.cancel(cause: Throwable? = null): Boolean
-@Suppress("EXPECTED_DECLARATION_WITH_DEFAULT_PARAMETER")
-public expect fun CoroutineContext.cancelChildren(cause: Throwable? = null)
-
-public expect fun Job.disposeOnCompletion(handle: DisposableHandle): DisposableHandle
-public expect suspend fun Job.cancelAndJoin()
-@Suppress("EXPECTED_DECLARATION_WITH_DEFAULT_PARAMETER", "EXTENSION_SHADOWED_BY_MEMBER") // See KT-21598
-public expect fun Job.cancelChildren(cause: Throwable? = null)
-public expect suspend fun Job.joinChildren()
-
-public expect object NonDisposableHandle : DisposableHandle {
- override fun dispose()
-}
-
-internal expect open class JobSupport(active: Boolean) : Job {
- public final override val key: CoroutineContext.Key<*>
- public final override val isActive: Boolean
- public final override val isCompleted: Boolean
- public final override val isCancelled: Boolean
-
- public final override fun getCancellationException(): CancellationException
- public final override fun start(): Boolean
- public final override val children: Sequence<Job>
-
- public override fun cancel(cause: Throwable?): Boolean
-
- public final override fun attachChild(child: Job): DisposableHandle
- public final override suspend fun join()
-
- // todo: non-final as a workaround for KT-21968, should be final in the future
- public override fun invokeOnCompletion(
- onCancelling: Boolean,
- invokeImmediately: Boolean,
- handler: CompletionHandler
- ): DisposableHandle
-
- public val isCompletedExceptionally: Boolean
- public fun getCompletionExceptionOrNull(): Throwable?
-
- internal fun initParentJobInternal(parent: Job?)
- internal fun makeCompletingOnce(proposedUpdate: Any?, mode: Int): Boolean
- internal open fun hasOnFinishingHandler(update: Any?): Boolean
- internal open fun onFinishingInternal(update: Any?)
- internal open fun onCompletionInternal(state: Any?, mode: Int)
- internal open fun onStartInternal()
- internal open fun onCancellationInternal(exceptionally: CompletedExceptionally?)
- internal open fun nameString(): String
- internal open fun handleException(exception: Throwable)
-}
\ No newline at end of file
diff --git a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonNonCancellable.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonNonCancellable.kt
deleted file mode 100644
index 756c06e..0000000
--- a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonNonCancellable.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2016-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package kotlinx.coroutines.experimental
-
-public expect object NonCancellable : Job {
- override val isActive: Boolean
- override val isCompleted: Boolean
- override val isCancelled: Boolean
- override fun start(): Boolean
- suspend override fun join()
- override fun getCancellationException(): CancellationException
- override fun invokeOnCompletion(onCancelling: Boolean, invokeImmediately: Boolean, handler: CompletionHandler): DisposableHandle
- override fun cancel(cause: Throwable?): Boolean
- override val children: Sequence<Job>
- @Suppress("OverridingDeprecatedMember")
- override fun attachChild(child: Job): DisposableHandle
-}
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CompletableDeferred.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CompletableDeferred.kt
similarity index 85%
rename from core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CompletableDeferred.kt
rename to common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CompletableDeferred.kt
index af61dd9..d1d8894 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CompletableDeferred.kt
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CompletableDeferred.kt
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-// :todo: Remove after transition to Kotlin 1.2.30+
-@file:Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
-
package kotlinx.coroutines.experimental
import kotlinx.coroutines.experimental.selects.*
@@ -32,14 +29,14 @@
* All functions on this interface and on all interfaces derived from it are **thread-safe** and can
* be safely invoked from concurrent coroutines without external synchronization.
*/
-public actual interface CompletableDeferred<T> : Deferred<T> {
+public interface CompletableDeferred<T> : Deferred<T> {
/**
* Completes this deferred value with a given [value]. The result is `true` if this deferred was
* completed as a result of this invocation and `false` otherwise (if it was already completed).
*
* Repeated invocations of this function have no effect and always produce `false`.
*/
- public actual fun complete(value: T): Boolean
+ public fun complete(value: T): Boolean
/**
* Completes this deferred value exceptionally with a given [exception]. The result is `true` if this deferred was
@@ -47,7 +44,7 @@
*
* Repeated invocations of this function have no effect and always produce `false`.
*/
- public actual fun completeExceptionally(exception: Throwable): Boolean
+ public fun completeExceptionally(exception: Throwable): Boolean
}
/**
@@ -55,7 +52,7 @@
* It is optionally a child of a [parent] job.
*/
@Suppress("FunctionName")
-public actual fun <T> CompletableDeferred(parent: Job? = null): CompletableDeferred<T> = CompletableDeferredImpl(parent)
+public fun <T> CompletableDeferred(parent: Job? = null): CompletableDeferred<T> = CompletableDeferredImpl(parent)
/** @suppress **Deprecated:** Binary compatibility only */
@Deprecated(message = "Binary compatibility only", level = DeprecationLevel.HIDDEN)
@@ -66,7 +63,7 @@
* Creates an already _completed_ [CompletableDeferred] with a given [value].
*/
@Suppress("FunctionName")
-public actual fun <T> CompletableDeferred(value: T): CompletableDeferred<T> = CompletableDeferredImpl<T>(null).apply { complete(value) }
+public fun <T> CompletableDeferred(value: T): CompletableDeferred<T> = CompletableDeferredImpl<T>(null).apply { complete(value) }
/**
* Concrete implementation of [CompletableDeferred].
diff --git a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CompletedExceptionally.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CompletedExceptionally.kt
index 8984a28..8e3276e 100644
--- a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CompletedExceptionally.kt
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CompletedExceptionally.kt
@@ -16,6 +16,8 @@
package kotlinx.coroutines.experimental
+import kotlinx.coroutines.experimental.internalAnnotations.*
+
/**
* Class for an internal state of a job that had completed exceptionally, including cancellation.
*
@@ -27,7 +29,7 @@
* @suppress **This is unstable API and it is subject to change.**
*/
public open class CompletedExceptionally protected constructor(
- public val cause: Throwable?,
+ @JvmField public val cause: Throwable?,
allowNullCause: Boolean
) {
/**
diff --git a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonCompletionHandler.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CompletionHandler.common.kt
similarity index 65%
rename from common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonCompletionHandler.kt
rename to common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CompletionHandler.common.kt
index d039861..dc2fd9a 100644
--- a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonCompletionHandler.kt
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CompletionHandler.common.kt
@@ -16,6 +16,8 @@
package kotlinx.coroutines.experimental
+import kotlinx.coroutines.experimental.internal.*
+
/**
* Handler for [Job.invokeOnCompletion].
*
@@ -28,3 +30,14 @@
* Implementations of `CompletionHandler` must be fast and _lock-free_.
*/
public typealias CompletionHandler = (cause: Throwable?) -> Unit
+
+// We want class that extends LockFreeLinkedListNode & CompletionHandler but we cannot do it on Kotlin/JS,
+// so this expect class provides us with the corresponding abstraction in a platform-agnostic way.
+internal expect abstract class CompletionHandlerNode() : LockFreeLinkedListNode {
+ val asHandler: CompletionHandler
+ abstract fun invoke(cause: Throwable?)
+}
+
+// :KLUDGE: We have to invoke a handler in platform-specific way via `invokeIt` extension,
+// because we play type tricks on Kotlin/JS and handler is not necessarily a function there
+internal expect fun CompletionHandler.invokeIt(cause: Throwable?)
diff --git a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonCoroutineContext.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineContext.common.kt
similarity index 74%
rename from common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonCoroutineContext.kt
rename to common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineContext.common.kt
index c9aa202..a60cec0 100644
--- a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonCoroutineContext.kt
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineContext.common.kt
@@ -18,10 +18,14 @@
import kotlin.coroutines.experimental.*
-public expect object Unconfined : CoroutineDispatcher {
- override fun isDispatchNeeded(context: CoroutineContext): Boolean
- override fun dispatch(context: CoroutineContext, block: Runnable)
-}
+@Suppress("EXPECTED_DECLARATION_WITH_DEFAULT_PARAMETER")
+public expect fun newCoroutineContext(context: CoroutineContext, parent: Job? = null): CoroutineContext
+
+@Suppress("PropertyName")
+public expect val DefaultDispatcher: CoroutineDispatcher
+
+@Suppress("PropertyName")
+internal expect val DefaultDelay: Delay
internal expect inline fun <T> withCoroutineContext(context: CoroutineContext, block: () -> T): T
internal expect fun Continuation<*>.toDebugString(): String
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineDispatcher.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineDispatcher.kt
similarity index 89%
rename from core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineDispatcher.kt
rename to common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineDispatcher.kt
index feb4b0d..9b98ba4 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineDispatcher.kt
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineDispatcher.kt
@@ -37,7 +37,7 @@
*
* This class ensures that debugging facilities in [newCoroutineContext] function work properly.
*/
-public actual abstract class CoroutineDispatcher actual constructor() :
+public abstract class CoroutineDispatcher :
AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
/**
* Returns `true` if execution shall be dispatched onto another thread.
@@ -70,17 +70,17 @@
* parameter that allows one to optionally choose C#-style [CoroutineStart.UNDISPATCHED] behaviour
* whenever it is needed for efficiency.
*/
- public actual open fun isDispatchNeeded(context: CoroutineContext): Boolean = true
+ public open fun isDispatchNeeded(context: CoroutineContext): Boolean = true
/**
* Dispatches execution of a runnable [block] onto another thread in the given [context].
*/
- public actual abstract fun dispatch(context: CoroutineContext, block: Runnable)
+ public abstract fun dispatch(context: CoroutineContext, block: Runnable)
/**
* Returns continuation that wraps the original [continuation], thus intercepting all resumptions.
*/
- public actual override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
+ public override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
DispatchedContinuation(this, continuation)
/**
@@ -98,11 +98,6 @@
public operator fun plus(other: CoroutineDispatcher) = other
// for nicer debugging
- override fun toString(): String =
- "${this::class.java.simpleName}@${Integer.toHexString(System.identityHashCode(this))}"
+ override fun toString(): String = "$classSimpleName@$hexAddress"
}
-/**
- * A runnable task for [CoroutineDispatcher.dispatch].
- */
-public actual typealias Runnable = java.lang.Runnable
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineExceptionHandler.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineExceptionHandler.kt
similarity index 69%
rename from core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineExceptionHandler.kt
rename to common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineExceptionHandler.kt
index 2bb0cc7..44e60fd 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineExceptionHandler.kt
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineExceptionHandler.kt
@@ -16,9 +16,9 @@
package kotlinx.coroutines.experimental
-import java.util.*
-import kotlin.coroutines.experimental.AbstractCoroutineContextElement
-import kotlin.coroutines.experimental.CoroutineContext
+import kotlin.coroutines.experimental.*
+
+internal expect fun handleCoroutineExceptionImpl(context: CoroutineContext, exception: Throwable)
/**
* Helper function for coroutine builder implementations to handle uncaught exception in coroutines.
@@ -32,7 +32,7 @@
* * all instances of [CoroutineExceptionHandler] found via [ServiceLoader] are invoked;
* * current thread's [Thread.uncaughtExceptionHandler] is invoked.
*/
-public actual fun handleCoroutineException(context: CoroutineContext, exception: Throwable) {
+public fun handleCoroutineException(context: CoroutineContext, exception: Throwable) {
// if exception handling fails, make sure the original exception is not lost
try {
context[CoroutineExceptionHandler]?.let {
@@ -43,24 +43,30 @@
if (exception is CancellationException) return
// try cancel job in the context
context[Job]?.cancel(exception)
- // use additional extension handlers
- ServiceLoader.load(CoroutineExceptionHandler::class.java).forEach { handler ->
- handler.handleException(context, exception)
- }
- // use thread's handler
- val currentThread = Thread.currentThread()
- currentThread.uncaughtExceptionHandler.uncaughtException(currentThread, exception)
+ // platform-specific
+ handleCoroutineExceptionImpl(context, exception)
} catch (handlerException: Throwable) {
// simply rethrow if handler threw the original exception
if (handlerException === exception) throw exception
// handler itself crashed for some other reason -- that is bad -- keep both
throw RuntimeException("Exception while trying to handle coroutine exception", exception).apply {
- addSuppressed(handlerException)
+ addSuppressedThrowable(handlerException)
}
}
}
/**
+ * Creates new [CoroutineExceptionHandler] instance.
+ * @param handler a function which handles exception thrown by a coroutine
+ */
+@Suppress("FunctionName")
+public inline fun CoroutineExceptionHandler(crossinline handler: (CoroutineContext, Throwable) -> Unit): CoroutineExceptionHandler =
+ object: AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler {
+ override fun handleException(context: CoroutineContext, exception: Throwable) =
+ handler.invoke(context, exception)
+ }
+
+/**
* An optional element on the coroutine context to handle uncaught exceptions.
*
* By default, when no handler is installed, uncaught exception are handled in the following way:
@@ -73,35 +79,15 @@
*
* See [handleCoroutineException].
*/
-public actual interface CoroutineExceptionHandler : CoroutineContext.Element {
+public interface CoroutineExceptionHandler : CoroutineContext.Element {
/**
* Key for [CoroutineExceptionHandler] instance in the coroutine context.
*/
- public actual companion object Key : CoroutineContext.Key<CoroutineExceptionHandler> {
- /**
- * Creates new [CoroutineExceptionHandler] instance.
- * @param handler a function which handles exception thrown by a coroutine
- * @suppress **Deprecated**
- */
- @Deprecated("Replaced with top-level function", level = DeprecationLevel.HIDDEN)
- public operator inline fun invoke(crossinline handler: (CoroutineContext, Throwable) -> Unit): CoroutineExceptionHandler =
- CoroutineExceptionHandler(handler)
- }
+ public companion object Key : CoroutineContext.Key<CoroutineExceptionHandler>
/**
* Handles uncaught [exception] in the given [context]. It is invoked
* if coroutine has an uncaught exception. See [handleCoroutineException].
*/
- public actual fun handleException(context: CoroutineContext, exception: Throwable)
+ public fun handleException(context: CoroutineContext, exception: Throwable)
}
-
-/**
- * Creates new [CoroutineExceptionHandler] instance.
- * @param handler a function which handles exception thrown by a coroutine
- */
-@Suppress("FunctionName")
-public actual inline fun CoroutineExceptionHandler(crossinline handler: (CoroutineContext, Throwable) -> Unit): CoroutineExceptionHandler =
- object: AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler {
- override fun handleException(context: CoroutineContext, exception: Throwable) =
- handler.invoke(context, exception)
- }
\ No newline at end of file
diff --git a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonDebug.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Debug.common.kt
similarity index 93%
rename from common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonDebug.kt
rename to common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Debug.common.kt
index 3143a1f..b6fb337 100644
--- a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonDebug.kt
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Debug.common.kt
@@ -16,4 +16,5 @@
package kotlinx.coroutines.experimental
-internal expect val Any.classSimpleName: String
\ No newline at end of file
+internal expect val Any.hexAddress: String
+internal expect val Any.classSimpleName: String
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Deferred.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Deferred.kt
similarity index 95%
rename from core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Deferred.kt
rename to common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Deferred.kt
index ddc8e0e..3f15e8a 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Deferred.kt
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Deferred.kt
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-// :todo: Remove after transition to Kotlin 1.2.30+
-@file:Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
-
package kotlinx.coroutines.experimental
import kotlinx.coroutines.experimental.intrinsics.*
@@ -81,14 +78,14 @@
* All functions on this interface and on all interfaces derived from it are **thread-safe** and can
* be safely invoked from concurrent coroutines without external synchronization.
*/
-public actual interface Deferred<out T> : Job {
+public interface Deferred<out T> : Job {
/**
* Returns `true` if computation of this deferred value has _completed exceptionally_ -- it had
* either _failed_ with exception during computation or was [cancelled][cancel].
*
* It implies that [isActive] is `false` and [isCompleted] is `true`.
*/
- public actual val isCompletedExceptionally: Boolean
+ public val isCompletedExceptionally: Boolean
/**
* Awaits for completion of this value without blocking a thread and resumes when deferred computation is complete,
@@ -101,7 +98,7 @@
* This function can be used in [select] invocation with [onAwait] clause.
* Use [isCompleted] to check for completion of this deferred value without waiting.
*/
- public actual suspend fun await(): T
+ public suspend fun await(): T
/**
* Clause for [select] expression of [await] suspending function that selects with the deferred value when it is
@@ -118,7 +115,7 @@
* This function is designed to be used from [invokeOnCompletion] handlers, when there is an absolute certainty that
* the value is already complete. See also [getCompletionExceptionOrNull].
*/
- public actual fun getCompleted(): T
+ public fun getCompleted(): T
/**
* Returns *completion exception* result if this deferred [completed exceptionally][isCompletedExceptionally],
@@ -128,7 +125,7 @@
* This function is designed to be used from [invokeOnCompletion] handlers, when there is an absolute certainty that
* the value is already complete. See also [getCompleted].
*/
- public actual fun getCompletionExceptionOrNull(): Throwable?
+ public fun getCompletionExceptionOrNull(): Throwable?
/**
* @suppress **Deprecated**: Use `isActive`.
@@ -163,7 +160,7 @@
* @param onCompletion optional completion handler for the coroutine (see [Job.invokeOnCompletion]).
* @param block the coroutine code.
*/
-public actual fun <T> async(
+public fun <T> async(
context: CoroutineContext = DefaultDispatcher,
start: CoroutineStart = CoroutineStart.DEFAULT,
parent: Job? = null,
@@ -209,7 +206,7 @@
* @suppress **Deprecated**: `defer` was renamed to `async`.
*/
@Deprecated(message = "`defer` was renamed to `async`", level = DeprecationLevel.WARNING,
- replaceWith = ReplaceWith("async(context, block = block)"))
+ replaceWith = ReplaceWith("async(context, block = block)"))
public fun <T> defer(context: CoroutineContext, block: suspend CoroutineScope.() -> T): Deferred<T> =
async(context, block = block)
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Delay.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Delay.kt
similarity index 84%
rename from core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Delay.kt
rename to common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Delay.kt
index 8d1e01e..25775d1 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Delay.kt
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Delay.kt
@@ -16,12 +16,9 @@
package kotlinx.coroutines.experimental
-import kotlinx.coroutines.experimental.selects.SelectBuilder
-import kotlinx.coroutines.experimental.selects.select
-import java.util.concurrent.Future
-import java.util.concurrent.TimeUnit
-import kotlin.coroutines.experimental.ContinuationInterceptor
-import kotlin.coroutines.experimental.CoroutineContext
+import kotlinx.coroutines.experimental.selects.*
+import kotlinx.coroutines.experimental.timeunit.*
+import kotlin.coroutines.experimental.*
/**
* This dispatcher _feature_ is implemented by [CoroutineDispatcher] implementations that natively support
@@ -30,7 +27,7 @@
* Implementation of this interface affects operation of
* [delay][kotlinx.coroutines.experimental.delay] and [withTimeout] functions.
*/
-public actual interface Delay {
+public interface Delay {
/**
* Delays coroutine for a given time without blocking a thread and resumes it after a specified time.
* This suspending function is cancellable.
@@ -68,7 +65,7 @@
* This implementation uses a built-in single-threaded scheduled executor service.
*/
fun invokeOnTimeout(time: Long, unit: TimeUnit, block: Runnable): DisposableHandle =
- DefaultExecutor.invokeOnTimeout(time, unit, block)
+ DefaultDelay.invokeOnTimeout(time, unit, block)
}
/**
@@ -84,7 +81,7 @@
*
* @param time time in milliseconds.
*/
-public actual suspend fun delay(time: Int) =
+public suspend fun delay(time: Int) =
delay(time.toLong(), TimeUnit.MILLISECONDS)
/**
@@ -109,16 +106,5 @@
}
}
-/**
- * An implementation of [DisposableHandle] that cancels the specified future on dispose.
- * @suppress **This is unstable API and it is subject to change.**
- */
-public class DisposableFutureHandle(private val future: Future<*>) : DisposableHandle {
- override fun dispose() {
- future.cancel(false)
- }
- override fun toString(): String = "DisposableFutureHandle[$future]"
-}
-
/** Returns [Delay] implementation of the given context */
-internal val CoroutineContext.delay: Delay get() = get(ContinuationInterceptor) as? Delay ?: DefaultExecutor
+internal val CoroutineContext.delay: Delay get() = get(ContinuationInterceptor) as? Delay ?: DefaultDelay
diff --git a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Dispatched.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Dispatched.kt
index ba29b44..e81e5a9 100644
--- a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Dispatched.kt
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Dispatched.kt
@@ -17,14 +17,15 @@
package kotlinx.coroutines.experimental
import kotlinx.coroutines.experimental.internal.*
+import kotlinx.coroutines.experimental.internalAnnotations.*
import kotlin.coroutines.experimental.*
@Suppress("PrivatePropertyName")
private val UNDEFINED = Symbol("UNDEFINED")
internal class DispatchedContinuation<in T>(
- val dispatcher: CoroutineDispatcher,
- val continuation: Continuation<T>
+ @JvmField val dispatcher: CoroutineDispatcher,
+ @JvmField val continuation: Continuation<T>
) : Continuation<T> by continuation, DispatchedTask<T> {
private var _state: Any? = UNDEFINED
public override var resumeMode: Int = 0
diff --git a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonExceptions.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Exceptions.common.kt
similarity index 92%
rename from common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonExceptions.kt
rename to common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Exceptions.common.kt
index 437fc71..e2e67d3 100644
--- a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonExceptions.kt
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Exceptions.common.kt
@@ -28,6 +28,6 @@
val job: Job
}
-public expect class TimeoutCancellationException public constructor(message: String)
+internal expect class DispatchException(message: String, cause: Throwable) : RuntimeException
-internal expect class DispatchException(message: String, cause: Throwable) : RuntimeException
\ No newline at end of file
+internal expect fun Throwable.addSuppressedThrowable(other: Throwable)
\ No newline at end of file
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Job.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Job.kt
similarity index 85%
rename from core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Job.kt
rename to common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Job.kt
index 6c03c07..e94e951 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Job.kt
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Job.kt
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-// :todo: Remove after transition to Kotlin 1.2.30+
-@file:Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
+@file:JvmMultifileClass
+@file:JvmName("JobKt")
package kotlinx.coroutines.experimental
import kotlinx.atomicfu.*
import kotlinx.coroutines.experimental.internal.*
+import kotlinx.coroutines.experimental.internalAnnotations.*
import kotlinx.coroutines.experimental.intrinsics.*
import kotlinx.coroutines.experimental.selects.*
-import java.util.concurrent.*
import kotlin.coroutines.experimental.*
import kotlin.coroutines.experimental.intrinsics.*
@@ -87,11 +87,11 @@
* All functions on this interface and on all interfaces derived from it are **thread-safe** and can
* be safely invoked from concurrent coroutines without external synchronization.
*/
-public actual interface Job : CoroutineContext.Element {
+public interface Job : CoroutineContext.Element {
/**
* Key for [Job] instance in the coroutine context.
*/
- public actual companion object Key : CoroutineContext.Key<Job> {
+ public companion object Key : CoroutineContext.Key<Job> {
/**
* Creates a new job object in _active_ state.
* It is optionally a child of a [parent] job.
@@ -117,20 +117,20 @@
* The job that is waiting for its [children] to complete is still considered to be active if it
* was not cancelled.
*/
- public actual val isActive: Boolean
+ public val isActive: Boolean
/**
* Returns `true` when this job has completed for any reason. A job that was cancelled and has
* finished its execution is also considered complete. Job becomes complete only after
* all its [children] complete.
*/
- public actual val isCompleted: Boolean
+ public val isCompleted: Boolean
/**
* Returns `true` if this job was [cancelled][cancel]. In the general case, it does not imply that the
* job has already [completed][isCompleted] (it may still be cancelling whatever it was doing).
*/
- public actual val isCancelled: Boolean
+ public val isCancelled: Boolean
/**
* Returns [CancellationException] that signals the completion of this job. This function is
@@ -146,7 +146,7 @@
* This function throws [IllegalStateException] when invoked on a job that has not
* [completed][isCompleted] nor [cancelled][isCancelled] yet.
*/
- public actual fun getCancellationException(): CancellationException
+ public fun getCancellationException(): CancellationException
/**
* @suppress **Deprecated**: Renamed to [getCancellationException]
@@ -162,7 +162,7 @@
* The result `true` if this invocation actually started coroutine or `false`
* if it was already started or completed.
*/
- public actual fun start(): Boolean
+ public fun start(): Boolean
/**
* Cancels this job with an optional cancellation [cause]. The result is `true` if this job was
@@ -174,7 +174,7 @@
* at the corresponding original cancellation site and passed into this method to aid in debugging by providing
* both the context of cancellation and text description of the reason.
*/
- public actual fun cancel(cause: Throwable? = null): Boolean
+ public fun cancel(cause: Throwable? = null): Boolean
// ------------ parent-child ------------
@@ -195,7 +195,7 @@
* coroutine builders do not have uncaught exceptions by definition, since all their exceptions are
* caught and are encapsulated in their result.
*/
- public actual val children: Sequence<Job>
+ public val children: Sequence<Job>
/**
* Attaches child job so that this job becomes its parent and
@@ -217,7 +217,7 @@
* @suppress This is an internal API. This method is too error prone for public API.
*/
@Deprecated(message = "Start child coroutine with 'parent' parameter", level = DeprecationLevel.WARNING)
- public actual fun attachChild(child: Job): DisposableHandle
+ public fun attachChild(child: Job): DisposableHandle
/**
* Cancels all children jobs of this coroutine with the given [cause]. Unlike [cancel],
@@ -250,7 +250,7 @@
*
* There is [cancelAndJoin] function that combines an invocation of [cancel] and `join`.
*/
- public actual suspend fun join()
+ public suspend fun join()
/**
* Clause for [select] expression of [join] suspending function that selects when the job is complete.
@@ -324,7 +324,7 @@
* when `false` then [NonDisposableHandle] is returned, but the [handler] is not invoked.
* @param handler the handler.
*/
- public actual fun invokeOnCompletion(
+ public fun invokeOnCompletion(
onCancelling: Boolean = false,
invokeImmediately: Boolean = true,
handler: CompletionHandler): DisposableHandle
@@ -338,27 +338,10 @@
*/
@Suppress("DeprecatedCallableAddReplaceWith")
@Deprecated(message = "Operator '+' on two Job objects is meaningless. " +
- "Job is a coroutine context element and `+` is a set-sum operator for coroutine contexts. " +
- "The job to the right of `+` just replaces the job the left of `+`.",
- level = DeprecationLevel.ERROR)
+ "Job is a coroutine context element and `+` is a set-sum operator for coroutine contexts. " +
+ "The job to the right of `+` just replaces the job the left of `+`.",
+ level = DeprecationLevel.ERROR)
public operator fun plus(other: Job) = other
-
- /**
- * Registration object for [invokeOnCompletion]. It can be used to [unregister] if needed.
- * There is no need to unregister after completion.
- * @suppress **Deprecated**: Replace with `DisposableHandle`
- */
- @Deprecated(message = "Replace with `DisposableHandle`",
- replaceWith = ReplaceWith("DisposableHandle"))
- public interface Registration {
- /**
- * Unregisters completion handler.
- * @suppress **Deprecated**: Replace with `dispose`
- */
- @Deprecated(message = "Replace with `dispose`",
- replaceWith = ReplaceWith("dispose()"))
- public fun unregister()
- }
}
/**
@@ -366,26 +349,17 @@
* It is optionally a child of a [parent] job.
*/
@Suppress("FunctionName")
-public actual fun Job(parent: Job? = null): Job = JobImpl(parent)
+public fun Job(parent: Job? = null): Job = JobImpl(parent)
/**
* A handle to an allocated object that can be disposed to make it eligible for garbage collection.
*/
-@Suppress("DEPRECATION") // todo: remove when Job.Registration is removed
-public actual interface DisposableHandle : Job.Registration {
+public interface DisposableHandle {
/**
* Disposes the corresponding object, making it eligible for garbage collection.
* Repeated invocation of this function has no effect.
*/
- public actual fun dispose()
-
- /**
- * Unregisters completion handler.
- * @suppress **Deprecated**: Replace with `dispose`
- */
- @Deprecated(message = "Replace with `dispose`",
- replaceWith = ReplaceWith("dispose()"))
- public override fun unregister() = dispose()
+ public fun dispose()
}
// -------------------- Job extensions --------------------
@@ -402,7 +376,7 @@
@Deprecated(message = "Renamed to `disposeOnCompletion`",
replaceWith = ReplaceWith("disposeOnCompletion(registration)"))
public fun Job.unregisterOnCompletion(registration: DisposableHandle): DisposableHandle =
- invokeOnCompletion(handler = DisposeOnCompletion(this, registration))
+ invokeOnCompletion(handler = DisposeOnCompletion(this, registration).asHandler)
/**
* Disposes a specified [handle] when this job is complete.
@@ -412,19 +386,8 @@
* invokeOnCompletion { handle.dispose() }
* ```
*/
-public actual fun Job.disposeOnCompletion(handle: DisposableHandle): DisposableHandle =
- invokeOnCompletion(handler = DisposeOnCompletion(this, handle))
-
-/**
- * Cancels a specified [future] when this job is complete.
- *
- * This is a shortcut for the following code with slightly more efficient implementation (one fewer object created).
- * ```
- * invokeOnCompletion { future.cancel(false) }
- * ```
- */
-public fun Job.cancelFutureOnCompletion(future: Future<*>): DisposableHandle =
- invokeOnCompletion(handler = CancelFutureOnCompletion(this, future))
+public fun Job.disposeOnCompletion(handle: DisposableHandle): DisposableHandle =
+ invokeOnCompletion(handler = DisposeOnCompletion(this, handle).asHandler)
/**
* Cancels the job and suspends invoking coroutine until the cancelled job is complete.
@@ -440,7 +403,7 @@
*
* This is a shortcut for the invocation of [cancel][Job.cancel] followed by [join][Job.join].
*/
-public actual suspend fun Job.cancelAndJoin() {
+public suspend fun Job.cancelAndJoin() {
cancel()
return join()
}
@@ -450,7 +413,7 @@
* for all of them. Unlike [Job.cancel] on this job as a whole, the state of this job itself is not affected.
*/
@Suppress("EXTENSION_SHADOWED_BY_MEMBER") // See KT-21598
-public actual fun Job.cancelChildren(cause: Throwable? = null) {
+public fun Job.cancelChildren(cause: Throwable? = null) {
children.forEach { it.cancel(cause) }
}
@@ -459,7 +422,7 @@
* [Job.join] for all of them. Unlike [Job.join] on this job as a whole, it does not wait until
* this job is complete.
*/
-public actual suspend fun Job.joinChildren() {
+public suspend fun Job.joinChildren() {
children.forEach { it.join() }
}
@@ -481,7 +444,7 @@
* The `coroutineContext.isActive` expression is a shortcut for `coroutineContext[Job]?.isActive == true`.
* See [Job.isActive].
*/
-public actual val CoroutineContext.isActive: Boolean
+public val CoroutineContext.isActive: Boolean
get() = this[Job]?.isActive == true
/**
@@ -489,7 +452,7 @@
* cancelled as a result of this invocation and `false` if there is no job in the context or if it was already
* cancelled or completed. See [Job.cancel] for details.
*/
-public actual fun CoroutineContext.cancel(cause: Throwable? = null): Boolean =
+public fun CoroutineContext.cancel(cause: Throwable? = null): Boolean =
this[Job]?.cancel(cause) ?: false
/**
@@ -497,8 +460,8 @@
* It does not do anything if there is no job in the context or it has no children.
* See [Job.cancelChildren] for details.
*/
-public actual fun CoroutineContext.cancelChildren(cause: Throwable? = null) {
- this[Job]?.cancelChildren(cause)
+public fun CoroutineContext.cancelChildren(cause: Throwable? = null) {
+ this[Job]?.cancelChildren(cause)
}
/**
@@ -509,20 +472,11 @@
public suspend fun Job.join() = this.join()
/**
- * No-op implementation of [Job.Registration].
- * @suppress: **Deprecated**: Replace with [NonDisposableHandle]
- */
-@Deprecated(message = "Replace with `NonDisposableHandle`",
- replaceWith = ReplaceWith("NonDisposableHandle"))
-@Suppress("unused")
-typealias EmptyRegistration = NonDisposableHandle
-
-/**
* No-op implementation of [DisposableHandle].
*/
-public actual object NonDisposableHandle : DisposableHandle {
+public object NonDisposableHandle : DisposableHandle {
/** Does not do anything. */
- actual override fun dispose() {}
+ override fun dispose() {}
/** Returns "NonDisposableHandle" string. */
override fun toString(): String = "NonDisposableHandle"
@@ -540,8 +494,8 @@
* @param active when `true` the job is created in _active_ state, when `false` in _new_ state. See [Job] for details.
* @suppress **This is unstable API and it is subject to change.**
*/
-internal actual open class JobSupport actual constructor(active: Boolean) : Job, SelectClause0 {
- actual final override val key: CoroutineContext.Key<*> get() = Job
+internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0 {
+ final override val key: CoroutineContext.Key<*> get() = Job
/*
=== Internal states ===
@@ -563,7 +517,7 @@
=== Transitions ===
New states Active states Inactive states
-
+
+---------+ +---------+ }
| EMPTY_N | --+-> | EMPTY_A | ----+ } Empty states
+---------+ | +---------+ | }
@@ -613,7 +567,7 @@
* It shall be invoked at most once after construction after all other initialization.
* @suppress **This is unstable API and it is subject to change.**
*/
- internal actual fun initParentJobInternal(parent: Job?) {
+ internal fun initParentJobInternal(parent: Job?) {
check(parentHandle == null)
if (parent == null) {
parentHandle = NonDisposableHandle
@@ -652,14 +606,14 @@
}
}
- public actual final override val isActive: Boolean get() {
+ public final override val isActive: Boolean get() {
val state = this.state
return state is Incomplete && state.isActive
}
- public actual final override val isCompleted: Boolean get() = state !is Incomplete
+ public final override val isCompleted: Boolean get() = state !is Incomplete
- public actual final override val isCancelled: Boolean get() {
+ public final override val isCancelled: Boolean get() {
val state = this.state
return state is Cancelled || (state is Finishing && state.cancelled != null)
}
@@ -695,7 +649,7 @@
if (proposedUpdate !is CompletedExceptionally) return cancelled // not exception -- just use original cancelled
val exception = proposedUpdate.exception
if (cancelled.exception == exception) return cancelled // that is the cancelled we need already!
- cancelled.cause?.let { exception.addSuppressed(it) }
+ cancelled.cause?.let { exception.addSuppressedThrowable(it) }
return Cancelled(this, exception)
}
@@ -742,7 +696,7 @@
try {
node.invoke(cause)
} catch (ex: Throwable) {
- exception?.apply { addSuppressed(ex) } ?: run {
+ exception?.apply { addSuppressedThrowable(ex) } ?: run {
exception = CompletionHandlerException("Exception in completion handler $node for $this", ex)
}
}
@@ -756,7 +710,7 @@
private fun notifyCancellation(list: NodeList, cause: Throwable?) =
notifyHandlers<JobCancellationNode<*>>(list, cause)
- public actual final override fun start(): Boolean {
+ public final override fun start(): Boolean {
loopOnState { state ->
when (startInternal(state)) {
FALSE -> return false
@@ -790,9 +744,9 @@
* Override to provide the actual [start] action.
* This function is invoked exactly once when non-active coroutine is [started][start].
*/
- internal actual open fun onStartInternal() {}
+ internal open fun onStartInternal() {}
- public actual final override fun getCancellationException(): CancellationException {
+ public final override fun getCancellationException(): CancellationException {
val state = this.state
return when {
state is Finishing && state.cancelled != null ->
@@ -838,7 +792,7 @@
invokeOnCompletion(onCancelling = onCancelling_, invokeImmediately = true, handler = handler)
// todo: non-final as a workaround for KT-21968, should be final in the future
- public actual override fun invokeOnCompletion(
+ public override fun invokeOnCompletion(
onCancelling: Boolean,
invokeImmediately: Boolean,
handler: CompletionHandler
@@ -870,7 +824,9 @@
}
}
else -> { // is complete
- if (invokeImmediately) handler((state as? CompletedExceptionally)?.cause)
+ // :KLUDGE: We have to invoke a handler in platform-specific way via `invokeIt` extension,
+ // because we play type tricks on Kotlin/JS and handler is not necessarily a function there
+ if (invokeImmediately) handler.invokeIt((state as? CompletedExceptionally)?.cause)
return NonDisposableHandle
}
}
@@ -899,12 +855,12 @@
// try to promote it to list (SINGLE+ state)
state.addOneIfEmpty(NodeList(active = true))
// it must be in SINGLE+ state or state has changed (node could have need removed from state)
- val list = state.next // either NodeList or somebody else won the race, updated state
+ val list = state.nextNode // either our NodeList or somebody else won the race, updated state
// just attempt converting it to list if state is still the same, then we'll continue lock-free loop
_state.compareAndSet(state, list)
}
- public actual final override suspend fun join() {
+ public final override suspend fun join() {
if (!joinInternal()) { // fast-path no wait
return suspendCoroutineOrReturn { cont ->
cont.context.checkCompletion()
@@ -922,7 +878,7 @@
}
private suspend fun joinSuspend() = suspendCancellableCoroutine<Unit> { cont ->
- cont.disposeOnCompletion(invokeOnCompletion(handler = ResumeOnCompletion(this, cont)))
+ cont.disposeOnCompletion(invokeOnCompletion(handler = ResumeOnCompletion(this, cont).asHandler))
}
public final override val onJoin: SelectClause0
@@ -943,7 +899,7 @@
}
if (startInternal(state) == 0) {
// slow-path -- register waiter for completion
- select.disposeOnSelect(invokeOnCompletion(handler = SelectJoinOnCompletion(this, select, block)))
+ select.disposeOnSelect(invokeOnCompletion(handler = SelectJoinOnCompletion(this, select, block).asHandler))
return
}
}
@@ -976,7 +932,7 @@
*/
internal open val onCancelMode: Int get() = ON_CANCEL_MAKE_CANCELLING
- public actual override fun cancel(cause: Throwable?): Boolean = when (onCancelMode) {
+ public override fun cancel(cause: Throwable?): Boolean = when (onCancelMode) {
ON_CANCEL_MAKE_CANCELLED -> makeCancelled(cause)
ON_CANCEL_MAKE_CANCELLING -> makeCancelling(cause)
ON_CANCEL_MAKE_COMPLETING -> makeCompletingOnCancel(cause)
@@ -1062,7 +1018,7 @@
* @throws IllegalStateException if job is already complete or completing
* @suppress **This is unstable API and it is subject to change.**
*/
- internal actual fun makeCompletingOnce(proposedUpdate: Any?, mode: Int): Boolean =
+ internal fun makeCompletingOnce(proposedUpdate: Any?, mode: Int): Boolean =
when (makeCompletingInternal(proposedUpdate, mode)) {
COMPLETING_COMPLETED -> true
COMPLETING_WAITING_CHILDREN -> false
@@ -1077,23 +1033,23 @@
if (state is Finishing && state.completing)
return COMPLETING_ALREADY_COMPLETING
val child: Child? = firstChild(state) ?: // or else complete immediately w/o children
- when {
- state !is Finishing && hasOnFinishingHandler(proposedUpdate) -> null // unless it has onFinishing handler
- updateState(state, proposedUpdate, mode) -> return COMPLETING_COMPLETED
- else -> return@loopOnState
- }
+ when {
+ state !is Finishing && hasOnFinishingHandler(proposedUpdate) -> null // unless it has onFinishing handler
+ updateState(state, proposedUpdate, mode) -> return COMPLETING_COMPLETED
+ else -> return@loopOnState
+ }
val list = state.list ?: // must promote to list to correctly operate on child lists
- when (state) {
- is Empty -> {
- promoteEmptyToNodeList(state)
- return@loopOnState // retry
- }
- is JobNode<*> -> {
- promoteSingleToNodeList(state)
- return@loopOnState // retry
- }
- else -> error("Unexpected state with an empty list: $state")
+ when (state) {
+ is Empty -> {
+ promoteEmptyToNodeList(state)
+ return@loopOnState // retry
}
+ is JobNode<*> -> {
+ promoteSingleToNodeList(state)
+ return@loopOnState // retry
+ }
+ else -> error("Unexpected state with an empty list: $state")
+ }
// cancel all children in list on exceptional completion
if (proposedUpdate is CompletedExceptionally)
child?.cancelChildrenInternal(proposedUpdate.exception)
@@ -1124,7 +1080,7 @@
// return false when there is no more incomplete children to wait
private tailrec fun tryWaitForChild(child: Child, proposedUpdate: Any?): Boolean {
val handle = child.childJob.invokeOnCompletion(invokeImmediately = false,
- handler = ChildCompletion(this, child, proposedUpdate))
+ handler = ChildCompletion(this, child, proposedUpdate).asHandler)
if (handle !== NonDisposableHandle) return true // child is not complete and we've started waiting for it
val nextChild = child.nextChild() ?: return false
return tryWaitForChild(nextChild, proposedUpdate)
@@ -1148,16 +1104,16 @@
private fun LockFreeLinkedListNode.nextChild(): Child? {
var cur = this
- while (cur.isRemoved) cur = cur.prev.unwrap() // rollback to prev non-removed (or list head)
+ while (cur.isRemoved) cur = cur.prevNode // rollback to prev non-removed (or list head)
while (true) {
- cur = cur.next.unwrap()
+ cur = cur.nextNode
if (cur.isRemoved) continue
if (cur is Child) return cur
if (cur is NodeList) return null // checked all -- no more children
}
}
- public actual final override val children: Sequence<Job> get() = buildSequence {
+ public final override val children: Sequence<Job> get() = buildSequence {
val state = this@JobSupport.state
when (state) {
is Child -> yield(state.childJob)
@@ -1168,8 +1124,8 @@
}
@Suppress("OverridingDeprecatedMember")
- public actual final override fun attachChild(child: Job): DisposableHandle =
- invokeOnCompletion(onCancelling = true, handler = Child(this, child))
+ public final override fun attachChild(child: Job): DisposableHandle =
+ invokeOnCompletion(onCancelling = true, handler = Child(this, child).asHandler)
@Suppress("OverridingDeprecatedMember")
public final override fun cancelChildren(cause: Throwable?) {
@@ -1181,7 +1137,7 @@
* installed via [invokeOnCompletion].
* @suppress **This is unstable API and it is subject to change.**
*/
- internal actual open fun handleException(exception: Throwable) {
+ internal open fun handleException(exception: Throwable) {
throw exception
}
@@ -1192,24 +1148,24 @@
* null when it has completed normally.
* @suppress **This is unstable API and it is subject to change.**
*/
- internal actual open fun onCancellationInternal(exceptionally: CompletedExceptionally?) {}
+ internal open fun onCancellationInternal(exceptionally: CompletedExceptionally?) {}
/**
* @suppress **This is unstable API and it is subject to change.**
*/
- internal actual open fun hasOnFinishingHandler(update: Any?) = false
+ internal open fun hasOnFinishingHandler(update: Any?) = false
/**
* @suppress **This is unstable API and it is subject to change.**
*/
- internal actual open fun onFinishingInternal(update: Any?) {}
+ internal open fun onFinishingInternal(update: Any?) {}
/**
* Override for post-completion actions that need to do something with the state.
* @param mode completion mode.
* @suppress **This is unstable API and it is subject to change.**
*/
- internal actual open fun onCompletionInternal(state: Any?, mode: Int) {}
+ internal open fun onCompletionInternal(state: Any?, mode: Int) {}
// for nicer debugging
public override fun toString(): String =
@@ -1218,7 +1174,7 @@
/**
* @suppress **This is unstable API and it is subject to change.**
*/
- internal actual open fun nameString(): String = this::class.java.simpleName
+ internal open fun nameString(): String = classSimpleName
private fun stateString(): String {
val state = this.state
@@ -1234,14 +1190,6 @@
}
}
- /**
- * @suppress **This is unstable API and it is subject to change.**
- */
- internal interface Incomplete {
- val isActive: Boolean
- val list: NodeList? // is null only for Empty and JobNode incomplete state objects
- }
-
// Cancelling or Completing
private class Finishing(
override val list: NodeList,
@@ -1254,36 +1202,6 @@
private val Incomplete.isCancelling: Boolean
get() = this is Finishing && cancelled != null
- /**
- * @suppress **This is unstable API and it is subject to change.**
- */
- internal class NodeList(
- active: Boolean
- ) : LockFreeLinkedListHead(), Incomplete {
- private val _active = atomic(if (active) 1 else 0)
-
- override val isActive: Boolean get() = _active.value != 0
- override val list: NodeList get() = this
-
- fun tryMakeActive(): Int {
- if (_active.value != 0) return FALSE
- if (_active.compareAndSet(0, 1)) return TRUE
- return RETRY
- }
-
- override fun toString(): String = buildString {
- append("List")
- append(if (isActive) "{Active}" else "{New}")
- append("[")
- var first = true
- this@NodeList.forEach<JobNode<*>> { node ->
- if (first) first = false else append(", ")
- append(node)
- }
- append("]")
- }
- }
-
/*
* =================================================================================================
* This is ready-to-use implementation for Deferred interface.
@@ -1292,9 +1210,9 @@
* =================================================================================================
*/
- public actual val isCompletedExceptionally: Boolean get() = state is CompletedExceptionally
+ public val isCompletedExceptionally: Boolean get() = state is CompletedExceptionally
- public actual fun getCompletionExceptionOrNull(): Throwable? {
+ public fun getCompletionExceptionOrNull(): Throwable? {
val state = this.state
check(state !is Incomplete) { "This job has not completed yet" }
return state.exceptionOrNull
@@ -1360,7 +1278,7 @@
}
if (startInternal(state) == 0) {
// slow-path -- register waiter for completion
- select.disposeOnSelect(invokeOnCompletion(handler = SelectAwaitOnCompletion(this, select, block)))
+ select.disposeOnSelect(invokeOnCompletion(handler = SelectAwaitOnCompletion(this, select, block).asHandler))
return
}
}
@@ -1369,8 +1287,8 @@
/**
* @suppress **This is unstable API and it is subject to change.**
*/
- @Suppress("UNCHECKED_CAST")
- internal fun <T, R> selectAwaitCompletion(select: SelectInstance<R>, block: suspend (T) -> R) {
+ @Suppress("UNCHECKED_CAST")
+ internal fun <T, R> selectAwaitCompletion(select: SelectInstance<R>, block: suspend (T) -> R) {
val state = this.state
// Note: await is non-atomic (can be cancelled while dispatched)
if (state is CompletedExceptionally)
@@ -1397,8 +1315,8 @@
@Suppress("PrivatePropertyName")
private val EmptyActive = Empty(true)
-private class Empty(override val isActive: Boolean) : JobSupport.Incomplete {
- override val list: JobSupport.NodeList? get() = null
+private class Empty(override val isActive: Boolean) : Incomplete {
+ override val list: NodeList? get() = null
override fun toString(): String = "Empty{${if (isActive) "Active" else "New" }}"
}
@@ -1409,28 +1327,59 @@
// -------- invokeOnCompletion nodes
-internal abstract class JobNode<out J : Job>(
+internal interface Incomplete {
+ val isActive: Boolean
+ val list: NodeList? // is null only for Empty and JobNode incomplete state objects
+}
+
+internal abstract class JobNode<out J : Job> actual constructor(
@JvmField val job: J
-) : LockFreeLinkedListNode(), DisposableHandle, CompletionHandler, JobSupport.Incomplete {
- final override val isActive: Boolean get() = true
- final override val list: JobSupport.NodeList? get() = null
- final override fun dispose() = (job as JobSupport).removeNode(this)
- abstract override fun invoke(reason: Throwable?)
+) : CompletionHandlerNode(), DisposableHandle, Incomplete {
+ override val isActive: Boolean get() = true
+ override val list: NodeList? get() = null
+ override fun dispose() = (job as JobSupport).removeNode(this)
+}
+
+internal class NodeList(
+ active: Boolean
+) : LockFreeLinkedListHead(), Incomplete {
+ private val _active = atomic(if (active) 1 else 0)
+
+ override val isActive: Boolean get() = _active.value != 0
+ override val list: NodeList get() = this
+
+ fun tryMakeActive(): Int {
+ if (_active.value != 0) return FALSE
+ if (_active.compareAndSet(0, 1)) return TRUE
+ return RETRY
+ }
+
+ override fun toString(): String = buildString {
+ append("List")
+ append(if (isActive) "{Active}" else "{New}")
+ append("[")
+ var first = true
+ this@NodeList.forEach<JobNode<*>> { node ->
+ if (first) first = false else append(", ")
+ append(node)
+ }
+ append("]")
+ }
}
private class InvokeOnCompletion(
job: Job,
private val handler: CompletionHandler
) : JobNode<Job>(job) {
- override fun invoke(reason: Throwable?) = handler.invoke(reason)
- override fun toString() = "InvokeOnCompletion[${handler::class.java.name}@${Integer.toHexString(System.identityHashCode(handler))}]"
+ override fun invoke(cause: Throwable?) = handler.invoke(cause)
+ override fun toString() = "InvokeOnCompletion[$classSimpleName@$hexAddress]"
}
private class ResumeOnCompletion(
job: Job,
private val continuation: Continuation<Unit>
) : JobNode<Job>(job) {
- override fun invoke(reason: Throwable?) = continuation.resume(Unit)
+ override fun invoke(cause: Throwable?) = continuation.resume(Unit)
override fun toString() = "ResumeOnCompletion[$continuation]"
}
@@ -1438,28 +1387,16 @@
job: Job,
private val handle: DisposableHandle
) : JobNode<Job>(job) {
- override fun invoke(reason: Throwable?) = handle.dispose()
+ override fun invoke(cause: Throwable?) = handle.dispose()
override fun toString(): String = "DisposeOnCompletion[$handle]"
}
-private class CancelFutureOnCompletion(
- job: Job,
- private val future: Future<*>
-) : JobNode<Job>(job) {
- override fun invoke(reason: Throwable?) {
- // Don't interrupt when cancelling future on completion, because no one is going to reset this
- // interruption flag and it will cause spurious failures elsewhere
- future.cancel(false)
- }
- override fun toString() = "CancelFutureOnCompletion[$future]"
-}
-
private class SelectJoinOnCompletion<R>(
job: JobSupport,
private val select: SelectInstance<R>,
private val block: suspend () -> R
) : JobNode<JobSupport>(job) {
- override fun invoke(reason: Throwable?) {
+ override fun invoke(cause: Throwable?) {
if (select.trySelect(null))
block.startCoroutineCancellable(select.completion)
}
@@ -1471,7 +1408,7 @@
private val select: SelectInstance<R>,
private val block: suspend (T) -> R
) : JobNode<JobSupport>(job) {
- override fun invoke(reason: Throwable?) {
+ override fun invoke(cause: Throwable?) {
if (select.trySelect(null))
job.selectAwaitCompletion(select, block)
}
@@ -1492,17 +1429,17 @@
) : JobCancellationNode<Job>(job) {
// delegate handler shall be invoked at most once, so here is an additional flag
private val _invoked = atomic(0)
- override fun invoke(reason: Throwable?) {
- if (_invoked.compareAndSet(0, 1)) handler.invoke(reason)
+ override fun invoke(cause: Throwable?) {
+ if (_invoked.compareAndSet(0, 1)) handler.invoke(cause)
}
- override fun toString() = "InvokeOnCancellation[${handler::class.java.name}@${Integer.toHexString(System.identityHashCode(handler))}]"
+ override fun toString() = "InvokeOnCancellation[$classSimpleName@$hexAddress]"
}
internal class Child(
parent: JobSupport,
@JvmField val childJob: Job
) : JobCancellationNode<JobSupport>(parent) {
- override fun invoke(reason: Throwable?) {
+ override fun invoke(cause: Throwable?) {
// Always materialize the actual instance of parent's completion exception and cancel child with it
childJob.cancel(job.getCancellationException())
}
@@ -1514,8 +1451,7 @@
private val child: Child,
private val proposedUpdate: Any?
) : JobNode<Job>(child.childJob) {
- override fun invoke(reason: Throwable?) {
+ override fun invoke(cause: Throwable?) {
parent.continueCompleting(child, proposedUpdate)
}
-}
-
+}
\ No newline at end of file
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/NonCancellable.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/NonCancellable.kt
similarity index 72%
rename from core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/NonCancellable.kt
rename to common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/NonCancellable.kt
index 51aa9cf..4dc1b8a 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/NonCancellable.kt
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/NonCancellable.kt
@@ -17,8 +17,8 @@
package kotlinx.coroutines.experimental
import kotlinx.coroutines.experimental.NonCancellable.isActive
-import kotlinx.coroutines.experimental.selects.SelectClause0
-import kotlin.coroutines.experimental.AbstractCoroutineContextElement
+import kotlinx.coroutines.experimental.selects.*
+import kotlin.coroutines.experimental.*
/**
* A non-cancelable job that is always [active][isActive]. It is designed for [withContext] function
@@ -31,21 +31,21 @@
* }
* ```
*/
-public actual object NonCancellable : AbstractCoroutineContextElement(Job), Job {
+public object NonCancellable : AbstractCoroutineContextElement(Job), Job {
/** Always returns `true`. */
- actual override val isActive: Boolean get() = true
+ override val isActive: Boolean get() = true
/** Always returns `false`. */
- actual override val isCompleted: Boolean get() = false
+ override val isCompleted: Boolean get() = false
/** Always returns `false`. */
- actual override val isCancelled: Boolean get() = false
+ override val isCancelled: Boolean get() = false
/** Always returns `false`. */
- actual override fun start(): Boolean = false
+ override fun start(): Boolean = false
/** Always throws [UnsupportedOperationException]. */
- actual suspend override fun join() {
+ override suspend fun join() {
throw UnsupportedOperationException("This job is always active")
}
@@ -53,7 +53,7 @@
get() = throw UnsupportedOperationException("This job is always active")
/** Always throws [IllegalStateException]. */
- actual override fun getCancellationException(): CancellationException = throw IllegalStateException("This job is always active")
+ override fun getCancellationException(): CancellationException = throw IllegalStateException("This job is always active")
/** Always returns [NonDisposableHandle]. */
@Suppress("OverridingDeprecatedMember")
@@ -71,19 +71,19 @@
NonDisposableHandle
/** Always returns [NonDisposableHandle]. */
- actual override fun invokeOnCompletion(onCancelling: Boolean, invokeImmediately: Boolean, handler: CompletionHandler): DisposableHandle =
+ override fun invokeOnCompletion(onCancelling: Boolean, invokeImmediately: Boolean, handler: CompletionHandler): DisposableHandle =
NonDisposableHandle
/** Always returns `false`. */
- actual override fun cancel(cause: Throwable?): Boolean = false
+ override fun cancel(cause: Throwable?): Boolean = false
/** Always returns [emptySequence]. */
- actual override val children: Sequence<Job>
+ override val children: Sequence<Job>
get() = emptySequence()
/** Always returns [NonDisposableHandle] and does not do anything. */
@Suppress("OverridingDeprecatedMember")
- actual override fun attachChild(child: Job): DisposableHandle = NonDisposableHandle
+ override fun attachChild(child: Job): DisposableHandle = NonDisposableHandle
/** Does not do anything. */
@Suppress("OverridingDeprecatedMember")
diff --git a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonDelay.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Runnable.common.kt
similarity index 80%
rename from common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonDelay.kt
rename to common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Runnable.common.kt
index 51a8a88..4152946 100644
--- a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonDelay.kt
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Runnable.common.kt
@@ -16,6 +16,9 @@
package kotlinx.coroutines.experimental
-public expect interface Delay
+public expect interface Runnable {
+ public fun run()
+}
-public expect suspend fun delay(time: Int)
+@Suppress("FunctionName")
+public expect inline fun Runnable(crossinline block: () -> Unit): Runnable
\ No newline at end of file
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Scheduled.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Scheduled.kt
similarity index 89%
rename from core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Scheduled.kt
rename to common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Scheduled.kt
index b881d98..93a522e 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Scheduled.kt
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Scheduled.kt
@@ -16,9 +16,9 @@
package kotlinx.coroutines.experimental
+import kotlinx.coroutines.experimental.internalAnnotations.*
import kotlinx.coroutines.experimental.intrinsics.*
-import kotlinx.coroutines.experimental.selects.*
-import java.util.concurrent.*
+import kotlinx.coroutines.experimental.timeunit.*
import kotlin.coroutines.experimental.*
import kotlin.coroutines.experimental.intrinsics.*
@@ -39,7 +39,7 @@
*
* @param time timeout time in milliseconds.
*/
-public actual suspend fun <T> withTimeout(time: Int, block: suspend CoroutineScope.() -> T): T =
+public suspend fun <T> withTimeout(time: Int, block: suspend CoroutineScope.() -> T): T =
withTimeout(time.toLong(), TimeUnit.MILLISECONDS, block)
/**
@@ -129,7 +129,7 @@
*
* @param time timeout time in milliseconds.
*/
-public actual suspend fun <T> withTimeoutOrNull(time: Int, block: suspend CoroutineScope.() -> T): T? =
+public suspend fun <T> withTimeoutOrNull(time: Int, block: suspend CoroutineScope.() -> T): T? =
withTimeoutOrNull(time.toLong(), TimeUnit.MILLISECONDS, block)
/**
@@ -182,3 +182,22 @@
}
}
+/**
+ * This exception is thrown by [withTimeout] to indicate timeout.
+ */
+public class TimeoutCancellationException internal constructor(
+ message: String,
+ @JvmField internal val coroutine: Job?
+) : CancellationException(message) {
+ /**
+ * Creates timeout exception with a given message.
+ */
+ public constructor(message: String) : this(message, null)
+}
+
+@Suppress("FunctionName")
+internal fun TimeoutCancellationException(
+ time: Long,
+ unit: TimeUnit,
+ coroutine: Job
+) : TimeoutCancellationException = TimeoutCancellationException("Timed out waiting for $time $unit", coroutine)
diff --git a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Unconfined.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Unconfined.kt
new file mode 100644
index 0000000..bb7397e
--- /dev/null
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Unconfined.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2016-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package kotlinx.coroutines.experimental
+
+import kotlin.coroutines.experimental.*
+
+/**
+ * A coroutine dispatcher that is not confined to any specific thread.
+ * It executes initial continuation of the coroutine _right here_ in the current call-frame
+ * and let the coroutine resume in whatever thread that is used by the corresponding suspending function, without
+ * mandating any specific threading policy.
+ *
+ * Note, that if you need your coroutine to be confined to a particular thread or a thread-pool after resumption,
+ * but still want to execute it in the current call-frame until its first suspension, then you can use
+ * an optional [CoroutineStart] parameter in coroutine builders like [launch] and [async] setting it to the
+ * the value of [CoroutineStart.UNDISPATCHED].
+ */
+public object Unconfined : CoroutineDispatcher() {
+ override fun isDispatchNeeded(context: CoroutineContext): Boolean = false
+ override fun dispatch(context: CoroutineContext, block: Runnable) { throw UnsupportedOperationException() }
+ override fun toString(): String = "Unconfined"
+}
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Yield.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Yield.kt
similarity index 84%
rename from core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Yield.kt
rename to common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Yield.kt
index 833a728..2a53760 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Yield.kt
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/Yield.kt
@@ -16,9 +16,8 @@
package kotlinx.coroutines.experimental
-import kotlin.coroutines.experimental.CoroutineContext
-import kotlin.coroutines.experimental.intrinsics.COROUTINE_SUSPENDED
-import kotlin.coroutines.experimental.intrinsics.suspendCoroutineOrReturn
+import kotlin.coroutines.experimental.*
+import kotlin.coroutines.experimental.intrinsics.*
/**
* Yields a thread (or thread pool) of the current coroutine dispatcher to other coroutines to run.
@@ -28,7 +27,7 @@
* If the [Job] of the current coroutine is cancelled or completed when this suspending function is invoked or while
* this function is waiting for dispatching, it resumes with [CancellationException].
*/
-public actual suspend fun yield(): Unit = suspendCoroutineOrReturn sc@ { cont ->
+public suspend fun yield(): Unit = suspendCoroutineOrReturn sc@ { cont ->
val context = cont.context
context.checkCompletion()
if (cont !is DispatchedContinuation<Unit>) return@sc Unit
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/internal/Atomic.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/internal/Atomic.kt
similarity index 98%
rename from core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/internal/Atomic.kt
rename to common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/internal/Atomic.kt
index 1eaa6e6..ce19e05 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/internal/Atomic.kt
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/internal/Atomic.kt
@@ -41,7 +41,7 @@
* by Timothy L. Harris, Keir Fraser and Ian A. Pratt.
*
* Note: parts of atomic operation must be globally ordered. Otherwise, this implementation will produce
- * [StackOverflowError].
+ * `StackOverflowError`.
*
* @suppress **This is unstable API and it is subject to change.**
*/
diff --git a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/internal/LockFreeLinkedList.common.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/internal/LockFreeLinkedList.common.kt
new file mode 100644
index 0000000..9cc3e14
--- /dev/null
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/internal/LockFreeLinkedList.common.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2016-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package kotlinx.coroutines.experimental.internal
+
+import kotlin.jvm.*
+
+/** @suppress **This is unstable API and it is subject to change.** */
+public expect open class LockFreeLinkedListNode() {
+ public val isRemoved: Boolean
+ public val nextNode: LockFreeLinkedListNode
+ public val prevNode: LockFreeLinkedListNode
+ public fun addLast(node: LockFreeLinkedListNode)
+ public fun addOneIfEmpty(node: LockFreeLinkedListNode): Boolean
+ public inline fun addLastIf(node: LockFreeLinkedListNode, crossinline condition: () -> Boolean): Boolean
+ public inline fun addLastIfPrev(
+ node: LockFreeLinkedListNode,
+ predicate: (LockFreeLinkedListNode) -> Boolean
+ ): Boolean
+
+ public inline fun addLastIfPrevAndIf(
+ node: LockFreeLinkedListNode,
+ predicate: (LockFreeLinkedListNode) -> Boolean, // prev node predicate
+ crossinline condition: () -> Boolean // atomically checked condition
+ ): Boolean
+
+ public open fun remove(): Boolean
+ public fun removeFirstOrNull(): LockFreeLinkedListNode?
+ public inline fun <reified T> removeFirstIfIsInstanceOfOrPeekIf(predicate: (T) -> Boolean): T?
+}
+
+/** @suppress **This is unstable API and it is subject to change.** */
+public expect open class LockFreeLinkedListHead() : LockFreeLinkedListNode {
+ public val isEmpty: Boolean
+ public inline fun <reified T : LockFreeLinkedListNode> forEach(block: (T) -> Unit)
+ public final override fun remove(): Nothing
+}
+
+/** @suppress **This is unstable API and it is subject to change.** */
+public expect open class AddLastDesc<T : LockFreeLinkedListNode>(
+ queue: LockFreeLinkedListNode,
+ node: T
+) : AbstractAtomicDesc {
+ val queue: LockFreeLinkedListNode
+ val node: T
+ protected override fun onPrepare(affected: LockFreeLinkedListNode, next: LockFreeLinkedListNode): Any?
+}
+
+/** @suppress **This is unstable API and it is subject to change.** */
+public expect abstract class AbstractAtomicDesc : AtomicDesc {
+ protected abstract fun onPrepare(affected: LockFreeLinkedListNode, next: LockFreeLinkedListNode): Any?
+ final override fun prepare(op: AtomicOp<*>): Any?
+ final override fun complete(op: AtomicOp<*>, failure: Any?)
+}
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/selects/Select.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/selects/Select.kt
similarity index 90%
rename from core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/selects/Select.kt
rename to common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/selects/Select.kt
index 8c6b208..85ba53c 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/selects/Select.kt
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/selects/Select.kt
@@ -18,11 +18,10 @@
import kotlinx.atomicfu.*
import kotlinx.coroutines.experimental.*
-import kotlinx.coroutines.experimental.channels.*
import kotlinx.coroutines.experimental.internal.*
+import kotlinx.coroutines.experimental.internalAnnotations.*
import kotlinx.coroutines.experimental.intrinsics.*
-import kotlinx.coroutines.experimental.sync.*
-import java.util.concurrent.*
+import kotlinx.coroutines.experimental.timeunit.*
import kotlin.coroutines.experimental.*
import kotlin.coroutines.experimental.intrinsics.*
@@ -58,30 +57,6 @@
* @param unit timeout unit (milliseconds by default)
*/
public fun onTimeout(time: Long, unit: TimeUnit = TimeUnit.MILLISECONDS, block: suspend () -> R)
-
- /** @suppress **Deprecated: for binary compatibility only **/
- @Deprecated("for binary compatibility only", level=DeprecationLevel.HIDDEN)
- public fun Job.onJoin(block: suspend () -> R) { onJoin(block) }
-
- /** @suppress **Deprecated: for binary compatibility only **/
- @Deprecated("for binary compatibility only", level=DeprecationLevel.HIDDEN)
- public fun <T> Deferred<T>.onAwait(block: suspend (T) -> R) { onAwait(block) }
-
- /** @suppress **Deprecated: for binary compatibility only **/
- @Deprecated("for binary compatibility only", level=DeprecationLevel.HIDDEN)
- public fun Mutex.onLock(owner: Any? = null, block: suspend () -> R) { onLock { block() } }
-
- /** @suppress **Deprecated: for binary compatibility only **/
- @Deprecated("for binary compatibility only", level=DeprecationLevel.HIDDEN)
- public fun <E> SendChannel<E>.onSend(element: E, block: suspend () -> R) { onSend(element) { block() } }
-
- /** @suppress **Deprecated: for binary compatibility only **/
- @Deprecated("for binary compatibility only", level=DeprecationLevel.HIDDEN)
- public fun <E> ReceiveChannel<E>.onReceive(block: suspend (E) -> R) { onReceive(block) }
-
- /** @suppress **Deprecated: for binary compatibility only **/
- @Deprecated("for binary compatibility only", level=DeprecationLevel.HIDDEN)
- public fun <E> ReceiveChannel<E>.onReceiveOrNull(block: suspend (E?) -> R) { onReceiveOrNull(block) }
}
/**
@@ -222,7 +197,8 @@
@PublishedApi
internal class SelectBuilderImpl<in R>(
private val delegate: Continuation<R>
-) : LockFreeLinkedListHead(), SelectBuilder<R>, SelectInstance<R>, Continuation<R> {
+) : LockFreeLinkedListHead(), SelectBuilder<R>,
+ SelectInstance<R>, Continuation<R> {
// selection state is "this" (list of nodes) initially and is replaced by idempotent marker (or null) when selected
private val _state = atomic<Any?>(this)
@@ -258,7 +234,9 @@
_result.loop { result ->
when {
result === UNDECIDED -> if (_result.compareAndSet(UNDECIDED, value())) return
- result === COROUTINE_SUSPENDED -> if (_result.compareAndSet(COROUTINE_SUSPENDED, RESUMED)) {
+ result === COROUTINE_SUSPENDED -> if (_result.compareAndSet(COROUTINE_SUSPENDED,
+ RESUMED
+ )) {
block()
return
}
@@ -305,7 +283,8 @@
private fun initCancellability() {
val parent = context[Job] ?: return
- val newRegistration = parent.invokeOnCompletion(onCancelling = true, handler = SelectOnCancellation(parent))
+ val newRegistration = parent.invokeOnCompletion(
+ onCancelling = true, handler = SelectOnCancellation(parent).asHandler)
parentHandle = newRegistration
// now check our state _after_ registering
if (isSelected) newRegistration.dispose()
@@ -313,7 +292,7 @@
private inner class SelectOnCancellation(job: Job) : JobCancellationNode<Job>(job) {
// Note: may be invoked multiple times, but only the first trySelect succeeds anyway
- override fun invoke(reason: Throwable?) {
+ override fun invoke(cause: Throwable?) {
if (trySelect(null))
resumeSelectCancellableWithException(job.getCancellationException())
}
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/selects/SelectUnbiased.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/selects/SelectUnbiased.kt
similarity index 88%
rename from core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/selects/SelectUnbiased.kt
rename to common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/selects/SelectUnbiased.kt
index cf79b47..59bfb5d 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/selects/SelectUnbiased.kt
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/selects/SelectUnbiased.kt
@@ -16,10 +16,9 @@
package kotlinx.coroutines.experimental.selects
-import java.util.*
-import java.util.concurrent.TimeUnit
-import kotlin.coroutines.experimental.Continuation
-import kotlin.coroutines.experimental.intrinsics.suspendCoroutineOrReturn
+import kotlinx.coroutines.experimental.timeunit.*
+import kotlin.coroutines.experimental.*
+import kotlin.coroutines.experimental.intrinsics.*
/**
* Waits for the result of multiple suspending functions simultaneously like [select], but in an _unbiased_
@@ -31,7 +30,7 @@
*
* See [select] function description for all the other details.
*/
-public inline suspend fun <R> selectUnbiased(crossinline builder: SelectBuilder<R>.() -> Unit): R =
+public suspend inline fun <R> selectUnbiased(crossinline builder: SelectBuilder<R>.() -> Unit): R =
suspendCoroutineOrReturn { cont ->
val scope = UnbiasedSelectBuilderImpl(cont)
try {
@@ -44,7 +43,8 @@
@PublishedApi
-internal class UnbiasedSelectBuilderImpl<in R>(cont: Continuation<R>) : SelectBuilder<R> {
+internal class UnbiasedSelectBuilderImpl<in R>(cont: Continuation<R>) :
+ SelectBuilder<R> {
val instance = SelectBuilderImpl(cont)
val clauses = arrayListOf<() -> Unit>()
@@ -55,7 +55,7 @@
internal fun initSelectResult(): Any? {
if (!instance.isSelected) {
try {
- Collections.shuffle(clauses)
+ clauses.shuffle()
clauses.forEach { it.invoke() }
} catch (e: Throwable) {
instance.handleBuilderException(e)
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/selects/WhileSelect.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/selects/WhileSelect.kt
similarity index 100%
rename from core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/selects/WhileSelect.kt
rename to common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/selects/WhileSelect.kt
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/sync/Mutex.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/sync/Mutex.kt
similarity index 94%
rename from core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/sync/Mutex.kt
rename to common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/sync/Mutex.kt
index 3130085..9807059 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/sync/Mutex.kt
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/sync/Mutex.kt
@@ -16,16 +16,13 @@
package kotlinx.coroutines.experimental.sync
-import kotlinx.atomicfu.atomic
-import kotlinx.atomicfu.loop
+import kotlinx.atomicfu.*
import kotlinx.coroutines.experimental.*
import kotlinx.coroutines.experimental.internal.*
-import kotlinx.coroutines.experimental.intrinsics.startCoroutineUndispatched
-import kotlinx.coroutines.experimental.selects.ALREADY_SELECTED
-import kotlinx.coroutines.experimental.selects.SelectClause2
-import kotlinx.coroutines.experimental.selects.SelectInstance
-import kotlinx.coroutines.experimental.selects.select
-import kotlin.coroutines.experimental.startCoroutine
+import kotlinx.coroutines.experimental.intrinsics.*
+import kotlinx.coroutines.experimental.selects.*
+import kotlin.coroutines.experimental.*
+import kotlinx.coroutines.experimental.internalAnnotations.*
/**
* Mutual exclusion for coroutines.
@@ -36,19 +33,6 @@
*/
public interface Mutex {
/**
- * Factory for [Mutex] instances.
- * @suppress **Deprecated**
- */
- public companion object Factory {
- /**
- * Creates new [Mutex] instance.
- * @suppress **Deprecated**
- */
- @Deprecated("Replaced with top-level function", level = DeprecationLevel.HIDDEN)
- public operator fun invoke(locked: Boolean = false): Mutex = Mutex(locked)
- }
-
- /**
* Returns `true` when this mutex is locked.
*/
public val isLocked: Boolean
@@ -113,7 +97,9 @@
*
* @param locked initial state of the mutex.
*/
-public fun Mutex(locked: Boolean = false): Mutex = MutexImpl(locked)
+@Suppress("FunctionName")
+public fun Mutex(locked: Boolean = false): Mutex =
+ MutexImpl(locked)
/**
* Executes the given [action] under this mutex's lock.
@@ -122,7 +108,7 @@
*
* @return the return value of the action.
*/
-public inline suspend fun <T> Mutex.withLock(owner: Any? = null, action: () -> T): T {
+public suspend inline fun <T> Mutex.withLock(owner: Any? = null, action: () -> T): T {
lock(owner)
try {
return action()
@@ -200,7 +186,9 @@
when (state) {
is Empty -> {
if (state.locked !== UNLOCKED) return false
- val update = if (owner == null) EmptyLocked else Empty(owner)
+ val update = if (owner == null) EmptyLocked else Empty(
+ owner
+ )
if (_state.compareAndSet(state, update)) return true
}
is LockedQueue -> {
diff --git a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonYield.kt b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/timeunit/TimeUnit.common.kt
similarity index 78%
rename from common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonYield.kt
rename to common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/timeunit/TimeUnit.common.kt
index e461c47..d41e2d2 100644
--- a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonYield.kt
+++ b/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/timeunit/TimeUnit.common.kt
@@ -14,6 +14,11 @@
* limitations under the License.
*/
-package kotlinx.coroutines.experimental
+package kotlinx.coroutines.experimental.timeunit
-public expect suspend fun yield()
\ No newline at end of file
+public expect enum class TimeUnit {
+ MILLISECONDS,
+ SECONDS;
+
+ public fun toMillis(time: Long): Long
+}
\ No newline at end of file
diff --git a/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonAsyncLazyTest.kt b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/AsyncLazyTest.kt
similarity index 99%
rename from common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonAsyncLazyTest.kt
rename to common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/AsyncLazyTest.kt
index 32e100e..af3115c 100644
--- a/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonAsyncLazyTest.kt
+++ b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/AsyncLazyTest.kt
@@ -21,7 +21,7 @@
import kotlin.coroutines.experimental.*
import kotlin.test.*
-class CommonAsyncLazyTest : TestBase() {
+class AsyncLazyTest : TestBase() {
@Test
fun testSimple() = runTest {
expect(1)
diff --git a/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonAsyncTest.kt b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/AsyncTest.kt
similarity index 98%
rename from common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonAsyncTest.kt
rename to common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/AsyncTest.kt
index 28349be..2eac23c 100644
--- a/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonAsyncTest.kt
+++ b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/AsyncTest.kt
@@ -21,7 +21,7 @@
import kotlin.coroutines.experimental.*
import kotlin.test.*
-class CommonAsyncTest : TestBase() {
+class AsyncTest : TestBase() {
@Test
fun testSimple() = runTest {
expect(1)
diff --git a/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonAtomicCancellationTest.kt b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/AtomicCancellationCommonTest.kt
similarity index 70%
rename from common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonAtomicCancellationTest.kt
rename to common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/AtomicCancellationCommonTest.kt
index e1008c6..fe69a8a 100644
--- a/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonAtomicCancellationTest.kt
+++ b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/AtomicCancellationCommonTest.kt
@@ -16,10 +16,12 @@
package kotlinx.coroutines.experimental
+import kotlinx.coroutines.experimental.selects.*
+import kotlinx.coroutines.experimental.sync.*
import kotlin.coroutines.experimental.*
import kotlin.test.*
-class CommonAtomicCancellationTest : TestBase() {
+class AtomicCancellationCommonTest : TestBase() {
@Test
fun testCancellableLaunch() = runTest {
expect(1)
@@ -96,4 +98,42 @@
yield() // to jobToJoin & canceller
expect(6)
}
+
+ @Test
+ fun testLockAtomicCancel() = runTest {
+ expect(1)
+ val mutex = Mutex(true) // locked mutex
+ val job = launch(coroutineContext, start = CoroutineStart.UNDISPATCHED) {
+ expect(2)
+ mutex.lock() // suspends
+ expect(4) // should execute despite cancellation
+ }
+ expect(3)
+ mutex.unlock() // unlock mutex first
+ job.cancel() // cancel the job next
+ yield() // now yield
+ finish(5)
+ }
+
+ @Test
+ fun testSelectLockAtomicCancel() = runTest {
+ expect(1)
+ val mutex = Mutex(true) // locked mutex
+ val job = launch(coroutineContext, start = CoroutineStart.UNDISPATCHED) {
+ expect(2)
+ val result = select<String> { // suspends
+ mutex.onLock {
+ expect(4)
+ "OK"
+ }
+ }
+ assertEquals("OK", result)
+ expect(5) // should execute despite cancellation
+ }
+ expect(3)
+ mutex.unlock() // unlock mutex first
+ job.cancel() // cancel the job next
+ yield() // now yield
+ finish(6)
+ }
}
\ No newline at end of file
diff --git a/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonCompletableDeferredTest.kt b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CompletableDeferredTest.kt
similarity index 99%
rename from common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonCompletableDeferredTest.kt
rename to common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CompletableDeferredTest.kt
index 2e371d5..d7ea3ac 100644
--- a/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonCompletableDeferredTest.kt
+++ b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CompletableDeferredTest.kt
@@ -21,7 +21,7 @@
import kotlin.coroutines.experimental.*
import kotlin.test.*
-class CommonCompletableDeferredTest : TestBase() {
+class CompletableDeferredTest : TestBase() {
@Test
fun testFresh() {
val c = CompletableDeferred<String>()
diff --git a/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonCoroutineExceptionHandlerTest.kt b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CoroutineExceptionHandlerTest.kt
similarity index 95%
rename from common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonCoroutineExceptionHandlerTest.kt
rename to common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CoroutineExceptionHandlerTest.kt
index 4a69753..becd2c7 100644
--- a/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonCoroutineExceptionHandlerTest.kt
+++ b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CoroutineExceptionHandlerTest.kt
@@ -19,7 +19,7 @@
import kotlin.coroutines.experimental.*
import kotlin.test.*
-class CommonCoroutineExceptionHandlerTest : TestBase() {
+class CoroutineExceptionHandlerTest : TestBase() {
@Test
fun testCoroutineExceptionHandlerCreator() = runTest {
expect(1)
diff --git a/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonCoroutinesTest.kt b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CoroutinesTest.kt
similarity index 99%
rename from common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonCoroutinesTest.kt
rename to common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CoroutinesTest.kt
index 749475b..5ebd129 100644
--- a/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonCoroutinesTest.kt
+++ b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CoroutinesTest.kt
@@ -21,7 +21,7 @@
import kotlin.coroutines.experimental.*
import kotlin.test.*
-class CommonCoroutinesTest : TestBase() {
+class CoroutinesTest : TestBase() {
@Test
fun testSimple() = runTest {
expect(1)
diff --git a/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonJobTest.kt b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/JobTest.kt
similarity index 99%
rename from common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonJobTest.kt
rename to common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/JobTest.kt
index 3ff6f7e..7ecbfdf 100644
--- a/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonJobTest.kt
+++ b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/JobTest.kt
@@ -19,7 +19,7 @@
import kotlin.coroutines.experimental.*
import kotlin.test.*
-class CommonJobTest : TestBase() {
+class JobTest : TestBase() {
@Test
fun testState() {
val job = Job()
diff --git a/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonLaunchLazyTest.kt b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/LaunchLazyTest.kt
similarity index 98%
rename from common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonLaunchLazyTest.kt
rename to common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/LaunchLazyTest.kt
index 42f4fb8..bb2a845 100644
--- a/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonLaunchLazyTest.kt
+++ b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/LaunchLazyTest.kt
@@ -19,7 +19,7 @@
import kotlin.coroutines.experimental.*
import kotlin.test.*
-class CommonLaunchLazyTest : TestBase() {
+class LaunchLazyTest : TestBase() {
@Test
fun testLaunchAndYieldJoin() = runTest {
expect(1)
diff --git a/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonTestBase.kt b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/TestBase.common.kt
similarity index 100%
rename from common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonTestBase.kt
rename to common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/TestBase.common.kt
diff --git a/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonWithContextTest.kt b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/WithContextTest.kt
similarity index 99%
rename from common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonWithContextTest.kt
rename to common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/WithContextTest.kt
index dde48b5..2679ad5 100644
--- a/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonWithContextTest.kt
+++ b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/WithContextTest.kt
@@ -22,7 +22,7 @@
import kotlin.coroutines.experimental.*
import kotlin.test.*
-class CommonWithContextTest : TestBase() {
+class WithContextTest : TestBase() {
@Test
fun testSameContextNoSuspend() = runTest {
expect(1)
diff --git a/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonWithTimeoutOrNullTest.kt b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/WithTimeoutOrNullTest.kt
similarity index 75%
rename from common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonWithTimeoutOrNullTest.kt
rename to common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/WithTimeoutOrNullTest.kt
index 891c9ef..69b9bd4 100644
--- a/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonWithTimeoutOrNullTest.kt
+++ b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/WithTimeoutOrNullTest.kt
@@ -22,7 +22,7 @@
import kotlin.coroutines.experimental.*
import kotlin.test.*
-class CommonWithTimeoutOrNullTest : TestBase() {
+class WithTimeoutOrNullTest : TestBase() {
/**
* Tests a case of no timeout and no suspension inside.
*/
@@ -142,4 +142,54 @@
override fun hashCode(): Int = error("Should not be called")
override fun toString(): String = error("Should not be called")
}
+
+ @Test
+ fun testNullOnTimeout() = runTest {
+ expect(1)
+ val result = withTimeoutOrNull(100) {
+ expect(2)
+ delay(1000)
+ expectUnreached()
+ "OK"
+ }
+ assertEquals(null, result)
+ finish(3)
+ }
+
+ @Test
+ fun testSuppressExceptionWithResult() = runTest {
+ expect(1)
+ val result = withTimeoutOrNull(100) {
+ expect(2)
+ try {
+ delay(1000)
+ } catch (e: CancellationException) {
+ expect(3)
+ }
+ "OK"
+ }
+ assertEquals(null, result)
+ finish(4)
+ }
+
+ @Test
+ fun testSuppressExceptionWithAnotherException() = runTest(
+ expected = { it is TestException }
+ ) {
+ expect(1)
+ val result = withTimeoutOrNull(100) {
+ expect(2)
+ try {
+ delay(1000)
+ } catch (e: CancellationException) {
+ finish(3)
+ throw TestException()
+ }
+ expectUnreached()
+ "OK"
+ }
+ expectUnreached()
+ }
+
+ private class TestException : Exception()
}
\ No newline at end of file
diff --git a/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonWithTimeoutTest.kt b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/WithTimeoutTest.kt
similarity index 70%
rename from common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonWithTimeoutTest.kt
rename to common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/WithTimeoutTest.kt
index 9589438..1aa2368 100644
--- a/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonWithTimeoutTest.kt
+++ b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/WithTimeoutTest.kt
@@ -22,7 +22,7 @@
import kotlin.coroutines.experimental.*
import kotlin.test.*
-class CommonWithTimeoutTest : TestBase() {
+class WithTimeoutTest : TestBase() {
/**
* Tests a case of no timeout and no suspension inside.
*/
@@ -125,5 +125,59 @@
override fun hashCode(): Int = error("Should not be called")
override fun toString(): String = error("Should not be called")
}
+
+ @Test
+ fun testExceptionOnTimeout() = runTest {
+ expect(1)
+ try {
+ withTimeout(100) {
+ expect(2)
+ delay(1000)
+ expectUnreached()
+ "OK"
+ }
+ } catch (e: CancellationException) {
+ assertEquals("Timed out waiting for 100 MILLISECONDS", e.message)
+ finish(3)
+ }
+ }
+
+ @Test
+ fun testSuppressExceptionWithResult() = runTest(
+ expected = { it is CancellationException }
+ ) {
+ expect(1)
+ val result = withTimeout(100) {
+ expect(2)
+ try {
+ delay(1000)
+ } catch (e: CancellationException) {
+ finish(3)
+ }
+ "OK"
+ }
+ expectUnreached()
+ }
+
+ @Test
+ fun testSuppressExceptionWithAnotherException() = runTest(
+ expected = { it is TestException }
+ ) {
+ expect(1)
+ withTimeout(100) {
+ expect(2)
+ try {
+ delay(1000)
+ } catch (e: CancellationException) {
+ finish(3)
+ throw TestException()
+ }
+ expectUnreached()
+ "OK"
+ }
+ expectUnreached()
+ }
+
+ private class TestException : Exception()
}
diff --git a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectBuilderImplTest.kt b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectBuilderImplTest.kt
similarity index 90%
rename from core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectBuilderImplTest.kt
rename to common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectBuilderImplTest.kt
index 39e289d..299156a 100644
--- a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectBuilderImplTest.kt
+++ b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectBuilderImplTest.kt
@@ -16,11 +16,9 @@
package kotlinx.coroutines.experimental.selects
-import org.junit.Test
-import kotlin.coroutines.experimental.Continuation
-import kotlin.coroutines.experimental.CoroutineContext
-import kotlin.coroutines.experimental.EmptyCoroutineContext
-import kotlin.coroutines.experimental.intrinsics.COROUTINE_SUSPENDED
+import kotlin.coroutines.experimental.*
+import kotlin.coroutines.experimental.intrinsics.*
+import kotlin.test.*
class SelectBuilderImplTest {
@Test
@@ -34,7 +32,7 @@
}
override fun resumeWithException(exception: Throwable) { error("Should not happen") }
}
- val c = SelectBuilderImpl<String>(delegate)
+ val c = SelectBuilderImpl(delegate)
// still running builder
check(!c.isSelected)
check(c.trySelect("SELECT"))
@@ -60,7 +58,7 @@
}
override fun resumeWithException(exception: Throwable) { error("Should not happen") }
}
- val c = SelectBuilderImpl<String>(delegate)
+ val c = SelectBuilderImpl(delegate)
check(c.getResult() === COROUTINE_SUSPENDED) // suspend first
check(!c.isSelected)
check(c.trySelect("SELECT"))
@@ -86,7 +84,7 @@
resumed = true
}
}
- val c = SelectBuilderImpl<String>(delegate)
+ val c = SelectBuilderImpl(delegate)
// still running builder
check(!c.isSelected)
check(c.trySelect("SELECT"))
@@ -117,7 +115,7 @@
resumed = true
}
}
- val c = SelectBuilderImpl<String>(delegate)
+ val c = SelectBuilderImpl(delegate)
check(c.getResult() === COROUTINE_SUSPENDED) // suspend first
check(!c.isSelected)
check(c.trySelect("SELECT"))
diff --git a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectDeferredTest.kt b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectDeferredTest.kt
similarity index 87%
rename from core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectDeferredTest.kt
rename to common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectDeferredTest.kt
index 3ab8955..dcaa857 100644
--- a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectDeferredTest.kt
+++ b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectDeferredTest.kt
@@ -14,18 +14,19 @@
* limitations under the License.
*/
+@file:Suppress("NAMED_ARGUMENTS_NOT_ALLOWED") // KT-21913
+
package kotlinx.coroutines.experimental.selects
import kotlinx.coroutines.experimental.*
-import org.junit.*
-import org.junit.Assert.*
import kotlin.coroutines.experimental.*
+import kotlin.test.*
class SelectDeferredTest : TestBase() {
@Test
- fun testSimpleReturnsImmediately() = runBlocking<Unit> {
+ fun testSimpleReturnsImmediately() = runTest {
expect(1)
- val d1 = async<Int>(coroutineContext) {
+ val d1 = async(coroutineContext) {
expect(3)
42
}
@@ -43,9 +44,9 @@
}
@Test
- fun testSimpleWithYield() = runBlocking<Unit> {
+ fun testSimpleWithYield() = runTest {
expect(1)
- val d1 = async<Int>(coroutineContext) {
+ val d1 = async(coroutineContext) {
expect(3)
42
}
@@ -69,7 +70,7 @@
}
@Test
- fun testSelectIncompleteLazy() = runBlocking<Unit> {
+ fun testSelectIncompleteLazy() = runTest {
expect(1)
val d1 = async(coroutineContext, CoroutineStart.LAZY) {
expect(5)
@@ -97,9 +98,9 @@
}
@Test
- fun testSelectTwo() = runBlocking<Unit> {
+ fun testSelectTwo() = runTest {
expect(1)
- val d1 = async<String>(coroutineContext) {
+ val d1 = async(coroutineContext) {
expect(3)
yield() // to the other deffered
expect(5)
@@ -107,7 +108,7 @@
expect(7)
"d1"
}
- val d2 = async<String>(coroutineContext) {
+ val d2 = async(coroutineContext) {
expect(4)
"d2" // returns result
}
diff --git a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectJobTest.kt b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectJobTest.kt
similarity index 97%
rename from core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectJobTest.kt
rename to common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectJobTest.kt
index 16cffda..3ca3e07 100644
--- a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectJobTest.kt
+++ b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectJobTest.kt
@@ -17,9 +17,8 @@
package kotlinx.coroutines.experimental.selects
import kotlinx.coroutines.experimental.*
-import org.junit.*
-import org.junit.Assert.*
import kotlin.coroutines.experimental.*
+import kotlin.test.*
class SelectJobTest : TestBase() {
@Test
diff --git a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectMutexTest.kt b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectMutexTest.kt
similarity index 68%
rename from core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectMutexTest.kt
rename to common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectMutexTest.kt
index 96c94a4..f2204dc 100644
--- a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectMutexTest.kt
+++ b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectMutexTest.kt
@@ -18,9 +18,8 @@
import kotlinx.coroutines.experimental.*
import kotlinx.coroutines.experimental.sync.*
-import org.junit.*
-import org.junit.Assert.*
import kotlin.coroutines.experimental.*
+import kotlin.test.*
class SelectMutexTest : TestBase() {
@Test
@@ -48,7 +47,8 @@
expect(1)
launch(coroutineContext) {
expect(3)
- val res = select<String> { // will suspended
+ val res = select<String> {
+ // will suspended
mutex.onLock {
assertTrue(mutex.isLocked)
expect(6)
@@ -66,27 +66,4 @@
yield() // to resumed select
finish(8)
}
-
- @Test
- fun testSelectCancelledResourceRelease() = runTest {
- val n = 1_000 * stressTestMultiplier
- val mutex = Mutex(true) as MutexImpl // locked
- expect(1)
- repeat(n) { i ->
- val job = launch(coroutineContext) {
- expect(i + 2)
- select<Unit> {
- mutex.onLock {
- expectUnreached() // never able to lock
- }
- }
- }
- yield() // to the launched job, so that it suspends
- job.cancel() // cancel the job and select
- yield() // so it can cleanup after itself
- }
- assertTrue(mutex.isLocked)
- assertTrue(mutex.isLockedEmptyQueueState)
- finish(n + 2)
- }
}
\ No newline at end of file
diff --git a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectTimeoutTest.kt b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectTimeoutTest.kt
similarity index 79%
rename from core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectTimeoutTest.kt
rename to common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectTimeoutTest.kt
index b8842c1..26bb748 100644
--- a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectTimeoutTest.kt
+++ b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectTimeoutTest.kt
@@ -16,15 +16,12 @@
package kotlinx.coroutines.experimental.selects
-import kotlinx.coroutines.experimental.TestBase
-import kotlinx.coroutines.experimental.runBlocking
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.core.IsEqual
-import org.junit.Test
+import kotlinx.coroutines.experimental.*
+import kotlin.test.*
class SelectTimeoutTest : TestBase() {
@Test
- fun testBasic() = runBlocking {
+ fun testBasic() = runTest {
expect(1)
val result = select<String> {
onTimeout(1000) {
@@ -40,7 +37,7 @@
"FAIL"
}
}
- assertThat(result, IsEqual("OK"))
+ assertEquals("OK", result)
finish(3)
}
}
\ No newline at end of file
diff --git a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/sync/MutexTest.kt b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/sync/MutexTest.kt
similarity index 79%
rename from core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/sync/MutexTest.kt
rename to common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/sync/MutexTest.kt
index 1d3fd30..050a103 100644
--- a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/sync/MutexTest.kt
+++ b/common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/sync/MutexTest.kt
@@ -17,14 +17,12 @@
package kotlinx.coroutines.experimental.sync
import kotlinx.coroutines.experimental.*
-import org.hamcrest.core.*
-import org.junit.*
-import org.junit.Assert.*
+import kotlin.test.*
import kotlin.coroutines.experimental.*
class MutexTest : TestBase() {
@Test
- fun testSimple() = runBlocking<Unit> {
+ fun testSimple() = runTest {
val mutex = Mutex()
expect(1)
launch(coroutineContext) {
@@ -64,7 +62,7 @@
}
@Test
- fun withLockTest() = runBlocking {
+ fun withLockTest() = runTest {
val mutex = Mutex()
assertFalse(mutex.isLocked)
mutex.withLock {
@@ -74,26 +72,6 @@
}
@Test
- fun testStress() = runBlocking<Unit> {
- val n = 1000 * stressTestMultiplier
- val k = 100
- var shared = 0
- val mutex = Mutex()
- val jobs = List(n) {
- launch(CommonPool) {
- repeat(k) {
- mutex.lock()
- shared++
- mutex.unlock()
- }
- }
- }
- jobs.forEach { it.join() }
- println("Shared value = $shared")
- assertEquals(n * k, shared)
- }
-
- @Test
fun testUnconfinedStackOverflow() {
val waiters = 10000
val mutex = Mutex(true)
@@ -106,11 +84,11 @@
}
}
mutex.unlock() // should not produce StackOverflowError
- assertThat(done, IsEqual(waiters))
+ assertEquals(waiters, done)
}
@Test
- fun holdLock() = runBlocking {
+ fun holdLock() = runTest {
val mutex = Mutex()
val firstOwner = Any()
val secondOwner = Any()
@@ -121,7 +99,7 @@
// owner firstOwner
mutex.lock(firstOwner)
- val secondLockJob = launch(CommonPool) {
+ val secondLockJob = launch {
mutex.lock(secondOwner)
}
diff --git a/core/kotlinx-coroutines-core/README.md b/core/kotlinx-coroutines-core/README.md
index ab1759e..1e1c722 100644
--- a/core/kotlinx-coroutines-core/README.md
+++ b/core/kotlinx-coroutines-core/README.md
@@ -87,6 +87,10 @@
Low-level primitives for finer-grained control of coroutines.
+# Package kotlinx.coroutines.experimental.timeunit
+
+Optional time unit support for multiplatform projects.
+
<!--- MODULE kotlinx-coroutines-core -->
<!--- INDEX kotlinx.coroutines.experimental -->
[launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/launch.html
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Annotations.kt b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Annotations.kt
new file mode 100644
index 0000000..21e4c78
--- /dev/null
+++ b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Annotations.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2016-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package kotlinx.coroutines.experimental.internalAnnotations
+
+@Suppress("ACTUAL_WITHOUT_EXPECT")
+internal actual typealias JvmName = kotlin.jvm.JvmName
+
+@Suppress("ACTUAL_WITHOUT_EXPECT")
+internal actual typealias JvmMultifileClass = kotlin.jvm.JvmMultifileClass
+
+@Suppress("ACTUAL_WITHOUT_EXPECT")
+internal actual typealias JvmField = kotlin.jvm.JvmField
+
+@Suppress("ACTUAL_WITHOUT_EXPECT")
+internal actual typealias Volatile = kotlin.jvm.Volatile
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Builders.kt b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Builders.kt
index d54986c..7d20709 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Builders.kt
+++ b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Builders.kt
@@ -14,153 +14,13 @@
* limitations under the License.
*/
-// :todo: Remove after transition to Kotlin 1.2.30+
-@file:Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
+@file:JvmMultifileClass
+@file:JvmName("BuildersKt")
package kotlinx.coroutines.experimental
-import kotlinx.coroutines.experimental.intrinsics.*
import java.util.concurrent.locks.*
import kotlin.coroutines.experimental.*
-import kotlin.coroutines.experimental.intrinsics.*
-
-// --------------- basic coroutine builders ---------------
-
-/**
- * Launches new coroutine without blocking current thread and returns a reference to the coroutine as a [Job].
- * The coroutine is cancelled when the resulting job is [cancelled][Job.cancel].
- *
- * The [context] for the new coroutine can be explicitly specified.
- * See [CoroutineDispatcher] for the standard context implementations that are provided by `kotlinx.coroutines`.
- * The [coroutineContext](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines.experimental/coroutine-context.html)
- * of the parent coroutine may be used,
- * in which case the [Job] of the resulting coroutine is a child of the job of the parent coroutine.
- * The parent job may be also explicitly specified using [parent] parameter.
- *
- * If the context does not have any dispatcher nor any other [ContinuationInterceptor], then [DefaultDispatcher] is used.
- *
- * By default, the coroutine is immediately scheduled for execution.
- * Other options can be specified via `start` parameter. See [CoroutineStart] for details.
- * An optional [start] parameter can be set to [CoroutineStart.LAZY] to start coroutine _lazily_. In this case,
- * the coroutine [Job] is created in _new_ state. It can be explicitly started with [start][Job.start] function
- * and will be started implicitly on the first invocation of [join][Job.join].
- *
- * Uncaught exceptions in this coroutine cancel parent job in the context by default
- * (unless [CoroutineExceptionHandler] is explicitly specified), which means that when `launch` is used with
- * the context of another coroutine, then any uncaught exception leads to the cancellation of parent coroutine.
- *
- * See [newCoroutineContext] for a description of debugging facilities that are available for newly created coroutine.
- *
- * @param context context of the coroutine. The default value is [DefaultDispatcher].
- * @param start coroutine start option. The default value is [CoroutineStart.DEFAULT].
- * @param parent explicitly specifies the parent job, overrides job from the [context] (if any).
- * @param onCompletion optional completion handler for the coroutine (see [Job.invokeOnCompletion]).
- * @param block the coroutine code.
- */
-public actual fun launch(
- context: CoroutineContext = DefaultDispatcher,
- start: CoroutineStart = CoroutineStart.DEFAULT,
- parent: Job? = null,
- onCompletion: CompletionHandler? = null,
- block: suspend CoroutineScope.() -> Unit
-): Job {
- val newContext = newCoroutineContext(context, parent)
- val coroutine = if (start.isLazy)
- LazyStandaloneCoroutine(newContext, block) else
- StandaloneCoroutine(newContext, active = true)
- if (onCompletion != null) coroutine.invokeOnCompletion(handler = onCompletion)
- coroutine.start(start, coroutine, block)
- return coroutine
-}
-
-/** @suppress **Deprecated**: Binary compatibility */
-@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN)
-public fun launch(
- context: CoroutineContext = DefaultDispatcher,
- start: CoroutineStart = CoroutineStart.DEFAULT,
- parent: Job? = null,
- block: suspend CoroutineScope.() -> Unit
-): Job = launch(context, start, parent, block = block)
-
-/** @suppress **Deprecated**: Binary compatibility */
-@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN)
-public fun launch(
- context: CoroutineContext = DefaultDispatcher,
- start: CoroutineStart = CoroutineStart.DEFAULT,
- block: suspend CoroutineScope.() -> Unit
-): Job =
- launch(context, start, block = block)
-
-/**
- * @suppress **Deprecated**: Use `start = CoroutineStart.XXX` parameter
- */
-@Deprecated(message = "Use `start = CoroutineStart.XXX` parameter",
- replaceWith = ReplaceWith("launch(context, if (start) CoroutineStart.DEFAULT else CoroutineStart.LAZY, block)"))
-public fun launch(context: CoroutineContext, start: Boolean, block: suspend CoroutineScope.() -> Unit): Job =
- launch(context, if (start) CoroutineStart.DEFAULT else CoroutineStart.LAZY, block = block)
-
-/**
- * Calls the specified suspending block with a given coroutine context, suspends until it completes, and returns
- * the result.
- *
- * This function immediately applies dispatcher from the new context, shifting execution of the block into the
- * different thread inside the block, and back when it completes.
- * The specified [context] is added onto the current coroutine context for the execution of the block.
- *
- * An optional `start` parameter is used only if the specified `context` uses a different [CoroutineDispatcher] than
- * a current one, otherwise it is ignored.
- * By default, the coroutine is immediately scheduled for execution and can be cancelled
- * while it is waiting to be executed and it can be cancelled while the result is scheduled
- * to be processed by the invoker context.
- * Other options can be specified via `start` parameter. See [CoroutineStart] for details.
- * A value of [CoroutineStart.LAZY] is not supported and produces [IllegalArgumentException].
- */
-public actual suspend fun <T> withContext(
- context: CoroutineContext,
- start: CoroutineStart = CoroutineStart.DEFAULT,
- block: suspend () -> T
-): T = suspendCoroutineOrReturn sc@ { cont ->
- val oldContext = cont.context
- // fast path #1 if there is no change in the actual context:
- if (context === oldContext || context is CoroutineContext.Element && oldContext[context.key] === context)
- return@sc block.startCoroutineUninterceptedOrReturn(cont)
- // compute new context
- val newContext = oldContext + context
- // fast path #2 if the result is actually the same
- if (newContext === oldContext)
- return@sc block.startCoroutineUninterceptedOrReturn(cont)
- // fast path #3 if the new dispatcher is the same as the old one.
- // `equals` is used by design (see equals implementation is wrapper context like ExecutorCoroutineDispatcher)
- if (newContext[ContinuationInterceptor] == oldContext[ContinuationInterceptor]) {
- val newContinuation = RunContinuationDirect(newContext, cont)
- return@sc block.startCoroutineUninterceptedOrReturn(newContinuation)
- }
- // slowest path otherwise -- use new interceptor, sync to its result via a full-blown instance of RunCompletion
- require(!start.isLazy) { "$start start is not supported" }
- val completion = RunCompletion(
- context = newContext,
- delegate = cont,
- resumeMode = if (start == CoroutineStart.ATOMIC) MODE_ATOMIC_DEFAULT else MODE_CANCELLABLE)
- completion.initParentJobInternal(newContext[Job]) // attach to job
- @Suppress("DEPRECATION")
- start(block, completion)
- completion.getResult()
-}
-
-/** @suppress **Deprecated**: Renamed to [withContext]. */
-@Deprecated(message = "Renamed to `withContext`", level=DeprecationLevel.WARNING,
- replaceWith = ReplaceWith("withContext(context, start, block)"))
-public suspend fun <T> run(
- context: CoroutineContext,
- start: CoroutineStart = CoroutineStart.DEFAULT,
- block: suspend () -> T
-): T =
- withContext(context, start, block)
-
-/** @suppress **Deprecated** */
-@Deprecated(message = "It is here for binary compatibility only", level=DeprecationLevel.HIDDEN)
-public suspend fun <T> run(context: CoroutineContext, block: suspend () -> T): T =
- withContext(context, start = CoroutineStart.ATOMIC, block = block)
/**
* Runs new coroutine and **blocks** current thread _interruptibly_ until its completion.
@@ -198,41 +58,6 @@
return coroutine.joinBlocking()
}
-// --------------- implementation ---------------
-
-private open class StandaloneCoroutine(
- private val parentContext: CoroutineContext,
- active: Boolean
-) : AbstractCoroutine<Unit>(parentContext, active) {
- override fun hasOnFinishingHandler(update: Any?) = update is CompletedExceptionally
- override fun onFinishingInternal(update: Any?) {
- // note the use of the parent's job context below!
- if (update is CompletedExceptionally) handleCoroutineException(parentContext, update.exception)
- }
-}
-
-private class LazyStandaloneCoroutine(
- parentContext: CoroutineContext,
- private val block: suspend CoroutineScope.() -> Unit
-) : StandaloneCoroutine(parentContext, active = false) {
- override fun onStart() {
- block.startCoroutineCancellable(this, this)
- }
-}
-
-private class RunContinuationDirect<in T>(
- override val context: CoroutineContext,
- continuation: Continuation<T>
-) : Continuation<T> by continuation
-
-
-@Suppress("UNCHECKED_CAST")
-private class RunCompletion<in T>(
- override val context: CoroutineContext,
- delegate: Continuation<T>,
- resumeMode: Int
-) : AbstractContinuation<T>(delegate, resumeMode)
-
private class BlockingCoroutine<T>(
parentContext: CoroutineContext,
private val blockedThread: Thread,
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CommonPool.kt b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CommonPool.kt
index a73ad00..10081ef 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CommonPool.kt
+++ b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CommonPool.kt
@@ -16,6 +16,7 @@
package kotlinx.coroutines.experimental
+import kotlinx.coroutines.experimental.timeunit.TimeUnit
import java.util.concurrent.*
import java.util.concurrent.atomic.AtomicInteger
import kotlin.coroutines.experimental.CoroutineContext
diff --git a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonScheduled.kt b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CompletionHandler.kt
similarity index 60%
copy from common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonScheduled.kt
copy to core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CompletionHandler.kt
index d9181d0..0f5cd77 100644
--- a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonScheduled.kt
+++ b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CompletionHandler.kt
@@ -16,6 +16,12 @@
package kotlinx.coroutines.experimental
-public expect suspend fun <T> withTimeout(time: Int, block: suspend CoroutineScope.() -> T): T
+import kotlinx.coroutines.experimental.internal.*
-public expect suspend fun <T> withTimeoutOrNull(time: Int, block: suspend CoroutineScope.() -> T): T?
+internal actual abstract class CompletionHandlerNode actual constructor() : LockFreeLinkedListNode(), CompletionHandler {
+ actual inline val asHandler: CompletionHandler get() = this
+ actual abstract override fun invoke(cause: Throwable?)
+}
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun CompletionHandler.invokeIt(cause: Throwable?) = invoke(cause)
\ No newline at end of file
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineContext.kt b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineContext.kt
index 832e1f0..774ebe7 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineContext.kt
+++ b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineContext.kt
@@ -42,30 +42,6 @@
}
/**
- * A coroutine dispatcher that is not confined to any specific thread.
- * It executes initial continuation of the coroutine _right here_ in the current call-frame
- * and let the coroutine resume in whatever thread that is used by the corresponding suspending function, without
- * mandating any specific threading policy.
- *
- * Note, that if you need your coroutine to be confined to a particular thread or a thread-pool after resumption,
- * but still want to execute it in the current call-frame until its first suspension, then you can use
- * an optional [CoroutineStart] parameter in coroutine builders like [launch] and [async] setting it to the
- * the value of [CoroutineStart.UNDISPATCHED].
- */
-public actual object Unconfined : CoroutineDispatcher() {
- actual override fun isDispatchNeeded(context: CoroutineContext): Boolean = false
- actual override fun dispatch(context: CoroutineContext, block: Runnable) { throw UnsupportedOperationException() }
- override fun toString(): String = "Unconfined"
-}
-
-/**
- * @suppress **Deprecated**: `Here` was renamed to `Unconfined`.
- */
-@Deprecated(message = "`Here` was renamed to `Unconfined`",
- replaceWith = ReplaceWith(expression = "Unconfined"))
-public typealias Here = Unconfined
-
-/**
* This is the default [CoroutineDispatcher] that is used by all standard builders like
* [launch], [async], etc if no dispatcher nor any other [ContinuationInterceptor] is specified in their context.
*
@@ -94,7 +70,7 @@
* The string "coroutine" is used as a default name.
*/
@JvmOverloads // for binary compatibility with newCoroutineContext(context: CoroutineContext) version
-public fun newCoroutineContext(context: CoroutineContext, parent: Job? = null): CoroutineContext {
+public actual fun newCoroutineContext(context: CoroutineContext, parent: Job? = null): CoroutineContext {
val debug = if (DEBUG) context + CoroutineId(COROUTINE_ID.incrementAndGet()) else context
val wp = if (parent == null) debug else debug + parent
return if (context !== DefaultDispatcher && context[ContinuationInterceptor] == null)
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineExceptionHandlerImpl.kt b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineExceptionHandlerImpl.kt
new file mode 100644
index 0000000..04246d3
--- /dev/null
+++ b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineExceptionHandlerImpl.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2016-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package kotlinx.coroutines.experimental
+
+import java.util.*
+import kotlin.coroutines.experimental.AbstractCoroutineContextElement
+import kotlin.coroutines.experimental.CoroutineContext
+
+internal actual fun handleCoroutineExceptionImpl(context: CoroutineContext, exception: Throwable) {
+ // use additional extension handlers
+ ServiceLoader.load(CoroutineExceptionHandler::class.java).forEach { handler ->
+ handler.handleException(context, exception)
+ }
+ // use thread's handler
+ val currentThread = Thread.currentThread()
+ currentThread.uncaughtExceptionHandler.uncaughtException(currentThread, exception)
+}
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Debug.kt b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Debug.kt
index 6930f33..a86d98f 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Debug.kt
+++ b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Debug.kt
@@ -20,7 +20,7 @@
// internal debugging tools
-internal val Any.hexAddress: String
+internal actual val Any.hexAddress: String
get() = Integer.toHexString(System.identityHashCode(this))
internal fun Any?.toSafeString(): String =
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/DefaultExecutor.kt b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/DefaultExecutor.kt
index d2aab07..7d7d67d 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/DefaultExecutor.kt
+++ b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/DefaultExecutor.kt
@@ -16,7 +16,9 @@
package kotlinx.coroutines.experimental
-import java.util.concurrent.*
+import kotlinx.coroutines.experimental.timeunit.*
+
+internal actual val DefaultDelay: Delay = DefaultExecutor
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
internal object DefaultExecutor : EventLoopBase(), Runnable {
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/EventLoop.kt b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/EventLoop.kt
index e752994..6d6685b 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/EventLoop.kt
+++ b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/EventLoop.kt
@@ -18,7 +18,7 @@
import kotlinx.atomicfu.*
import kotlinx.coroutines.experimental.internal.*
-import java.util.concurrent.*
+import kotlinx.coroutines.experimental.timeunit.TimeUnit
import java.util.concurrent.locks.*
import kotlin.coroutines.experimental.*
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Exceptions.kt b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Exceptions.kt
index ed43104..7d7c532 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Exceptions.kt
+++ b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Exceptions.kt
@@ -26,9 +26,12 @@
cause: Throwable
) : RuntimeException(message, cause)
- /**
- * Thrown by cancellable suspending functions if the [Job] of the coroutine is cancelled while it is suspending.
- */
+/**
+ * Thrown by cancellable suspending functions if the [Job] of the coroutine is cancelled while it is suspending.
+ * It indicates _normal_ cancellation of a coroutine.
+ * **It is not printed to console/log by default uncaught exception handler**.
+ * (see [handleCoroutineException]).
+*/
public actual typealias CancellationException = java.util.concurrent.CancellationException
/**
@@ -53,31 +56,8 @@
(message!!.hashCode() * 31 + job.hashCode()) * 31 + (cause?.hashCode() ?: 0)
}
-/**
- * This exception is thrown by [withTimeout] to indicate timeout.
- */
-@Suppress("DEPRECATION")
-public actual class TimeoutCancellationException internal constructor(
- message: String,
- @JvmField internal val coroutine: Job?
-) : TimeoutException(message) {
- /**
- * Creates timeout exception with a given message.
- */
- public actual constructor(message: String) : this(message, null)
-}
-
-@Suppress("FunctionName")
-internal fun TimeoutCancellationException(
- time: Long,
- unit: TimeUnit,
- coroutine: Job
-) : TimeoutCancellationException = TimeoutCancellationException("Timed out waiting for $time $unit", coroutine)
-
-/**
- * @suppress **Deprecated**: Renamed to TimeoutCancellationException
- */
-@Deprecated("Renamed to TimeoutCancellationException", replaceWith = ReplaceWith("TimeoutCancellationException"))
-public open class TimeoutException(message: String) : CancellationException(message)
-
internal actual class DispatchException actual constructor(message: String, cause: Throwable) : RuntimeException(message, cause)
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun Throwable.addSuppressedThrowable(other: Throwable) =
+ addSuppressed(other)
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Executors.kt b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Executors.kt
index 545300f..f20753a 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Executors.kt
+++ b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Executors.kt
@@ -16,13 +16,10 @@
package kotlinx.coroutines.experimental
-import java.io.Closeable
-import java.util.concurrent.Executor
-import java.util.concurrent.ExecutorService
-import java.util.concurrent.RejectedExecutionException
-import java.util.concurrent.ScheduledExecutorService
-import java.util.concurrent.TimeUnit
-import kotlin.coroutines.experimental.CoroutineContext
+import kotlinx.coroutines.experimental.timeunit.TimeUnit
+import java.io.*
+import java.util.concurrent.*
+import kotlin.coroutines.experimental.*
/**
* [CoroutineDispatcher] that implements [Closeable]
@@ -108,3 +105,14 @@
with(continuation) { dispatcher.resumeUndispatched(Unit) }
}
}
+
+/**
+ * An implementation of [DisposableHandle] that cancels the specified future on dispose.
+ * @suppress **This is unstable API and it is subject to change.**
+ */
+public class DisposableFutureHandle(private val future: Future<*>) : DisposableHandle {
+ override fun dispose() {
+ future.cancel(false)
+ }
+ override fun toString(): String = "DisposableFutureHandle[$future]"
+}
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Future.kt b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Future.kt
new file mode 100644
index 0000000..b10f0cd
--- /dev/null
+++ b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Future.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:JvmMultifileClass
+@file:JvmName("JobKt")
+
+package kotlinx.coroutines.experimental
+
+import java.util.concurrent.*
+
+/**
+ * Cancels a specified [future] when this job is complete.
+ *
+ * This is a shortcut for the following code with slightly more efficient implementation (one fewer object created).
+ * ```
+ * invokeOnCompletion { future.cancel(false) }
+ * ```
+ */
+public fun Job.cancelFutureOnCompletion(future: Future<*>): DisposableHandle =
+ invokeOnCompletion(handler = CancelFutureOnCompletion(this, future))
+
+private class CancelFutureOnCompletion(
+ job: Job,
+ private val future: Future<*>
+) : JobNode<Job>(job) {
+ override fun invoke(reason: Throwable?) {
+ // Don't interrupt when cancelling future on completion, because no one is going to reset this
+ // interruption flag and it will cause spurious failures elsewhere
+ future.cancel(false)
+ }
+ override fun toString() = "CancelFutureOnCompletion[$future]"
+}
+
diff --git a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonScheduled.kt b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Runnable.kt
similarity index 67%
rename from common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonScheduled.kt
rename to core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Runnable.kt
index d9181d0..83e239d 100644
--- a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonScheduled.kt
+++ b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Runnable.kt
@@ -16,6 +16,14 @@
package kotlinx.coroutines.experimental
-public expect suspend fun <T> withTimeout(time: Int, block: suspend CoroutineScope.() -> T): T
+/**
+ * A runnable task for [CoroutineDispatcher.dispatch].
+ */
+public actual typealias Runnable = java.lang.Runnable
-public expect suspend fun <T> withTimeoutOrNull(time: Int, block: suspend CoroutineScope.() -> T): T?
+/**
+ * Creates [Runnable] task instance.
+ */
+@Suppress("FunctionName")
+public actual inline fun Runnable(crossinline block: () -> Unit): Runnable =
+ java.lang.Runnable { block() }
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/AbstractChannel.kt b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/AbstractChannel.kt
index a42517a..4ce28da 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/AbstractChannel.kt
+++ b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/AbstractChannel.kt
@@ -138,7 +138,7 @@
*/
protected fun describeSendBuffered(element: E): AddLastDesc<*> = SendBufferedDesc(queue, element)
- private open class SendBufferedDesc<out E>(
+ private open class SendBufferedDesc<E>(
queue: LockFreeLinkedListHead,
element: E
) : AddLastDesc<SendBuffered<E>>(queue, SendBuffered(element)) {
@@ -153,7 +153,7 @@
*/
protected fun describeSendConflated(element: E): AddLastDesc<*> = SendConflatedDesc(queue, element)
- private class SendConflatedDesc<out E>(
+ private class SendConflatedDesc<E>(
queue: LockFreeLinkedListHead,
element: E
) : SendBufferedDesc<E>(queue, element) {
@@ -647,7 +647,7 @@
}
}
- private inner class TryEnqueueReceiveDesc<in E, R>(
+ private inner class TryEnqueueReceiveDesc<E, R>(
select: SelectInstance<R>,
block: suspend (E?) -> R,
nullOnClose: Boolean
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/Actor.kt b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/Actor.kt
index d10bcf6..54adc81 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/Actor.kt
+++ b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/Actor.kt
@@ -172,7 +172,8 @@
parentContext: CoroutineContext,
channel: Channel<E>,
private val block: suspend ActorScope<E>.() -> Unit
-) : ActorCoroutine<E>(parentContext, channel, active = false), SelectClause2<E, SendChannel<E>> {
+) : ActorCoroutine<E>(parentContext, channel, active = false),
+ SelectClause2<E, SendChannel<E>> {
override fun onStart() {
block.startCoroutineCancellable(this, this)
}
diff --git a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/internal/LockFreeLinkedList.kt b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/internal/LockFreeLinkedList.kt
index 06e9384..17a0ebb 100644
--- a/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/internal/LockFreeLinkedList.kt
+++ b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/internal/LockFreeLinkedList.kt
@@ -41,15 +41,14 @@
private val REMOVE_PREPARED: Any = Symbol("REMOVE_PREPARED")
-/**
- * @suppress **This is unstable API and it is subject to change.**
- */
+/** @suppress **This is unstable API and it is subject to change.** */
public typealias RemoveFirstDesc<T> = LockFreeLinkedListNode.RemoveFirstDesc<T>
-/**
- * @suppress **This is unstable API and it is subject to change.**
- */
-public typealias AddLastDesc<T> = LockFreeLinkedListNode.AddLastDesc<T>
+/** @suppress **This is unstable API and it is subject to change.** */
+public actual typealias AddLastDesc<T> = LockFreeLinkedListNode.AddLastDesc<T>
+
+/** @suppress **This is unstable API and it is subject to change.** */
+public actual typealias AbstractAtomicDesc = LockFreeLinkedListNode.AbstractAtomicDesc
/**
* Doubly-linked concurrent list node with remove support.
@@ -67,7 +66,7 @@
* @suppress **This is unstable API and it is subject to change.**
*/
@Suppress("LeakingThis")
-public open class LockFreeLinkedListNode {
+public actual open class LockFreeLinkedListNode {
private val _next = atomic<Any>(this) // Node | Removed | OpDescriptor
private val _prev = atomic<Any>(this) // Node | Removed
private val _removedRef = atomic<Removed?>(null) // lazily cached removed ref to this
@@ -99,7 +98,7 @@
public val isFresh: Boolean get() = _next.value === this
- public val isRemoved: Boolean get() = next is Removed
+ public actual val isRemoved: Boolean get() = next is Removed
// LINEARIZABLE. Returns Node | Removed
public val next: Any get() {
@@ -109,6 +108,8 @@
}
}
+ public actual val nextNode: Node get() = next.unwrap()
+
// LINEARIZABLE. Returns Node | Removed
public val prev: Any get() {
_prev.loop { prev ->
@@ -119,9 +120,11 @@
}
}
+ public actual val prevNode: Node get() = prev.unwrap()
+
// ------ addOneIfEmpty ------
- public fun addOneIfEmpty(node: Node): Boolean {
+ public actual fun addOneIfEmpty(node: Node): Boolean {
node._prev.lazySet(this)
node._next.lazySet(this)
while (true) {
@@ -140,7 +143,7 @@
/**
* Adds last item to this list.
*/
- public fun addLast(node: Node) {
+ public actual fun addLast(node: Node) {
while (true) { // lock-free loop on prev.next
val prev = prev as Node // sentinel node is never removed, so prev is always defined
if (prev.addNext(node, this)) return
@@ -152,7 +155,7 @@
/**
* Adds last item to this list atomically if the [condition] is true.
*/
- public inline fun addLastIf(node: Node, crossinline condition: () -> Boolean): Boolean {
+ public actual inline fun addLastIf(node: Node, crossinline condition: () -> Boolean): Boolean {
val condAdd = makeCondAddOp(node, condition)
while (true) { // lock-free loop on prev.next
val prev = prev as Node // sentinel node is never removed, so prev is always defined
@@ -163,7 +166,7 @@
}
}
- public inline fun addLastIfPrev(node: Node, predicate: (Node) -> Boolean): Boolean {
+ public actual inline fun addLastIfPrev(node: Node, predicate: (Node) -> Boolean): Boolean {
while (true) { // lock-free loop on prev.next
val prev = prev as Node // sentinel node is never removed, so prev is always defined
if (!predicate(prev)) return false
@@ -171,7 +174,7 @@
}
}
- public inline fun addLastIfPrevAndIf(
+ public actual inline fun addLastIfPrevAndIf(
node: Node,
predicate: (Node) -> Boolean, // prev node predicate
crossinline condition: () -> Boolean // atomically checked condition
@@ -239,7 +242,7 @@
* Removes this node from the list. Returns `true` when removed successfully, or `false` if the node was already
* removed or if it was not added to any list in the first place.
*/
- public open fun remove(): Boolean {
+ public actual open fun remove(): Boolean {
while (true) { // lock-free loop on next
val next = this.next
if (next is Removed) return false // was already removed -- don't try to help (original thread will take care)
@@ -273,7 +276,7 @@
}
}
- public fun removeFirstOrNull(): Node? {
+ public actual fun removeFirstOrNull(): Node? {
while (true) { // try to linearize
val first = next as Node
if (first === this) return null
@@ -295,7 +298,7 @@
}
// just peek at item when predicate is true
- public inline fun <reified T> removeFirstIfIsInstanceOfOrPeekIf(predicate: (T) -> Boolean): T? {
+ public actual inline fun <reified T> removeFirstIfIsInstanceOfOrPeekIf(predicate: (T) -> Boolean): T? {
while (true) { // try to linearize
val first = next as Node
if (first === this) return null
@@ -308,7 +311,7 @@
// ------ multi-word atomic operations helpers ------
- public open class AddLastDesc<out T : Node>(
+ public open class AddLastDesc<T : Node> actual constructor(
@JvmField val queue: Node,
@JvmField val node: T
) : AbstractAtomicDesc() {
@@ -340,7 +343,7 @@
override fun retry(affected: Node, next: Any): Boolean = next !== queue
- override fun onPrepare(affected: Node, next: Node): Any? {
+ protected override fun onPrepare(affected: Node, next: Node): Any? {
// Note: onPrepare must use CAS to make sure the stale invocation is not
// going to overwrite the previous decision on successful preparation.
// Result of CAS is irrelevant, but we must ensure that it is set when invoker completes
@@ -659,20 +662,20 @@
}
@PublishedApi
-internal fun Any.unwrap(): Node = if (this is Removed) ref else this as Node
+internal fun Any.unwrap(): Node = (this as? Removed)?.ref ?: this as Node
/**
* Head (sentinel) item of the linked list that is never removed.
*
* @suppress **This is unstable API and it is subject to change.**
*/
-public open class LockFreeLinkedListHead : LockFreeLinkedListNode() {
- public val isEmpty: Boolean get() = next === this
+public actual open class LockFreeLinkedListHead : LockFreeLinkedListNode() {
+ public actual val isEmpty: Boolean get() = next === this
/**
* Iterates over all elements in this list of a specified type.
*/
- public inline fun <reified T : Node> forEach(block: (T) -> Unit) {
+ public actual inline fun <reified T : Node> forEach(block: (T) -> Unit) {
var cur: Node = next as Node
while (cur != this) {
if (cur is T) block(cur)
@@ -681,8 +684,9 @@
}
// just a defensive programming -- makes sure that list head sentinel is never removed
- public final override fun remove() = throw UnsupportedOperationException()
- public final override fun describeRemove(): AtomicDesc? = throw UnsupportedOperationException()
+ public actual final override fun remove(): Nothing = throw UnsupportedOperationException()
+
+ public final override fun describeRemove(): Nothing = throw UnsupportedOperationException()
internal fun validate() {
var prev: Node = this
diff --git a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonYield.kt b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/timeunit/TimeUnit.kt
similarity index 73%
copy from common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonYield.kt
copy to core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/timeunit/TimeUnit.kt
index e461c47..8c0a66c 100644
--- a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonYield.kt
+++ b/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/timeunit/TimeUnit.kt
@@ -14,6 +14,10 @@
* limitations under the License.
*/
-package kotlinx.coroutines.experimental
+package kotlinx.coroutines.experimental.timeunit
-public expect suspend fun yield()
\ No newline at end of file
+/**
+ * Time unit type alias for writing multiplatform code.
+ */
+@Suppress("ACTUAL_WITHOUT_EXPECT")
+public actual typealias TimeUnit = java.util.concurrent.TimeUnit
\ No newline at end of file
diff --git a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/AsyncTest.kt b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/AsyncJvmTest.kt
similarity index 98%
rename from core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/AsyncTest.kt
rename to core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/AsyncJvmTest.kt
index 7b93917..a2f4d5c 100644
--- a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/AsyncTest.kt
+++ b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/AsyncJvmTest.kt
@@ -19,7 +19,7 @@
import kotlin.coroutines.experimental.*
import kotlin.test.*
-class AsyncTest : TestBase() {
+class AsyncJvmTest : TestBase() {
// This must be a common test but it fails on JS because of KT-21961
@Test
fun testAsyncWithFinally() = runTest {
diff --git a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/AtomicCancellationTest.kt b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/AtomicCancellationTest.kt
index 9d7a20a..1ea089a 100644
--- a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/AtomicCancellationTest.kt
+++ b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/AtomicCancellationTest.kt
@@ -24,44 +24,6 @@
class AtomicCancellationTest : TestBase() {
@Test
- fun testLockAtomicCancel() = runBlocking {
- expect(1)
- val mutex = Mutex(true) // locked mutex
- val job = launch(coroutineContext, start = CoroutineStart.UNDISPATCHED) {
- expect(2)
- mutex.lock() // suspends
- expect(4) // should execute despite cancellation
- }
- expect(3)
- mutex.unlock() // unlock mutex first
- job.cancel() // cancel the job next
- yield() // now yield
- finish(5)
- }
-
- @Test
- fun testSelectLockAtomicCancel() = runBlocking {
- expect(1)
- val mutex = Mutex(true) // locked mutex
- val job = launch(coroutineContext, start = CoroutineStart.UNDISPATCHED) {
- expect(2)
- val result = select<String> { // suspends
- mutex.onLock {
- expect(4)
- "OK"
- }
- }
- assertEquals("OK", result)
- expect(5) // should execute despite cancellation
- }
- expect(3)
- mutex.unlock() // unlock mutex first
- job.cancel() // cancel the job next
- yield() // now yield
- finish(6)
- }
-
- @Test
fun testSendAtomicCancel() = runBlocking {
expect(1)
val channel = Channel<Int>()
diff --git a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/CoroutinesTest.kt b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/CoroutinesJvmTest.kt
similarity index 97%
rename from core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/CoroutinesTest.kt
rename to core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/CoroutinesJvmTest.kt
index 58e7bfe..b200015 100644
--- a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/CoroutinesTest.kt
+++ b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/CoroutinesJvmTest.kt
@@ -19,7 +19,7 @@
import kotlin.coroutines.experimental.*
import kotlin.test.*
-class CoroutinesTest : TestBase() {
+class CoroutinesJvmTest : TestBase() {
@Test
fun testNotCancellableCodeWithExceptionCancelled() = runTest {
expect(1)
diff --git a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/JobTest.kt b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/JobStressTest.kt
similarity index 95%
rename from core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/JobTest.kt
rename to core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/JobStressTest.kt
index 06f7c68..50ef98e 100644
--- a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/JobTest.kt
+++ b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/JobStressTest.kt
@@ -18,7 +18,7 @@
import kotlin.test.*
-class JobTest : TestBase() {
+class JobStressTest : TestBase() {
@Test
fun testMemoryRelease() {
val job = Job()
diff --git a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/TestBase.kt b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/TestBase.kt
index 4edc6e9..f6f087b 100644
--- a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/TestBase.kt
+++ b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/TestBase.kt
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-// :todo: Remove after transition to Kotlin 1.2.30+
-@file:Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
-
package kotlinx.coroutines.experimental
import org.junit.After
diff --git a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/WithContextTest.kt b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/WithContextCommonPoolTest.kt
similarity index 95%
rename from core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/WithContextTest.kt
rename to core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/WithContextCommonPoolTest.kt
index ee26c07..993aeef 100644
--- a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/WithContextTest.kt
+++ b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/WithContextCommonPoolTest.kt
@@ -18,7 +18,7 @@
import kotlin.test.*
-class WithContextTest : TestBase() {
+class WithContextCommonPoolTest : TestBase() {
@Test
fun testCommonPoolNoSuspend() = runTest {
expect(1)
diff --git a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/WithTimeoutOrNullJvmTest.kt b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/WithTimeoutOrNullJvmTest.kt
new file mode 100644
index 0000000..560d087
--- /dev/null
+++ b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/WithTimeoutOrNullJvmTest.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2016-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package kotlinx.coroutines.experimental
+
+import kotlin.test.*
+
+class WithTimeoutOrNullJvmTest : TestBase() {
+ @Test
+ fun testOuterTimeoutFiredBeforeInner() = runTest {
+ val result = withTimeoutOrNull(100) {
+ Thread.sleep(200) // wait enough for outer timeout to fire
+ withContext(NonCancellable) { yield() } // give an event loop a chance to run and process that cancellation
+ withTimeoutOrNull(100) {
+ yield() // will cancel because of outer timeout
+ expectUnreached()
+ }
+ expectUnreached() // should not be reached, because it is outer timeout
+ }
+ // outer timeout results in null
+ assertEquals(null, result)
+ }
+}
\ No newline at end of file
diff --git a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/WithTimeoutOrNullTest.kt b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/WithTimeoutOrNullTest.kt
deleted file mode 100644
index 6c43f76..0000000
--- a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/WithTimeoutOrNullTest.kt
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright 2016-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package kotlinx.coroutines.experimental
-
-import kotlin.test.*
-
-class WithTimeoutOrNullTest : TestBase() {
- @Test
- fun testNullOnTimeout() = runTest {
- expect(1)
- val result = withTimeoutOrNull(100) {
- expect(2)
- delay(1000)
- expectUnreached()
- "OK"
- }
- assertEquals(null, result)
- finish(3)
- }
-
- @Test
- fun testSuppressExceptionWithResult() = runTest {
- expect(1)
- val result = withTimeoutOrNull(100) {
- expect(2)
- try {
- delay(1000)
- } catch (e: CancellationException) {
- expect(3)
- }
- "OK"
- }
- assertEquals(null, result)
- finish(4)
- }
-
- @Test
- fun testSuppressExceptionWithAnotherException() = runTest(
- expected = { it is TestException }
- ) {
- expect(1)
- val result = withTimeoutOrNull(100) {
- expect(2)
- try {
- delay(1000)
- } catch (e: CancellationException) {
- finish(3)
- throw TestException()
- }
- expectUnreached()
- "OK"
- }
- expectUnreached()
- }
-
- @Test
- fun testOuterTimeoutFiredBeforeInner() = runTest {
- val result = withTimeoutOrNull(100) {
- Thread.sleep(200) // wait enough for outer timeout to fire
- withContext(NonCancellable) { yield() } // give an event loop a chance to run and process that cancellation
- withTimeoutOrNull(100) {
- yield() // will cancel because of outer timeout
- expectUnreached()
- }
- expectUnreached() // should not be reached, because it is outer timeout
- }
- // outer timeout results in null
- assertEquals(null, result)
- }
-
- private class TestException : Exception()
-}
\ No newline at end of file
diff --git a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/WithTimeoutOrNullThreadDispatchTest.kt b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/WithTimeoutOrNullThreadDispatchTest.kt
index df1387c..f5b8562 100644
--- a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/WithTimeoutOrNullThreadDispatchTest.kt
+++ b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/WithTimeoutOrNullThreadDispatchTest.kt
@@ -47,7 +47,6 @@
}
}
-
@Test
fun testCancellationDispatchCustomNoDelay() {
// it also checks that there is at most once scheduled request in flight (no spurious concurrency)
diff --git a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/WithTimeoutTest.kt b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/WithTimeoutTest.kt
deleted file mode 100644
index a793a1e..0000000
--- a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/WithTimeoutTest.kt
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 2016-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-@file:Suppress("NAMED_ARGUMENTS_NOT_ALLOWED") // KT-21913
-
-package kotlinx.coroutines.experimental
-
-import kotlin.test.*
-import java.io.IOException
-
-class WithTimeoutTest : TestBase() {
- @Test
- fun testExceptionOnTimeout() = runTest {
- expect(1)
- try {
- withTimeout(100) {
- expect(2)
- delay(1000)
- expectUnreached()
- "OK"
- }
- } catch (e: CancellationException) {
- assertEquals("Timed out waiting for 100 MILLISECONDS", e.message)
- finish(3)
- }
- }
-
- @Test
- fun testSuppressExceptionWithResult() = runTest(
- expected = { it is CancellationException }
- ) {
- expect(1)
- val result = withTimeout(100) {
- expect(2)
- try {
- delay(1000)
- } catch (e: CancellationException) {
- finish(3)
- }
- "OK"
- }
- expectUnreached()
- }
-
- @Test
- fun testSuppressExceptionWithAnotherException() = runTest(
- expected = { it is IOException }
- ) {
- expect(1)
- withTimeout(100) {
- expect(2)
- try {
- delay(1000)
- } catch (e: CancellationException) {
- finish(3)
- throw IOException(e)
- }
- expectUnreached()
- "OK"
- }
- expectUnreached()
- }
-}
\ No newline at end of file
diff --git a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/channels/BroadcastChannelMultiReceiveStressTest.kt b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/channels/BroadcastChannelMultiReceiveStressTest.kt
index 7f341c1..60bdeb1 100644
--- a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/channels/BroadcastChannelMultiReceiveStressTest.kt
+++ b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/channels/BroadcastChannelMultiReceiveStressTest.kt
@@ -18,10 +18,10 @@
import kotlinx.coroutines.experimental.*
import kotlinx.coroutines.experimental.selects.*
+import kotlinx.coroutines.experimental.timeunit.*
import org.junit.*
import org.junit.runner.*
import org.junit.runners.*
-import java.util.concurrent.*
import java.util.concurrent.atomic.*
import kotlin.coroutines.experimental.*
diff --git a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/channels/BroadcastChannelSubStressTest.kt b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/channels/BroadcastChannelSubStressTest.kt
index 21b238c..cfef040 100644
--- a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/channels/BroadcastChannelSubStressTest.kt
+++ b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/channels/BroadcastChannelSubStressTest.kt
@@ -17,10 +17,10 @@
package kotlinx.coroutines.experimental.channels
import kotlinx.coroutines.experimental.*
+import kotlinx.coroutines.experimental.timeunit.*
import org.junit.*
import org.junit.runner.*
import org.junit.runners.*
-import java.util.concurrent.*
import java.util.concurrent.atomic.*
import kotlin.coroutines.experimental.*
diff --git a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectMutexStressTest.kt b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectMutexStressTest.kt
new file mode 100644
index 0000000..d7be9ee
--- /dev/null
+++ b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectMutexStressTest.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package kotlinx.coroutines.experimental.selects
+
+import kotlinx.coroutines.experimental.*
+import kotlinx.coroutines.experimental.sync.*
+import kotlin.test.*
+
+class SelectMutexStressTest : TestBase() {
+ @Test
+ fun testSelectCancelledResourceRelease() = runTest {
+ val n = 1_000 * stressTestMultiplier
+ val mutex = Mutex(true) as MutexImpl // locked
+ expect(1)
+ repeat(n) { i ->
+ val job = launch(kotlin.coroutines.experimental.coroutineContext) {
+ expect(i + 2)
+ select<Unit> {
+ mutex.onLock {
+ expectUnreached() // never able to lock
+ }
+ }
+ }
+ yield() // to the launched job, so that it suspends
+ job.cancel() // cancel the job and select
+ yield() // so it can cleanup after itself
+ }
+ assertTrue(mutex.isLocked)
+ assertTrue(mutex.isLockedEmptyQueueState)
+ finish(n + 2)
+ }
+}
\ No newline at end of file
diff --git a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/sync/MutexStressTest.kt b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/sync/MutexStressTest.kt
new file mode 100644
index 0000000..720bd3b
--- /dev/null
+++ b/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/sync/MutexStressTest.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package kotlinx.coroutines.experimental.sync
+
+import kotlinx.coroutines.experimental.*
+import kotlin.test.*
+
+class MutexStressTest : TestBase() {
+ @Test
+ fun testStress() = runTest {
+ val n = 1000 * stressTestMultiplier
+ val k = 100
+ var shared = 0
+ val mutex = Mutex()
+ val jobs = List(n) {
+ launch(CommonPool) {
+ repeat(k) {
+ mutex.lock()
+ shared++
+ mutex.unlock()
+ }
+ }
+ }
+ jobs.forEach { it.join() }
+ println("Shared value = $shared")
+ assertEquals(n * k, shared)
+ }
+}
\ No newline at end of file
diff --git a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/AbstractContinuation.kt b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/AbstractContinuation.kt
deleted file mode 100644
index 6b31a91..0000000
--- a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/AbstractContinuation.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright 2016-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package kotlinx.coroutines.experimental
-
-import kotlin.coroutines.experimental.Continuation
-import kotlin.coroutines.experimental.intrinsics.COROUTINE_SUSPENDED
-
-private const val UNDECIDED = 0
-private const val SUSPENDED = 1
-private const val RESUMED = 2
-
-/**
- * @suppress **This is unstable API and it is subject to change.**
- */
-internal abstract class AbstractContinuation<in T>(
- public final override val delegate: Continuation<T>,
- public final override val resumeMode: Int
-) : JobSupport(true), Continuation<T>, DispatchedTask<T> {
- private var decision = UNDECIDED
-
- /* decision state machine
-
- +-----------+ trySuspend +-----------+
- | UNDECIDED | -------------> | SUSPENDED |
- +-----------+ +-----------+
- |
- | tryResume
- V
- +-----------+
- | RESUMED |
- +-----------+
-
- Note: both tryResume and trySuspend can be invoked at most once, first invocation wins
- */
-
- override fun takeState(): Any? = state
-
- private fun trySuspend(): Boolean = when (decision) {
- UNDECIDED -> { decision = SUSPENDED; true }
- RESUMED -> false
- else -> error("Already suspended")
- }
-
- private fun tryResume(): Boolean = when (decision) {
- UNDECIDED -> { decision = RESUMED; true }
- SUSPENDED -> false
- else -> error("Already resumed")
- }
-
- @PublishedApi
- internal fun getResult(): Any? {
- if (trySuspend()) return COROUTINE_SUSPENDED
- // otherwise, onCompletionInternal was already invoked & invoked tryResume, and the result is in the state
- val state = this.state
- if (state is CompletedExceptionally) throw state.exception
- return getSuccessfulResult(state)
- }
-
- internal final override fun onCompletionInternal(state: Any?, mode: Int) {
- if (tryResume()) return // completed before getResult invocation -- bail out
- // otherwise, getResult has already commenced, i.e. completed later or in other thread
- dispatch(mode)
- }
-
- override fun resume(value: T) =
- resumeImpl(value, resumeMode)
-
- override fun resumeWithException(exception: Throwable) =
- resumeImpl(CompletedExceptionally(exception), resumeMode)
-
- protected fun resumeImpl(proposedUpdate: Any?, resumeMode: Int) {
- val state = this.state
- return when (state) {
- is Incomplete -> updateState(proposedUpdate, resumeMode)
- is Cancelled -> {
- // Ignore resumes in cancelled coroutines, but handle exception if a different one here
- if (proposedUpdate is CompletedExceptionally && proposedUpdate.exception != state.exception)
- handleException(proposedUpdate.exception)
- return
- }
- else -> error("Already resumed, but got $proposedUpdate")
- }
- }
-
- override fun handleException(exception: Throwable) {
- handleCoroutineException(context, exception)
- }
-}
\ No newline at end of file
diff --git a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonScheduled.kt b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Annotations.kt
similarity index 64%
copy from common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonScheduled.kt
copy to js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Annotations.kt
index d9181d0..26c19ae 100644
--- a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonScheduled.kt
+++ b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Annotations.kt
@@ -14,8 +14,14 @@
* limitations under the License.
*/
-package kotlinx.coroutines.experimental
+package kotlinx.coroutines.experimental.internalAnnotations
-public expect suspend fun <T> withTimeout(time: Int, block: suspend CoroutineScope.() -> T): T
+@Target(AnnotationTarget.FILE)
+internal actual annotation class JvmName(actual val name: String)
-public expect suspend fun <T> withTimeoutOrNull(time: Int, block: suspend CoroutineScope.() -> T): T?
+@Target(AnnotationTarget.FILE)
+internal actual annotation class JvmMultifileClass
+
+internal actual annotation class JvmField
+
+internal actual annotation class Volatile
diff --git a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/CancellableContinuation.kt b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/CancellableContinuation.kt
deleted file mode 100644
index 8a36f27..0000000
--- a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/CancellableContinuation.kt
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- * Copyright 2016-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// :todo: Remove after transition to Kotlin 1.2.30+
-@file:Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
-
-package kotlinx.coroutines.experimental
-
-import kotlin.coroutines.experimental.Continuation
-import kotlin.coroutines.experimental.CoroutineContext
-import kotlin.coroutines.experimental.intrinsics.suspendCoroutineOrReturn
-import kotlin.coroutines.experimental.suspendCoroutine
-
-// --------------- cancellable continuations ---------------
-
-/**
- * Cancellable continuation. Its job is _completed_ when it is resumed or cancelled.
- * When [cancel] function is explicitly invoked, this continuation immediately resumes with [CancellationException] or
- * with the specified cancel cause.
- *
- * Cancellable continuation has three states (as subset of [Job] states):
- *
- * | **State** | [isActive] | [isCompleted] | [isCancelled] |
- * | ----------------------------------- | ---------- | ------------- | ------------- |
- * | _Active_ (initial state) | `true` | `false` | `false` |
- * | _Resumed_ (final _completed_ state) | `false` | `true` | `false` |
- * | _Canceled_ (final _completed_ state)| `false` | `true` | `true` |
- *
- * Invocation of [cancel] transitions this continuation from _active_ to _cancelled_ state, while
- * invocation of [resume] or [resumeWithException] transitions it from _active_ to _resumed_ state.
- *
- * A [cancelled][isCancelled] continuation implies that it is [completed][isCompleted].
- *
- * Invocation of [resume] or [resumeWithException] in _resumed_ state produces [IllegalStateException]
- * but is ignored in _cancelled_ state.
- *
- * ```
- * +-----------+ resume +---------+
- * | Active | ----------> | Resumed |
- * +-----------+ +---------+
- * |
- * | cancel
- * V
- * +-----------+
- * | Cancelled |
- * +-----------+
- *
- * ```
- */
-public actual interface CancellableContinuation<in T> : Continuation<T> {
- /**
- * Returns `true` when this continuation is active -- it has not completed or cancelled yet.
- */
- public actual val isActive: Boolean
-
- /**
- * Returns `true` when this continuation has completed for any reason. A continuation
- * that was cancelled is also considered complete.
- */
- public actual val isCompleted: Boolean
-
- /**
- * Returns `true` if this continuation was [cancelled][cancel].
- *
- * It implies that [isActive] is `false` and [isCompleted] is `true`.
- */
- public actual val isCancelled: Boolean
-
- /**
- * Tries to resume this continuation with a given value and returns non-null object token if it was successful,
- * or `null` otherwise (it was already resumed or cancelled). When non-null object was returned,
- * [completeResume] must be invoked with it.
- *
- * When [idempotent] is not `null`, this function performs _idempotent_ operation, so that
- * further invocations with the same non-null reference produce the same result.
- *
- * @suppress **This is unstable API and it is subject to change.**
- */
- public actual fun tryResume(value: T, idempotent: Any? = null): Any?
-
- /**
- * Tries to resume this continuation with a given exception and returns non-null object token if it was successful,
- * or `null` otherwise (it was already resumed or cancelled). When non-null object was returned,
- * [completeResume] must be invoked with it.
- *
- * @suppress **This is unstable API and it is subject to change.**
- */
- public fun tryResumeWithException(exception: Throwable): Any?
-
- /**
- * Completes the execution of [tryResume] or [tryResumeWithException] on its non-null result.
- *
- * @suppress **This is unstable API and it is subject to change.**
- */
- public actual fun completeResume(token: Any)
-
- /**
- * Makes this continuation cancellable. Use it with `holdCancellability` optional parameter to
- * [suspendCancellableCoroutine] function. It throws [IllegalStateException] if invoked more than once.
- */
- public actual fun initCancellability()
-
- /**
- * Cancels this continuation with an optional cancellation [cause]. The result is `true` if this continuation was
- * cancelled as a result of this invocation and `false` otherwise.
- */
- public actual fun cancel(cause: Throwable? = null): Boolean
-
- /**
- * Registers handler that is **synchronously** invoked once on completion of this continuation.
- * When continuation is already complete, then the handler is immediately invoked
- * with continuation's exception or `null`. Otherwise, handler will be invoked once when this
- * continuation is complete.
- *
- * The resulting [DisposableHandle] can be used to [dispose][DisposableHandle.dispose] the
- * registration of this handler and release its memory if its invocation is no longer needed.
- * There is no need to dispose the handler after completion of this continuation. The references to
- * all the handlers are released when this continuation completes.
- *
- * Installed [handler] should not throw any exceptions. If it does, they will get caught,
- * wrapped into [CompletionHandlerException], and rethrown, potentially causing crash of unrelated code.
- */
- public actual fun invokeOnCompletion(handler: CompletionHandler): DisposableHandle
-
- /**
- * Resumes this continuation with a given [value] in the invoker thread without going though
- * [dispatch][CoroutineDispatcher.dispatch] function of the [CoroutineDispatcher] in the [context].
- * This function is designed to be used only by the [CoroutineDispatcher] implementations themselves.
- * **It should not be used in general code**.
- */
- public actual fun CoroutineDispatcher.resumeUndispatched(value: T)
-
- /**
- * Resumes this continuation with a given [exception] in the invoker thread without going though
- * [dispatch][CoroutineDispatcher.dispatch] function of the [CoroutineDispatcher] in the [context].
- * This function is designed to be used only by the [CoroutineDispatcher] implementations themselves.
- * **It should not be used in general code**.
- */
- public actual fun CoroutineDispatcher.resumeUndispatchedWithException(exception: Throwable)
-}
-
-/**
- * Suspends coroutine similar to [suspendCoroutine], but provide an implementation of [CancellableContinuation] to
- * the [block]. This function throws [CancellationException] if the coroutine is cancelled or completed while suspended.
- *
- * If [holdCancellability] optional parameter is `true`, then the coroutine is suspended, but it is not
- * cancellable until [CancellableContinuation.initCancellability] is invoked.
- *
- * See [suspendAtomicCancellableCoroutine] for suspending functions that need *atomic cancellation*.
- */
-public actual inline suspend fun <T> suspendCancellableCoroutine(
- holdCancellability: Boolean = false,
- crossinline block: (CancellableContinuation<T>) -> Unit
-): T =
- suspendCoroutineOrReturn { cont ->
- val cancellable = CancellableContinuationImpl(cont, resumeMode = MODE_CANCELLABLE)
- if (!holdCancellability) cancellable.initCancellability()
- block(cancellable)
- cancellable.getResult()
-}
-
-/**
- * Suspends coroutine similar to [suspendCancellableCoroutine], but with *atomic cancellation*.
- *
- * When suspended function throws [CancellationException] it means that the continuation was not resumed.
- * As a side-effect of atomic cancellation, a thread-bound coroutine (to some UI thread, for example) may
- * continue to execute even after it was cancelled from the same thread in the case when the continuation
- * was already resumed and was posted for execution to the thread's queue.
- */
-public actual inline suspend fun <T> suspendAtomicCancellableCoroutine(
- holdCancellability: Boolean = false,
- crossinline block: (CancellableContinuation<T>) -> Unit
-): T =
- suspendCoroutineOrReturn { cont ->
- val cancellable = CancellableContinuationImpl(cont, resumeMode = MODE_ATOMIC_DEFAULT)
- if (!holdCancellability) cancellable.initCancellability()
- block(cancellable)
- cancellable.getResult()
- }
-
-// --------------- implementation details ---------------
-
-@PublishedApi
-internal class CancellableContinuationImpl<in T>(
- delegate: Continuation<T>,
- resumeMode: Int
-) : AbstractContinuation<T>(delegate, resumeMode), CancellableContinuation<T> {
- @Volatile // just in case -- we don't want an extra data race, even benign one
- private var _context: CoroutineContext? = null // created on first need
-
- public override val context: CoroutineContext
- get() = _context ?: (delegate.context + this).also { _context = it }
-
- override fun initCancellability() {
- initParentJobInternal(delegate.context[Job])
- }
-
- override val onCancelMode: Int get() = ON_CANCEL_MAKE_CANCELLED
-
- override fun tryResume(value: T, idempotent: Any?): Any? {
- val state = this.state
- return when (state) {
- is Incomplete -> {
- val update: Any? = if (idempotent == null) value else
- CompletedIdempotentResult(idempotent, value, state)
- tryUpdateState(update)
- state
- }
- is CompletedIdempotentResult -> {
- if (state.idempotentResume === idempotent) {
- check(state.result === value) { "Non-idempotent resume" }
- state.token
- } else
- null
- }
- else -> null // cannot resume -- not active anymore
- }
- }
-
- override fun tryResumeWithException(exception: Throwable): Any? {
- val state = this.state
- return when (state) {
- is Incomplete -> {
- tryUpdateState(CompletedExceptionally(exception))
- state
- }
- else -> null // cannot resume -- not active anymore
- }
- }
-
- override fun completeResume(token: Any) {
- completeUpdateState(token as Incomplete, state, resumeMode)
- }
-
- override fun invokeOnCompletion(handler: CompletionHandler): DisposableHandle =
- invokeOnCompletion(onCancelling = false, invokeImmediately = true, handler = handler)
-
- override fun CoroutineDispatcher.resumeUndispatched(value: T) {
- val dc = delegate as? DispatchedContinuation
- resumeImpl(value, if (dc?.dispatcher === this) MODE_UNDISPATCHED else resumeMode)
- }
-
- override fun CoroutineDispatcher.resumeUndispatchedWithException(exception: Throwable) {
- val dc = delegate as? DispatchedContinuation
- resumeImpl(CompletedExceptionally(exception), if (dc?.dispatcher === this) MODE_UNDISPATCHED else resumeMode)
- }
-
- @Suppress("UNCHECKED_CAST")
- override fun <T> getSuccessfulResult(state: Any?): T =
- if (state is CompletedIdempotentResult) state.result as T else state as T
-
- override fun nameString(): String =
- "CancellableContinuation(${delegate.toDebugString()})"
-
- // todo: This workaround for KT-21968, should be removed in the future
- public override fun cancel(cause: Throwable?): Boolean =
- super.cancel(cause)
-}
-
-private class CompletedIdempotentResult(
- val idempotentResume: Any?,
- val result: Any?,
- val token: JobSupport.Incomplete
-) {
- override fun toString(): String = "CompletedIdempotentResult[$result]"
-}
-
diff --git a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/CompletableDeferred.kt b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/CompletableDeferred.kt
deleted file mode 100644
index dca6ee9..0000000
--- a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/CompletableDeferred.kt
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2016-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// :todo: Remove after transition to Kotlin 1.2.30+
-@file:Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
-
-package kotlinx.coroutines.experimental
-
-/**
- * A [Deferred] that can be completed via public functions
- * [complete], [completeExceptionally], and [cancel].
- *
- * Completion functions return `false` when this deferred value is already complete or completing.
- *
- * An instance of completable deferred can be created by `CompletableDeferred()` function in _active_ state.
- *
- * All functions on this interface and on all interfaces derived from it are **thread-safe** and can
- * be safely invoked from concurrent coroutines without external synchronization.
- */
-public actual interface CompletableDeferred<T> : Deferred<T> {
- /**
- * Completes this deferred value with a given [value]. The result is `true` if this deferred was
- * completed as a result of this invocation and `false` otherwise (if it was already completed).
- *
- * Repeated invocations of this function have no effect and always produce `false`.
- */
- public actual fun complete(value: T): Boolean
-
- /**
- * Completes this deferred value exceptionally with a given [exception]. The result is `true` if this deferred was
- * completed as a result of this invocation and `false` otherwise (if it was already completed).
- *
- * Repeated invocations of this function have no effect and always produce `false`.
- */
- public actual fun completeExceptionally(exception: Throwable): Boolean
-}
-
-/**
- * Creates a [CompletableDeferred] in an _active_ state.
- * It is optionally a child of a [parent] job.
- */
-@Suppress("FunctionName")
-public actual fun <T> CompletableDeferred(parent: Job? = null): CompletableDeferred<T> = CompletableDeferredImpl(parent)
-
-/**
- * Creates an already _completed_ [CompletableDeferred] with a given [value].
- */
-@Suppress("FunctionName")
-public actual fun <T> CompletableDeferred(value: T): CompletableDeferred<T> = CompletableDeferredImpl<T>(null).apply { complete(value) }
-
-/**
- * Concrete implementation of [CompletableDeferred].
- */
-@Suppress("UNCHECKED_CAST")
-private class CompletableDeferredImpl<T>(
- parent: Job?
-) : JobSupport(true), CompletableDeferred<T> {
- init { initParentJobInternal(parent) }
- override val onCancelMode: Int get() = ON_CANCEL_MAKE_COMPLETING
-
- override fun getCompleted(): T = getCompletedInternal() as T
- override suspend fun await(): T = awaitInternal() as T
-
- override fun complete(value: T): Boolean =
- makeCompleting(value)
-
- override fun completeExceptionally(exception: Throwable): Boolean =
- makeCompleting(CompletedExceptionally(exception))
-}
diff --git a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/CompletionHandler.kt b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/CompletionHandler.kt
new file mode 100644
index 0000000..82db551
--- /dev/null
+++ b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/CompletionHandler.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package kotlinx.coroutines.experimental
+
+import kotlinx.coroutines.experimental.internal.*
+
+internal actual abstract class CompletionHandlerNode : LinkedListNode() {
+ @Suppress("UnsafeCastFromDynamic")
+ actual inline val asHandler: CompletionHandler get() = asDynamic()
+ actual abstract fun invoke(cause: Throwable?)
+}
+
+internal actual fun CompletionHandler.invokeIt(cause: Throwable?) {
+ when(jsTypeOf(this)) {
+ "function" -> invoke(cause)
+ else -> (this as CompletionHandlerNode).invoke(cause)
+ }
+}
diff --git a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineContext.kt b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineContext.kt
index 73358a9..6d1cbd8 100644
--- a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineContext.kt
+++ b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineContext.kt
@@ -19,23 +19,6 @@
import kotlin.browser.*
import kotlin.coroutines.experimental.*
-/**
- * A coroutine dispatcher that is not confined to any specific thread.
- * It executes initial continuation of the coroutine _right here_ in the current call-frame
- * and let the coroutine resume in whatever thread that is used by the corresponding suspending function, without
- * mandating any specific threading policy.
- *
- * Note, that if you need your coroutine to be confined to a particular thread or a thread-pool after resumption,
- * but still want to execute it in the current call-frame until its first suspension, then you can use
- * an optional [CoroutineStart] parameter in coroutine builders like [launch] and [async] setting it to the
- * the value of [CoroutineStart.UNDISPATCHED].
- */
-public actual object Unconfined : CoroutineDispatcher() {
- actual override fun isDispatchNeeded(context: CoroutineContext): Boolean = false
- actual override fun dispatch(context: CoroutineContext, block: Runnable) { throw UnsupportedOperationException() }
- override fun toString(): String = "Unconfined"
-}
-
private external val navigator: dynamic
private const val UNDEFINED = "undefined"
@@ -58,11 +41,13 @@
else -> NodeDispatcher()
}
+internal actual val DefaultDelay: Delay = DefaultDispatcher as Delay
+
/**
* Creates context for the new coroutine. It installs [DefaultDispatcher] when no other dispatcher nor
* [ContinuationInterceptor] is specified, and adds optional support for debugging facilities (when turned on).
*/
-public fun newCoroutineContext(context: CoroutineContext, parent: Job? = null): CoroutineContext {
+public actual fun newCoroutineContext(context: CoroutineContext, parent: Job? = null): CoroutineContext {
val wp = if (parent == null) context else context + parent
return if (context !== DefaultDispatcher && context[ContinuationInterceptor] == null)
wp + DefaultDispatcher else wp
diff --git a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineDispatcher.kt b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineDispatcher.kt
deleted file mode 100644
index b6e659e..0000000
--- a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineDispatcher.kt
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2016-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package kotlinx.coroutines.experimental
-
-import kotlin.coroutines.experimental.AbstractCoroutineContextElement
-import kotlin.coroutines.experimental.Continuation
-import kotlin.coroutines.experimental.ContinuationInterceptor
-import kotlin.coroutines.experimental.CoroutineContext
-
-/**
- * Base class that shall be extended by all coroutine dispatcher implementations.
- *
- * The following standard implementations are provided by `kotlinx.coroutines`:
- * * [Unconfined] -- starts coroutine execution in the current call-frame until the first suspension.
- * On first suspension the coroutine builder function returns.
- * The coroutine will resume in whatever thread that is used by the
- * corresponding suspending function, without confining it to any specific thread or pool.
- * This in an appropriate choice for IO-intensive coroutines that do not consume CPU resources.
- * * [DefaultDispatcher] -- is used by all standard builder if no dispatcher nor any other [ContinuationInterceptor]
- * is specified in their context.
- */
-public actual abstract class CoroutineDispatcher actual constructor() :
- AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
- /**
- * Returns `true` if execution shall be dispatched onto another thread.
- * The default behaviour for most dispatchers is to return `true`.
- *
- * UI dispatchers _should not_ override `isDispatchNeeded`, but leave a default implementation that
- * returns `true`. To understand the rationale beyond this recommendation, consider the following code:
- *
- * ```kotlin
- * fun asyncUpdateUI() = async(MainThread) {
- * // do something here that updates something in UI
- * }
- * ```
- *
- * When you invoke `asyncUpdateUI` in some background thread, it immediately continues to the next
- * line, while UI update happens asynchronously in the UI thread. However, if you invoke
- * it in the UI thread itself, it updates UI _synchronously_ if your `isDispatchNeeded` is
- * overridden with a thread check. Checking if we are already in the UI thread seems more
- * efficient (and it might indeed save a few CPU cycles), but this subtle and context-sensitive
- * difference in behavior makes the resulting async code harder to debug.
- *
- * Basically, the choice here is between "JS-style" asynchronous approach (async actions
- * are always postponed to be executed later in the even dispatch thread) and "C#-style" approach
- * (async actions are executed in the invoker thread until the first suspension point).
- * While, C# approach seems to be more efficient, it ends up with recommendations like
- * "use `yield` if you need to ....". This is error-prone. JS-style approach is more consistent
- * and does not require programmers to think about whether they need to yield or not.
- *
- * However, coroutine builders like [launch] and [async] accept an optional [CoroutineStart]
- * parameter that allows one to optionally choose C#-style [CoroutineStart.UNDISPATCHED] behaviour
- * whenever it is needed for efficiency.
- */
- public actual open fun isDispatchNeeded(context: CoroutineContext): Boolean = true
-
- /**
- * Dispatches execution of a runnable [block] onto another thread in the given [context].
- */
- public actual abstract fun dispatch(context: CoroutineContext, block: Runnable)
-
- /**
- * Returns continuation that wraps the original [continuation], thus intercepting all resumptions.
- */
- public actual override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
- DispatchedContinuation(this, continuation)
-}
-
-/**
- * A runnable task for [CoroutineDispatcher.dispatch].
- */
-public actual interface Runnable {
- public actual fun run()
-}
-
diff --git a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineExceptionHandler.kt b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineExceptionHandler.kt
deleted file mode 100644
index 61743d0..0000000
--- a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineExceptionHandler.kt
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2016-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package kotlinx.coroutines.experimental
-
-import kotlin.coroutines.experimental.AbstractCoroutineContextElement
-import kotlin.coroutines.experimental.CoroutineContext
-
-/**
- * Helper function for coroutine builder implementations to handle uncaught exception in coroutines.
- *
- * It tries to handle uncaught exception in the following way:
- * * If there is [CoroutineExceptionHandler] in the context, then it is used.
- * * Otherwise, if exception is [CancellationException] then it is ignored
- * (because that is the supposed mechanism to cancel the running coroutine)
- * * Otherwise:
- * * if there is a [Job] in the context, then [Job.cancel] is invoked;
- * * exception is logged to console.
- */
-public actual fun handleCoroutineException(context: CoroutineContext, exception: Throwable) {
- context[CoroutineExceptionHandler]?.let {
- it.handleException(context, exception)
- return
- }
- // ignore CancellationException (they are normal means to terminate a coroutine)
- if (exception is CancellationException) return
- // try cancel job in the context
- context[Job]?.cancel(exception)
- // log exception
- console.error(exception)
-}
-
-/**
- * An optional element on the coroutine context to handle uncaught exceptions.
- *
- * By default, when no handler is installed, uncaught exception are handled in the following way:
- * * If exception is [CancellationException] then it is ignored
- * (because that is the supposed mechanism to cancel the running coroutine)
- * * Otherwise:
- * * if there is a [Job] in the context, then [Job.cancel] is invoked;
- * * exception is logged to console.
- *
- * See [handleCoroutineException].
- */
-public actual interface CoroutineExceptionHandler : CoroutineContext.Element {
- /**
- * Key for [CoroutineExceptionHandler] instance in the coroutine context.
- */
- public actual companion object Key : CoroutineContext.Key<CoroutineExceptionHandler>
-
- /**
- * Handles uncaught [exception] in the given [context]. It is invoked
- * if coroutine has an uncaught exception. See [handleCoroutineException].
- */
- public actual fun handleException(context: CoroutineContext, exception: Throwable)
-}
-
-/**
- * Creates new [CoroutineExceptionHandler] instance.
- * @param handler a function which handles exception thrown by a coroutine
- */
-@Suppress("FunctionName")
-public actual inline fun CoroutineExceptionHandler(crossinline handler: (CoroutineContext, Throwable) -> Unit): CoroutineExceptionHandler =
- object: AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler {
- override fun handleException(context: CoroutineContext, exception: Throwable) =
- handler.invoke(context, exception)
- }
\ No newline at end of file
diff --git a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonYield.kt b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineExceptionHandlerImpl.kt
similarity index 76%
copy from common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonYield.kt
copy to js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineExceptionHandlerImpl.kt
index e461c47..d9ade66 100644
--- a/common/kotlinx-coroutines-core-common/src/main/kotlin/kotlinx/coroutines/experimental/CommonYield.kt
+++ b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineExceptionHandlerImpl.kt
@@ -16,4 +16,9 @@
package kotlinx.coroutines.experimental
-public expect suspend fun yield()
\ No newline at end of file
+import kotlin.coroutines.experimental.*
+
+internal actual fun handleCoroutineExceptionImpl(context: CoroutineContext, exception: Throwable) {
+ // log exception
+ console.error(exception)
+}
diff --git a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Debug.kt b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Debug.kt
index e2e8908..546864c 100644
--- a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Debug.kt
+++ b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Debug.kt
@@ -16,4 +16,17 @@
package kotlinx.coroutines.experimental
+private var counter = 0
+
+internal actual val Any.hexAddress: String
+ get() {
+ var result = this.asDynamic().__debug_counter
+ if (jsTypeOf(result) !== "number") {
+ result = ++counter
+ this.asDynamic().__debug_counter = result
+
+ }
+ return (result as Int).toString()
+ }
+
internal actual val Any.classSimpleName: String get() = this::class.simpleName ?: "Unknown"
diff --git a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Deferred.kt b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Deferred.kt
deleted file mode 100644
index 9e36e31..0000000
--- a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Deferred.kt
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright 2016-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// :todo: Remove after transition to Kotlin 1.2.30+
-@file:Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
-
-package kotlinx.coroutines.experimental
-
-import kotlinx.coroutines.experimental.intrinsics.*
-import kotlin.coroutines.experimental.*
-
-/**
- * Deferred value is a non-blocking cancellable future.
- *
- * It is created with [async] coroutine builder or via constructor of [CompletableDeferred] class.
- * It is in [active][isActive] state while the value is being computed.
- *
- * Deferred value has the following states:
- *
- * | **State** | [isActive] | [isCompleted] | [isCompletedExceptionally] | [isCancelled] |
- * | --------------------------------------- | ---------- | ------------- | -------------------------- | ------------- |
- * | _New_ (optional initial state) | `false` | `false` | `false` | `false` |
- * | _Active_ (default initial state) | `true` | `false` | `false` | `false` |
- * | _Completing_ (optional transient state) | `true` | `false` | `false` | `false` |
- * | _Cancelling_ (optional transient state) | `false` | `false` | `false` | `true` |
- * | _Cancelled_ (final state) | `false` | `true` | `true` | `true` |
- * | _Resolved_ (final state) | `false` | `true` | `false` | `false` |
- * | _Failed_ (final state) | `false` | `true` | `true` | `false` |
- *
- * Usually, a deferred value is created in _active_ state (it is created and started).
- * However, [async] coroutine builder has an optional `start` parameter that creates a deferred value in _new_ state
- * when this parameter is set to [CoroutineStart.LAZY].
- * Such a deferred can be be made _active_ by invoking [start], [join], or [await].
- *
- * A deferred can be _cancelled_ at any time with [cancel] function that forces it to transition to
- * _cancelling_ state immediately. Deferred that is not backed by a coroutine (see [CompletableDeferred]) and does not have
- * [children] becomes _cancelled_ on [cancel] immediately.
- * Otherwise, deferred becomes _cancelled_ when it finishes executing its code and
- * when all its children [complete][isCompleted].
- *
- * ```
- * wait children
- * +-----+ start +--------+ complete +-------------+ finish +-----------+
- * | New | ---------------> | Active | ----------> | Completing | ---+-> | Resolved |
- * +-----+ +--------+ +-------------+ | |(completed)|
- * | | | | +-----------+
- * | cancel | cancel | cancel |
- * V V | | +-----------+
- * +-----------+ finish +------------+ | +-> | Failed |
- * | Cancelled | <--------- | Cancelling | <---------------+ |(completed)|
- * |(completed)| +------------+ +-----------+
- * +-----------+
- * ```
- *
- * A deferred value is a [Job]. A job in the
- * [coroutineContext](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines.experimental/coroutine-context.html)
- * of [async] builder represents the coroutine itself.
- * A deferred value is active while the coroutine is working and cancellation aborts the coroutine when
- * the coroutine is suspended on a _cancellable_ suspension point by throwing [CancellationException]
- * or the cancellation cause inside the coroutine.
- *
- * A deferred value can have a _parent_ job. A deferred value with a parent is cancelled when its parent is
- * cancelled or completes. Parent waits for all its [children] to complete in _completing_ or
- * _cancelling_ state. _Completing_ state is purely internal. For an outside observer a _completing_
- * deferred is still active, while internally it is waiting for its children.
- *
- * All functions on this interface and on all interfaces derived from it are **thread-safe** and can
- * be safely invoked from concurrent coroutines without external synchronization.
- */
-public actual interface Deferred<out T> : Job {
- /**
- * Returns `true` if computation of this deferred value has _completed exceptionally_ -- it had
- * either _failed_ with exception during computation or was [cancelled][cancel].
- *
- * It implies that [isActive] is `false` and [isCompleted] is `true`.
- */
- public actual val isCompletedExceptionally: Boolean
-
- /**
- * Awaits for completion of this value without blocking a thread and resumes when deferred computation is complete,
- * returning the resulting value or throwing the corresponding exception if the deferred had completed exceptionally.
- *
- * This suspending function is cancellable.
- * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function
- * immediately resumes with [CancellationException].
- */
- public actual suspend fun await(): T
-
- /**
- * Returns *completed* result or throws [IllegalStateException] if this deferred value has not
- * [completed][isCompleted] yet. It throws the corresponding exception if this deferred has
- * [completed exceptionally][isCompletedExceptionally].
- *
- * This function is designed to be used from [invokeOnCompletion] handlers, when there is an absolute certainty that
- * the value is already complete. See also [getCompletionExceptionOrNull].
- */
- public actual fun getCompleted(): T
-
- /**
- * Returns *completion exception* result if this deferred [completed exceptionally][isCompletedExceptionally],
- * `null` if it is completed normally, or throws [IllegalStateException] if this deferred value has not
- * [completed][isCompleted] yet.
- *
- * This function is designed to be used from [invokeOnCompletion] handlers, when there is an absolute certainty that
- * the value is already complete. See also [getCompleted].
- */
- public actual fun getCompletionExceptionOrNull(): Throwable?
-}
-
-/**
- * Creates new coroutine and returns its future result as an implementation of [Deferred].
- *
- * The running coroutine is cancelled when the resulting object is [cancelled][Job.cancel].
- *
- * The [context] for the new coroutine can be explicitly specified.
- * See [CoroutineDispatcher] for the standard context implementations that are provided by `kotlinx.coroutines`.
- * The [coroutineContext](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines.experimental/coroutine-context.html)
- * of the parent coroutine may be used,
- * in which case the [Job] of the resulting coroutine is a child of the job of the parent coroutine.
- * The parent job may be also explicitly specified using [parent] parameter.
- *
- * If the context does not have any dispatcher nor any other [ContinuationInterceptor], then [DefaultDispatcher] is used.
- *
- * By default, the coroutine is immediately scheduled for execution.
- * Other options can be specified via `start` parameter. See [CoroutineStart] for details.
- * An optional [start] parameter can be set to [CoroutineStart.LAZY] to start coroutine _lazily_. In this case,,
- * the resulting [Deferred] is created in _new_ state. It can be explicitly started with [start][Job.start]
- * function and will be started implicitly on the first invocation of [join][Job.join] or [await][Deferred.await].
- *
- * @param context context of the coroutine. The default value is [DefaultDispatcher].
- * @param start coroutine start option. The default value is [CoroutineStart.DEFAULT].
- * @param parent explicitly specifies the parent job, overrides job from the [context] (if any).
- * @param onCompletion optional completion handler for the coroutine (see [Job.invokeOnCompletion]).
- * @param block the coroutine code.
- */
-public actual fun <T> async(
- context: CoroutineContext = DefaultDispatcher,
- start: CoroutineStart = CoroutineStart.DEFAULT,
- parent: Job? = null,
- onCompletion: CompletionHandler? = null,
- block: suspend CoroutineScope.() -> T
-): Deferred<T> {
- val newContext = newCoroutineContext(context, parent)
- val coroutine = if (start.isLazy)
- LazyDeferredCoroutine(newContext, block) else
- DeferredCoroutine<T>(newContext, active = true)
- if (onCompletion != null) coroutine.invokeOnCompletion(handler = onCompletion)
- coroutine.start(start, coroutine, block)
- return coroutine
-}
-
-@Suppress("UNCHECKED_CAST")
-private open class DeferredCoroutine<T>(
- parentContext: CoroutineContext,
- active: Boolean
-) : AbstractCoroutine<T>(parentContext, active), Deferred<T> {
- override fun getCompleted(): T = getCompletedInternal() as T
- override suspend fun await(): T = awaitInternal() as T
-}
-
-private class LazyDeferredCoroutine<T>(
- parentContext: CoroutineContext,
- private val block: suspend CoroutineScope.() -> T
-) : DeferredCoroutine<T>(parentContext, active = false) {
- override fun onStart() {
- block.startCoroutineCancellable(this, this)
- }
-}
diff --git a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Delay.kt b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Delay.kt
deleted file mode 100644
index 10b7c18..0000000
--- a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Delay.kt
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 2016-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package kotlinx.coroutines.experimental
-
-import kotlin.coroutines.experimental.ContinuationInterceptor
-import kotlin.coroutines.experimental.CoroutineContext
-
-/**
- * This dispatcher _feature_ is implemented by [CoroutineDispatcher] implementations that natively support
- * scheduled execution of tasks.
- *
- * Implementation of this interface affects operation of
- * [delay][kotlinx.coroutines.experimental.delay] and [withTimeout] functions.
- */
-public actual interface Delay {
- /**
- * Schedules resume of a specified [continuation] after a specified delay [time].
- *
- * Continuation **must be scheduled** to resume even if it is already cancelled, because a cancellation is just
- * an exception that the coroutine that used `delay` might wanted to catch and process. It might
- * need to close some resources in its `finally` blocks, for example.
- *
- * This implementation is supposed to use dispatcher's native ability for scheduled execution in its thread(s).
- * In order to avoid an extra delay of execution, the following code shall be used to resume this
- * [continuation] when the code is already executing in the appropriate dispatcher:
- *
- * ```kotlin
- * with(continuation) { resumeUndispatched(Unit) }
- * ```
- */
- fun scheduleResumeAfterDelay(time: Int, continuation: CancellableContinuation<Unit>)
-
- /**
- * Schedules invocation of a specified [block] after a specified delay [time].
- * The resulting [DisposableHandle] can be used to [dispose][DisposableHandle.dispose] of this invocation
- * request if it is not needed anymore.
- */
- fun invokeOnTimeout(time: Int, block: Runnable): DisposableHandle
-}
-
-/**
- * Delays coroutine for a given time without blocking and resumes it after a specified time.
- * This suspending function is cancellable.
- * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function
- * immediately resumes with [CancellationException].
- *
- * This function delegates to [Delay.scheduleResumeAfterDelay] if the context [CoroutineDispatcher]
- * implements [Delay] interface, otherwise it resumes using a built-in scheduler.
- *
- * @param time time in milliseconds.
- */
-public actual suspend fun delay(time: Int) {
- kotlin.require(time >= 0) { "Delay time $time cannot be negative" }
- if (time <= 0) return // don't delay
- return suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->
- cont.context.delay.scheduleResumeAfterDelay(time, cont)
- }
-}
-
-/** Returns [Delay] implementation of the given context */
-internal val CoroutineContext.delay: Delay get() =
- get(ContinuationInterceptor) as? Delay ?: (DefaultDispatcher as Delay)
diff --git a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Exceptions.kt b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Exceptions.kt
index 0d95eba..37a0437 100644
--- a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Exceptions.kt
+++ b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Exceptions.kt
@@ -24,6 +24,12 @@
public override val cause: Throwable
) : RuntimeException(message.withCause(cause))
+/**
+ * Thrown by cancellable suspending functions if the [Job] of the coroutine is cancelled while it is suspending.
+ * It indicates _normal_ cancellation of a coroutine.
+ * **It is not printed to console/log by default uncaught exception handler**.
+ * (see [handleCoroutineException]).
+ */
public actual open class CancellationException actual constructor(message: String) : IllegalStateException(message)
/**
@@ -47,26 +53,6 @@
(message!!.hashCode() * 31 + job.hashCode()) * 31 + (cause?.hashCode() ?: 0)
}
-/**
- * This exception is thrown by [withTimeout] to indicate timeout.
- */
-@Suppress("DEPRECATION")
-public actual class TimeoutCancellationException internal constructor(
- message: String,
- internal val coroutine: Job?
-) : CancellationException(message) {
- /**
- * Creates timeout exception with a given message.
- */
- public actual constructor(message: String) : this(message, null)
-}
-
-@Suppress("FunctionName")
-internal fun TimeoutCancellationException(
- time: Int,
- coroutine: Job
-) : TimeoutCancellationException = TimeoutCancellationException("Timed out waiting for $time", coroutine)
-
internal actual class DispatchException actual constructor(message: String, cause: Throwable) : RuntimeException(message.withCause(cause))
@Suppress("FunctionName")
@@ -75,3 +61,6 @@
private fun String.withCause(cause: Throwable?) =
if (cause == null) this else "$this; caused by $cause"
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun Throwable.addSuppressedThrowable(other: Throwable) { /* empty */ }
\ No newline at end of file
diff --git a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/JSDispatcher.kt b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/JSDispatcher.kt
index 53cadc1..10e072f 100644
--- a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/JSDispatcher.kt
+++ b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/JSDispatcher.kt
@@ -16,6 +16,7 @@
package kotlinx.coroutines.experimental
+import kotlinx.coroutines.experimental.timeunit.TimeUnit
import kotlin.coroutines.experimental.*
import org.w3c.dom.*
@@ -24,12 +25,12 @@
setTimeout({ block.run() }, 0)
}
- override fun scheduleResumeAfterDelay(time: Int, continuation: CancellableContinuation<Unit>) {
- setTimeout({ with(continuation) { resumeUndispatched(Unit) } }, time.coerceAtLeast(0))
+ override fun scheduleResumeAfterDelay(time: Long, unit: TimeUnit, continuation: CancellableContinuation<Unit>) {
+ setTimeout({ with(continuation) { resumeUndispatched(Unit) } }, time.toIntMillis(unit))
}
- override fun invokeOnTimeout(time: Int, block: Runnable): DisposableHandle {
- val handle = setTimeout({ block.run() }, time.coerceAtLeast(0))
+ override fun invokeOnTimeout(time: Long, unit: TimeUnit, block: Runnable): DisposableHandle {
+ val handle = setTimeout({ block.run() }, time.toIntMillis(unit))
return object : DisposableHandle {
override fun dispose() {
clearTimeout(handle)
@@ -60,12 +61,12 @@
queue.enqueue(block)
}
- override fun scheduleResumeAfterDelay(time: Int, continuation: CancellableContinuation<Unit>) {
- window.setTimeout({ with(continuation) { resumeUndispatched(Unit) } }, time.coerceAtLeast(0))
+ override fun scheduleResumeAfterDelay(time: Long, unit: TimeUnit, continuation: CancellableContinuation<Unit>) {
+ window.setTimeout({ with(continuation) { resumeUndispatched(Unit) } }, time.toIntMillis(unit))
}
- override fun invokeOnTimeout(time: Int, block: Runnable): DisposableHandle {
- val handle = window.setTimeout({ block.run() }, time.coerceAtLeast(0))
+ override fun invokeOnTimeout(time: Long, unit: TimeUnit, block: Runnable): DisposableHandle {
+ val handle = window.setTimeout({ block.run() }, time.toIntMillis(unit))
return object : DisposableHandle {
override fun dispose() {
window.clearTimeout(handle)
@@ -106,6 +107,9 @@
}
}
+private fun Long.toIntMillis(unit: TimeUnit): Int =
+ unit.toMillis(this).coerceIn(0L, Int.MAX_VALUE.toLong()).toInt()
+
internal open class Queue<T : Any> {
private var queue = arrayOfNulls<Any?>(8)
private var head = 0
diff --git a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Job.kt b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Job.kt
deleted file mode 100644
index 42de2d3..0000000
--- a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Job.kt
+++ /dev/null
@@ -1,1113 +0,0 @@
-/*
- * Copyright 2016-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// :todo: Remove after transition to Kotlin 1.2.30+
-@file:Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
-
-package kotlinx.coroutines.experimental
-
-import kotlinx.coroutines.experimental.internal.LinkedListHead
-import kotlinx.coroutines.experimental.internal.LinkedListNode
-import kotlin.coroutines.experimental.CoroutineContext
-import kotlin.coroutines.experimental.buildSequence
-import kotlin.coroutines.experimental.intrinsics.suspendCoroutineOrReturn
-
-/**
- * A background job. Conceptually, a job is a cancellable thing with a simple life-cycle that
- * culminates in its completion. Jobs can be arranged into parent-child hierarchies where cancellation
- * or completion of parent immediately cancels all its [children].
- *
- * The most basic instances of [Job] are created with [launch] coroutine builder or with a
- * `Job()` factory function. Other coroutine builders and primitives like
- * [Deferred] also implement [Job] interface.
- *
- * A job has the following states:
- *
- * | **State** | [isActive] | [isCompleted] | [isCancelled] |
- * | --------------------------------------- | ---------- | ------------- | ------------- |
- * | _New_ (optional initial state) | `false` | `false` | `false` |
- * | _Active_ (default initial state) | `true` | `false` | `false` |
- * | _Completing_ (optional transient state) | `true` | `false` | `false` |
- * | _Cancelling_ (optional transient state) | `false` | `false` | `true` |
- * | _Cancelled_ (final state) | `false` | `true` | `true` |
- * | _Completed_ (final state) | `false` | `true` | `false` |
- *
- * Usually, a job is created in _active_ state (it is created and started). However, coroutine builders
- * that provide an optional `start` parameter create a coroutine in _new_ state when this parameter is set to
- * [CoroutineStart.LAZY]. Such a job can be made _active_ by invoking [start] or [join].
- *
- * A job can be _cancelled_ at any time with [cancel] function that forces it to transition to
- * _cancelling_ state immediately. Job that is not backed by a coroutine (see `Job()` function) and does not have
- * [children] becomes _cancelled_ on [cancel] immediately.
- * Otherwise, job becomes _cancelled_ when it finishes executing its code and
- * when all its children [complete][isCompleted].
- *
- * ```
- * wait children
- * +-----+ start +--------+ complete +-------------+ finish +-----------+
- * | New | ---------------> | Active | -----------> | Completing | -------> | Completed |
- * +-----+ +--------+ +-------------+ +-----------+
- * | | |
- * | cancel | cancel | cancel
- * V V |
- * +-----------+ finish +------------+ |
- * | Cancelled | <--------- | Cancelling | <----------------+
- * |(completed)| +------------+
- * +-----------+
- * ```
- *
- * A job in the
- * [coroutineContext](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines.experimental/coroutine-context.html)
- * represents the coroutine itself.
- * A job is active while the coroutine is working and job's cancellation aborts the coroutine when
- * the coroutine is suspended on a _cancellable_ suspension point by throwing [CancellationException].
- *
- * A job can have a _parent_ job. A job with a parent is cancelled when its parent is cancelled or completes exceptionally.
- * Parent job waits for all its children to complete in _completing_ or _cancelling_ state.
- * _Completing_ state is purely internal to the job. For an outside observer a _completing_ job is still active,
- * while internally it is waiting for its children.
- *
- * All functions on this interface and on all interfaces derived from it are **thread-safe** and can
- * be safely invoked from concurrent coroutines without external synchronization.
- */
-public actual interface Job : CoroutineContext.Element {
-
- // ------------ state query ------------
-
- /**
- * Returns `true` when this job is active -- it was already started and has not completed or cancelled yet.
- * The job that is waiting for its [children] to complete is still considered to be active if it
- * was not cancelled.
- */
- public actual val isActive: Boolean
-
- /**
- * Returns `true` when this job has completed for any reason. A job that was cancelled and has
- * finished its execution is also considered complete. Job becomes complete only after
- * all its [children] complete.
- */
- public actual val isCompleted: Boolean
-
- /**
- * Returns `true` if this job was [cancelled][cancel]. In the general case, it does not imply that the
- * job has already [completed][isCompleted] (it may still be cancelling whatever it was doing).
- */
- public actual val isCancelled: Boolean
-
- /**
- * Returns [CancellationException] that signals the completion of this job. This function is
- * used by [cancellable][suspendCancellableCoroutine] suspending functions. They throw exception
- * returned by this function when they suspend in the context of this job and this job becomes _complete_.
- *
- * This function returns the original [cancel] cause of this job if that `cause` was an instance of
- * [CancellationException]. Otherwise (if this job was cancelled with a cause of a different type, or
- * was cancelled without a cause, or had completed normally), an instance of [JobCancellationException] is
- * returned. The [JobCancellationException.cause] of the resulting [JobCancellationException] references
- * the original cancellation cause that was passed to [cancel] function.
- *
- * This function throws [IllegalStateException] when invoked on a job that has not
- * [completed][isCompleted] nor [cancelled][isCancelled] yet.
- */
- public actual fun getCancellationException(): CancellationException
-
- // ------------ state update ------------
-
- /**
- * Starts coroutine related to this job (if any) if it was not started yet.
- * The result `true` if this invocation actually started coroutine or `false`
- * if it was already started or completed.
- */
- public actual fun start(): Boolean
-
- /**
- * Cancels this job with an optional cancellation [cause]. The result is `true` if this job was
- * cancelled as a result of this invocation and `false` otherwise
- * (if it was already _completed_ or if it is [NonCancellable]).
- * Repeated invocations of this function have no effect and always produce `false`.
- *
- * When cancellation has a clear reason in the code, an instance of [CancellationException] should be created
- * at the corresponding original cancellation site and passed into this method to aid in debugging by providing
- * both the context of cancellation and text description of the reason.
- */
- public actual fun cancel(cause: Throwable? = null): Boolean
-
- // ------------ parent-child ------------
-
- /**
- * Returns a sequence of this job's children.
- *
- * A job becomes a child of this job when it is constructed with this job in its
- * [CoroutineContext] or using an explicit `parent` parameter.
- *
- * A parent-child relation has the following effect:
- *
- * * Cancellation of parent with [cancel] or its exceptional completion (failure)
- * immediately cancels all its children.
- * * Parent cannot complete until all its children are complete. Parent waits for all its children to
- * complete in _completing_ or _cancelling_ state.
- * * Uncaught exception in a child, by default, cancels parent. In particular, this applies to
- * children created with [launch] coroutine builder. Note, that [async] and other future-like
- * coroutine builders do not have uncaught exceptions by definition, since all their exceptions are
- * caught and are encapsulated in their result.
- */
- public actual val children: Sequence<Job>
-
- /**
- * Attaches child job so that this job becomes its parent and
- * returns a handle that should be used to detach it.
- *
- * A parent-child relation has the following effect:
- * * Cancellation of parent with [cancel] or its exceptional completion (failure)
- * immediately cancels all its children.
- * * Parent cannot complete until all its children are complete. Parent waits for all its children to
- * complete in _completing_ or _cancelling_ state.
- *
- * **A child must store the resulting [DisposableHandle] and [dispose][DisposableHandle.dispose] the attachment
- * to its parent on its own completion.**
- *
- * Coroutine builders and job factory functions that accept `parent` [CoroutineContext] parameter
- * lookup a [Job] instance in the parent context and use this function to attach themselves as a child.
- * They also store a reference to the resulting [DisposableHandle] and dispose a handle when they complete.
- *
- * @suppress This is an internal API. This method is too error prone for public API.
- */
- @Deprecated(message = "Start child coroutine with 'parent' parameter", level = DeprecationLevel.WARNING)
- public actual fun attachChild(child: Job): DisposableHandle
-
- // ------------ state waiting ------------
-
- /**
- * Suspends coroutine until this job is complete. This invocation resumes normally (without exception)
- * when the job is complete for any reason and the [Job] of the invoking coroutine is still [active][isActive].
- * This function also [starts][Job.start] the corresponding coroutine if the [Job] was still in _new_ state.
- *
- * Note, that the job becomes complete only when all its children are complete.
- *
- * This suspending function is cancellable and **always** checks for the cancellation of invoking coroutine's Job.
- * If the [Job] of the invoking coroutine is cancelled or completed when this
- * suspending function is invoked or while it is suspended, this function
- * throws [CancellationException].
- *
- * In particular, it means that a parent coroutine invoking `join` on a child coroutine that was started using
- * `launch(coroutineContext) { ... }` builder throws [CancellationException] if the child
- * had crashed, unless a non-standard [CoroutineExceptionHandler] if installed in the context.
- *
- * There is [cancelAndJoin] function that combines an invocation of [cancel] and `join`.
- */
- public actual suspend fun join()
-
- // ------------ low-level state-notification ------------
-
- /**
- * Registers handler that is **synchronously** invoked once on cancellation or completion of this job.
- * When job is already cancelling or complete, then the handler is immediately invoked
- * with a job's cancellation cause or `null` unless [invokeImmediately] is set to false.
- * Otherwise, handler will be invoked once when this job is cancelled or complete.
- *
- * Invocation of this handler on a transition to a transient _cancelling_ state
- * is controlled by [onCancelling] boolean parameter.
- * The handler is invoked on invocation of [cancel] when
- * job becomes _cancelling_ if [onCancelling] parameter is set to `true`. However,
- * when this [Job] is not backed by a coroutine, like [CompletableDeferred] or [CancellableContinuation]
- * (both of which do not posses a _cancelling_ state), then the value of [onCancelling] parameter is ignored.
- *
- * The resulting [DisposableHandle] can be used to [dispose][DisposableHandle.dispose] the
- * registration of this handler and release its memory if its invocation is no longer needed.
- * There is no need to dispose the handler after completion of this job. The references to
- * all the handlers are released when this job completes.
- *
- * Installed [handler] should not throw any exceptions. If it does, they will get caught,
- * wrapped into [CompletionHandlerException], and rethrown, potentially causing crash of unrelated code.
- *
- * **Note**: This function is a part of internal machinery that supports parent-child hierarchies
- * and allows for implementation of suspending functions that wait on the Job's state.
- * This function should not be used in general application code.
- * Implementations of `CompletionHandler` must be fast and _lock-free_.
- *
- * @param onCancelling when `true`, then the [handler] is invoked as soon as this job transitions to _cancelling_ state;
- * when `false` then the [handler] is invoked only when it transitions to _completed_ state.
- * @param invokeImmediately when `true` and this job is already in the desired state (depending on [onCancelling]),
- * then the [handler] is immediately and synchronously invoked and [NonDisposableHandle] is returned;
- * when `false` then [NonDisposableHandle] is returned, but the [handler] is not invoked.
- * @param handler the handler.
- */
- public actual fun invokeOnCompletion(
- onCancelling: Boolean = false,
- invokeImmediately: Boolean = true,
- handler: CompletionHandler): DisposableHandle
-
- /**
- * Key for [Job] instance in the coroutine context.
- */
- public actual companion object Key : CoroutineContext.Key<Job>
-}
-
-/**
- * Creates a new job object in an _active_ state.
- * It is optionally a child of a [parent] job.
- */
-@Suppress("FunctionName")
-public actual fun Job(parent: Job? = null): Job = JobImpl(parent)
-
-/**
- * A handle to an allocated object that can be disposed to make it eligible for garbage collection.
- */
-public actual interface DisposableHandle {
- /**
- * Disposes the corresponding object, making it eligible for garbage collection.
- * Repeated invocation of this function has no effect.
- */
- public actual fun dispose()
-}
-
-// -------------------- CoroutineContext extensions --------------------
-
-/**
- * Returns `true` when the [Job] of the coroutine in this context is still active
- * (has not completed and was not cancelled yet).
- *
- * Check this property in long-running computation loops to support cancellation
- * when [CoroutineScope.isActive] is not available:
- *
- * ```
- * while (coroutineContext.isActive) {
- * // do some computation
- * }
- * ```
- *
- * The `coroutineContext.isActive` expression is a shortcut for `coroutineContext[Job]?.isActive == true`.
- * See [Job.isActive].
- */
-public actual val CoroutineContext.isActive: Boolean
- get() = this[Job]?.isActive == true
-
-/**
- * Cancels [Job] of this context with an optional cancellation [cause]. The result is `true` if the job was
- * cancelled as a result of this invocation and `false` if there is no job in the context or if it was already
- * cancelled or completed. See [Job.cancel] for details.
- */
-public actual fun CoroutineContext.cancel(cause: Throwable? = null): Boolean =
- this[Job]?.cancel(cause) ?: false
-
-/**
- * Cancels all children of the [Job] in this context with an optional cancellation [cause].
- * It does not do anything if there is no job in the context or it has no children.
- * See [Job.cancelChildren] for details.
- */
-public actual fun CoroutineContext.cancelChildren(cause: Throwable? = null) {
- this[Job]?.cancelChildren(cause)
-}
-
-// -------------------- Job extensions --------------------
-
-/**
- * Disposes a specified [handle] when this job is complete.
- *
- * This is a shortcut for the following code:
- * ```
- * invokeOnCompletion { handle.dispose() }
- * ```
- */
-public actual fun Job.disposeOnCompletion(handle: DisposableHandle): DisposableHandle =
- invokeOnCompletion { handle.dispose() }
-
-/**
- * Cancels the job and suspends invoking coroutine until the cancelled job is complete.
- *
- * This suspending function is cancellable and **always** checks for the cancellation of invoking coroutine's Job.
- * If the [Job] of the invoking coroutine is cancelled or completed when this
- * suspending function is invoked or while it is suspended, this function
- * throws [CancellationException].
- *
- * In particular, it means that a parent coroutine invoking `cancelAndJoin` on a child coroutine that was started using
- * `launch(coroutineContext) { ... }` builder throws [CancellationException] if the child
- * had crashed, unless a non-standard [CoroutineExceptionHandler] if installed in the context.
- *
- * This is a shortcut for the invocation of [cancel][Job.cancel] followed by [join][Job.join].
- */
-public actual suspend fun Job.cancelAndJoin() {
- cancel()
- return join()
-}
-
-/**
- * Cancels all [children][Job.children] jobs of this coroutine with the given [cause] using [Job.cancel]
- * for all of them. Unlike [Job.cancel] on this job as a whole, the state of this job itself is not affected.
- */
-public actual fun Job.cancelChildren(cause: Throwable? = null) {
- children.forEach { it.cancel(cause) }
-}
-
-/**
- * Suspends coroutine until all [children][Job.children] of this job are complete using
- * [Job.join] for all of them. Unlike [Job.join] on this job as a whole, it does not wait until
- * this job is complete.
- */
-public actual suspend fun Job.joinChildren() {
- children.forEach { it.join() }
-}
-
-/**
- * No-op implementation of [DisposableHandle].
- */
-public actual object NonDisposableHandle : DisposableHandle {
- /** Does not do anything. */
- actual override fun dispose() {}
-
- /** Returns "NonDisposableHandle" string. */
- override fun toString(): String = "NonDisposableHandle"
-}
-
-// --------------- helper classes to simplify job implementation
-
-
-/**
- * A concrete implementation of [Job]. It is optionally a child to a parent job.
- * This job is cancelled when the parent is complete, but not vise-versa.
- *
- * This is an open class designed for extension by more specific classes that might augment the
- * state and mare store addition state information for completed jobs, like their result values.
- *
- * @param active when `true` the job is created in _active_ state, when `false` in _new_ state. See [Job] for details.
- * @suppress **This is unstable API and it is subject to change.**
- */
-internal actual open class JobSupport actual constructor(active: Boolean) : Job {
- public actual override val key: CoroutineContext.Key<*> get() = Job
-
- /**
- * Returns current state of this job.
- * @suppress **This is unstable API and it is subject to change.**
- */
- // Note: use shared objects while we have no listeners
- internal var state: Any? = if (active) EmptyActive else EmptyNew
- private set
-
- private var parentHandle: DisposableHandle? = null
-
- // ------------ initialization ------------
-
- /**
- * Initializes parent job.
- * It shall be invoked at most once after construction after all other initialization.
- * @suppress **This is unstable API and it is subject to change.**
- */
- internal actual fun initParentJobInternal(parent: Job?) {
- check(parentHandle == null) { "Shall be invoked at most once" }
- if (parent == null) {
- parentHandle = NonDisposableHandle
- return
- }
- parent.start() // make sure the parent is started
- @Suppress("DEPRECATION")
- val handle = parent.attachChild(this)
- parentHandle = handle
- // now check our state _after_ registering (see updateState order of actions)
- if (isCompleted) {
- handle.dispose()
- parentHandle = NonDisposableHandle // release it just in case, to aid GC
- }
- }
-
- // ------------ state query ------------
-
- public actual final override val isActive: Boolean get() {
- val state = this.state
- return state is Incomplete && state.isActive
- }
-
- public actual final override val isCompleted: Boolean get() = state !is Incomplete
-
- public actual final override val isCancelled: Boolean get() {
- val state = this.state
- return state is Cancelled || (state is Finishing && state.cancelled != null)
- }
-
- // ------------ state update ------------
-
- /**
- * Updates current [state] of this job.
- * @suppress **This is unstable API and it is subject to change.**
- */
- internal fun updateState(proposedUpdate: Any?, mode: Int) {
- val state = this.state as Incomplete // current state must be incomplete
- val update = coerceProposedUpdate(state, proposedUpdate)
- tryUpdateState(update)
- completeUpdateState(state, update, mode)
- }
-
- internal fun tryUpdateState(update: Any?) {
- require(update !is Incomplete) // only incomplete -> completed transition is allowed
- this.state = update
- // Unregister from parent job
- parentHandle?.let {
- it.dispose()
- parentHandle = NonDisposableHandle // release it just in case, to aid GC
- }
- }
-
- // when Job is in Cancelling state, it can only be promoted to Cancelled state,
- // so if the proposed Update is not an appropriate Cancelled (preserving the cancellation cause),
- // then the corresponding Cancelled state is constructed.
- private fun coerceProposedUpdate(expect: Incomplete, proposedUpdate: Any?): Any? =
- if (expect is Finishing && expect.cancelled != null && !isCorrespondinglyCancelled(expect.cancelled, proposedUpdate))
- createCancelled(expect.cancelled, proposedUpdate) else proposedUpdate
-
- private fun isCorrespondinglyCancelled(cancelled: Cancelled, proposedUpdate: Any?): Boolean {
- if (proposedUpdate !is Cancelled) return false
- // NOTE: equality comparison of causes is performed here by design, see equals of JobCancellationException
- return proposedUpdate.cause == cancelled.cause ||
- proposedUpdate.cause is JobCancellationException && cancelled.cause == null
- }
-
- private fun createCancelled(cancelled: Cancelled, proposedUpdate: Any?): Cancelled {
- if (proposedUpdate !is CompletedExceptionally) return cancelled // not exception -- just use original cancelled
- val exception = proposedUpdate.exception
- if (cancelled.exception == exception) return cancelled // that is the cancelled we need already!
- //cancelled.cause?.let { exception.addSuppressed(it) }
- return Cancelled(this, exception)
- }
-
- /**
- * Completes update of the current [state] of this job.
- * @suppress **This is unstable API and it is subject to change.**
- */
- internal fun completeUpdateState(expect: Incomplete, update: Any?, mode: Int) {
- val exceptionally = update as? CompletedExceptionally
- // Do overridable processing before completion handlers
- if (!expect.isCancelling) onCancellationInternal(exceptionally) // only notify when was not cancelling before
- onCompletionInternal(update, mode)
- // Invoke completion handlers
- val cause = exceptionally?.cause
- if (expect is JobNode<*>) { // SINGLE/SINGLE+ state -- one completion handler (common case)
- try {
- expect.invoke(cause)
- } catch (ex: Throwable) {
- handleException(CompletionHandlerException("Exception in completion handler $expect for $this", ex))
- }
- } else {
- expect.list?.notifyCompletion(cause)
- }
- }
-
- private inline fun <reified T: JobNode<*>> notifyHandlers(list: NodeList, cause: Throwable?) {
- var exception: Throwable? = null
- list.forEach<T> { node ->
- try {
- node.invoke(cause)
- } catch (ex: Throwable) {
- exception?.apply { /* addSuppressed(ex) */ } ?: run {
- exception = CompletionHandlerException("Exception in completion handler $node for $this", ex)
- }
- }
- }
- exception?.let { handleException(it) }
- }
-
- private fun NodeList.notifyCompletion(cause: Throwable?) =
- notifyHandlers<JobNode<*>>(this, cause)
-
- private fun notifyCancellation(list: NodeList, cause: Throwable?) =
- notifyHandlers<JobCancellationNode<*>>(list, cause)
-
- public actual final override fun start(): Boolean {
- val state = this.state
- when (state) {
- is Empty -> { // EMPTY_X state -- no completion handlers
- if (state.isActive) return false // already active
- this.state = EmptyActive
- onStartInternal()
- return true
- }
- is NodeList -> { // LIST -- a list of completion handlers (either new or active)
- return state.makeActive().also { result ->
- if (result) onStartInternal()
- }
- }
- else -> return false // not a new state
- }
- }
-
- /**
- * Override to provide the actual [start] action.
- * @suppress **This is unstable API and it is subject to change.**
- */
- internal actual open fun onStartInternal() {}
-
- public actual final override fun getCancellationException(): CancellationException {
- val state = this.state
- return when {
- state is Finishing && state.cancelled != null ->
- state.cancelled.exception.toCancellationException("Job is being cancelled")
- state is Incomplete ->
- error("Job was not completed or cancelled yet: $this")
- state is CompletedExceptionally ->
- state.exception.toCancellationException("Job has failed")
- else -> JobCancellationException("Job has completed normally", null, this)
- }
- }
-
- private fun Throwable.toCancellationException(message: String): CancellationException =
- this as? CancellationException ?: JobCancellationException(message, this, this@JobSupport)
-
- /**
- * Returns the cause that signals the completion of this job -- it returns the original
- * [cancel] cause or **`null` if this job had completed
- * normally or was cancelled without a cause**. This function throws
- * [IllegalStateException] when invoked for an job that has not [completed][isCompleted] nor
- * [isCancelled] yet.
- */
- protected fun getCompletionCause(): Throwable? {
- val state = this.state
- return when {
- state is Finishing && state.cancelled != null -> state.cancelled.cause
- state is Incomplete -> error("Job was not completed or cancelled yet")
- state is CompletedExceptionally -> state.cause
- else -> null
- }
- }
-
- // todo: non-final as a workaround for KT-21968, should be final in the future
- public actual override fun invokeOnCompletion(onCancelling: Boolean, invokeImmediately: Boolean, handler: CompletionHandler) =
- installNode(onCancelling, invokeImmediately, makeNode(handler, onCancelling))
-
- private fun installNode(
- onCancelling: Boolean,
- invokeImmediately: Boolean,
- node: JobNode<*>
- ): DisposableHandle {
- while (true) {
- val state = this.state
- when (state) {
- is Empty -> { // EMPTY_X state -- no completion handlers
- if (state.isActive) {
- // move to SINGLE state
- this.state = node
- return node
- } else
- promoteEmptyToNodeList(state) // that way we can add listener for non-active coroutine
- }
- is Incomplete -> {
- val list = state.list
- if (list == null) { // SINGLE/SINGLE+
- promoteSingleToNodeList(state as JobNode<*>)
- } else {
- if (state is Finishing && state.cancelled != null && onCancelling) {
- // cannot be in this state unless were support cancelling state
- check(onCancelMode != ON_CANCEL_MAKE_CANCELLED) // cannot be in this state unless were support cancelling state
- // installing cancellation handler on job that is being cancelled
- if (invokeImmediately) node.invoke(state.cancelled.cause)
- return NonDisposableHandle
- }
- list.addLast(node)
- return node
- }
- }
- else -> { // is complete
- if (invokeImmediately) node.invoke((state as? CompletedExceptionally)?.cause)
- return NonDisposableHandle
- }
- }
- }
- }
-
- private fun makeNode(handler: CompletionHandler, onCancelling: Boolean): JobNode<*> {
- val hasCancellingState = onCancelMode != ON_CANCEL_MAKE_CANCELLED
- return if (onCancelling && hasCancellingState)
- InvokeOnCancellation(this, handler)
- else
- InvokeOnCompletion(this, handler)
- }
-
-
- private fun promoteEmptyToNodeList(state: Empty) {
- check(state === this.state) { "Expected empty state"}
- // promote it to list in new state
- this.state = NodeList(state.isActive)
- }
-
- private fun promoteSingleToNodeList(state: JobNode<*>) {
- check(state === this.state) { "Expected single state" }
- // promote it to list (SINGLE+ state)
- val list = NodeList(isActive = true)
- list.addLast(state)
- this.state = list
- }
-
- public actual final override suspend fun join() {
- if (!joinInternal()) { // fast-path no wait
- return suspendCoroutineOrReturn { cont ->
- cont.context.checkCompletion()
- Unit // do not suspend
- }
- }
- return joinSuspend() // slow-path wait
- }
-
- private fun joinInternal(): Boolean {
- if (state !is Incomplete) return false // not active anymore (complete) -- no need to wait
- start()
- return true // wait
- }
-
- private suspend fun joinSuspend() = suspendCancellableCoroutine<Unit> { cont ->
- val handle = invokeOnCompletion { cont.resume(Unit) }
- cont.invokeOnCompletion { handle.dispose() }
- }
-
- internal fun removeNode(node: JobNode<*>) {
- // remove logic depends on the state of the job
- val state = this.state
- when (state) {
- is JobNode<*> -> { // SINGE/SINGLE+ state -- one completion handler
- if (state !== node) return // a different job node --> we were already removed
- // remove and revert back to empty state
- this.state = EmptyActive
- }
- is Incomplete -> { // may have a list of completion handlers
- // remove node from the list if there is a list
- if (state.list != null) node.remove()
- }
- }
- }
-
- protected open val onCancelMode: Int get() = ON_CANCEL_MAKE_CANCELLING
-
- public actual override fun cancel(cause: Throwable?): Boolean = when (onCancelMode) {
- ON_CANCEL_MAKE_CANCELLED -> makeCancelled(cause)
- ON_CANCEL_MAKE_CANCELLING -> makeCancelling(cause)
- ON_CANCEL_MAKE_COMPLETING -> makeCompletingOnCancel(cause)
- else -> error("Invalid onCancelMode $onCancelMode")
- }
-
- // we will be dispatching coroutine to process its cancellation exception, so there is no need for
- // an extra check for Job status in MODE_CANCELLABLE
- private fun updateStateCancelled(cause: Throwable?) =
- updateState(Cancelled(this, cause), mode = MODE_ATOMIC_DEFAULT)
-
- // transitions to Cancelled state
- private fun makeCancelled(cause: Throwable?): Boolean {
- if (state !is Incomplete) return false // quit if already complete
- updateStateCancelled(cause)
- return true
- }
-
- // transitions to Cancelling state
- private fun makeCancelling(cause: Throwable?): Boolean {
- while (true) {
- val state = this.state
- when (state) {
- is Empty -> { // EMPTY_X state -- no completion handlers
- if (state.isActive) {
- promoteEmptyToNodeList(state) // this way can wrap it into Cancelling on next pass
- } else {
- // cancelling a non-started coroutine makes it immediately cancelled
- // (and we have no listeners to notify which makes it very simple)
- updateStateCancelled(cause)
- return true
- }
- }
- is JobNode<*> -> { // SINGLE/SINGLE+ state -- one completion handler
- promoteSingleToNodeList(state)
- }
- is NodeList -> { // LIST -- a list of completion handlers (either new or active)
- if (state.isActive) {
- makeCancellingList(state.list, cause)
- return true
- } else {
- // cancelling a non-started coroutine makes it immediately cancelled
- updateStateCancelled(cause)
- return true
- }
- }
- is Finishing -> { // Completing/Cancelling the job, may cancel
- if (state.cancelled != null) return false // already cancelling
- makeCancellingList(state.list, cause)
- return true
- }
- else -> { // is inactive
- return false
- }
- }
- }
- }
-
- // make expected state in cancelling
- private fun makeCancellingList(list: NodeList, cause: Throwable?) {
- val cancelled = Cancelled(this, cause)
- state = Finishing(list, cancelled, false)
- onFinishingInternal(cancelled)
- onCancellationInternal(cancelled)
- notifyCancellation(list, cause)
- }
-
- private fun makeCompletingOnCancel(cause: Throwable?): Boolean =
- makeCompleting(Cancelled(this, cause))
-
- internal fun makeCompleting(proposedUpdate: Any?): Boolean =
- when (makeCompletingInternal(proposedUpdate, mode = MODE_ATOMIC_DEFAULT)) {
- COMPLETING_ALREADY_COMPLETING -> false
- else -> true
- }
-
- /**
- * Returns:
- * * `true` if state was updated to completed/cancelled;
- * * `false` if made completing or it is cancelling and is waiting for children.
- *
- * @throws IllegalStateException if job is already complete or completing
- * @suppress **This is unstable API and it is subject to change.**
- */
- internal actual fun makeCompletingOnce(proposedUpdate: Any?, mode: Int): Boolean =
- when (makeCompletingInternal(proposedUpdate, mode)) {
- COMPLETING_COMPLETED -> true
- COMPLETING_WAITING_CHILDREN -> false
- else -> throw IllegalStateException("Job $this is already complete or completing, " +
- "but is being completed with $proposedUpdate", proposedUpdate.exceptionOrNull)
- }
-
- private fun makeCompletingInternal(proposedUpdate: Any?, mode: Int): Int {
- loop@ while (true) {
- val state = this.state
- @Suppress("FoldInitializerAndIfToElvis")
- if (state !is Incomplete)
- return COMPLETING_ALREADY_COMPLETING
- if (state is Finishing && state.completing)
- return COMPLETING_ALREADY_COMPLETING
- val child: Child? = firstChild(state) ?: // or else complete immediately w/o children
- when {
- state !is Finishing && hasOnFinishingHandler(proposedUpdate) -> null // unless it has onCompleting handler
- else -> {
- updateState(proposedUpdate, mode)
- return COMPLETING_COMPLETED
- }
- }
- val list = state.list ?: // must promote to list to correctly operate on child lists
- when (state) {
- is Empty -> {
- promoteEmptyToNodeList(state)
- continue@loop // retry
- }
- is JobNode<*> -> {
- promoteSingleToNodeList(state)
- continue@loop // retry
- }
- else -> error("Unexpected state with an empty list: $state")
- }
- // cancel all children in list on exceptional completion
- if (proposedUpdate is CompletedExceptionally)
- child?.cancelChildrenInternal(proposedUpdate.exception)
- // switch to completing state
- val completing = Finishing(list, (state as? Finishing)?.cancelled, true)
- this.state = completing
- if (state !is Finishing) onFinishingInternal(proposedUpdate)
- if (child != null && tryWaitForChild(child, proposedUpdate))
- return COMPLETING_WAITING_CHILDREN
- updateState(proposedUpdate, mode = MODE_ATOMIC_DEFAULT)
- return COMPLETING_COMPLETED
- }
- }
-
- private tailrec fun Child.cancelChildrenInternal(cause: Throwable) {
- childJob.cancel(JobCancellationException("Child job was cancelled because of parent failure", cause, childJob))
- nextChild()?.cancelChildrenInternal(cause)
- }
-
- private val Any?.exceptionOrNull: Throwable?
- get() = (this as? CompletedExceptionally)?.exception
-
- private fun firstChild(state: Incomplete) =
- state as? Child ?: state.list?.nextChild()
-
- // return false when there is no more incomplete children to wait
- private tailrec fun tryWaitForChild(child: Child, proposedUpdate: Any?): Boolean {
- val handle = child.childJob.invokeOnCompletion(invokeImmediately = false) {
- continueCompleting(child, proposedUpdate)
- }
- if (handle !== NonDisposableHandle) return true // child is not complete and we've started waiting for it
- val nextChild = child.nextChild() ?: return false
- return tryWaitForChild(nextChild, proposedUpdate)
- }
-
- internal fun continueCompleting(lastChild: Child, proposedUpdate: Any?) {
- val state = this.state
- @Suppress("FoldInitializerAndIfToElvis")
- if (state !is Finishing)
- throw IllegalStateException("Job $this is found in expected state while completing with $proposedUpdate", proposedUpdate.exceptionOrNull)
- // figure out if we need to wait for next child
- val waitChild = lastChild.nextChild()
- // try wait for next child
- if (waitChild != null && tryWaitForChild(waitChild, proposedUpdate)) return // waiting for next child
- // no more children to wait -- update state
- updateState(proposedUpdate, mode = MODE_ATOMIC_DEFAULT)
- }
-
- private fun LinkedListNode.nextChild(): Child? {
- var cur = this
- while (cur.isRemoved) cur = cur.prev // rollback to prev non-removed (or list head)
- while (true) {
- cur = cur.next
- if (cur is Child) return cur
- if (cur is NodeList) return null // checked all -- no more children
- }
- }
-
- public actual final override val children: Sequence<Job> get() = buildSequence<Job> {
- val state = this@JobSupport.state
- when (state) {
- is Child -> yield(state.childJob)
- is Incomplete -> state.list?.let { list ->
- list.forEach<Child> { yield(it.childJob) }
- }
- }
- }
-
- @Suppress("OverridingDeprecatedMember")
- public actual override fun attachChild(child: Job): DisposableHandle =
- installNode(onCancelling = true, invokeImmediately = true, node = Child(this, child))
-
- /**
- * Override to process any exceptions that were encountered while invoking completion handlers
- * installed via [invokeOnCompletion].
- * @suppress **This is unstable API and it is subject to change.**
- */
- internal actual open fun handleException(exception: Throwable) {
- throw exception
- }
-
- /**
- * It is invoked once when job is cancelled or is completed, similarly to [invokeOnCompletion] with
- * `onCancelling` set to `true`.
- * @param exceptionally not null when the the job was cancelled or completed exceptionally,
- * null when it has completed normally.
- * @suppress **This is unstable API and it is subject to change.**
- */
- internal actual open fun onCancellationInternal(exceptionally: CompletedExceptionally?) {}
-
- /**
- * @suppress **This is unstable API and it is subject to change.**
- */
- internal actual open fun hasOnFinishingHandler(update: Any?) = false
-
- /**
- * @suppress **This is unstable API and it is subject to change.**
- */
- internal actual open fun onFinishingInternal(update: Any?) {}
-
- /**
- * Override for completion actions that need to do something with the state.
- * @param mode completion mode.
- * @suppress **This is unstable API and it is subject to change.**
- */
- internal actual open fun onCompletionInternal(state: Any?, mode: Int) {}
-
- // for nicer debugging
- public override fun toString(): String =
- "${nameString()}{${stateString()}}"
-
- /**
- * @suppress **This is unstable API and it is subject to change.**
- */
- internal actual open fun nameString(): String = classSimpleName
-
- private fun stateString(): String {
- val state = this.state
- return when (state) {
- is Finishing -> buildString {
- if (state.cancelled != null) append("Cancelling")
- if (state.completing) append("Completing")
- }
- is Incomplete -> if (state.isActive) "Active" else "New"
- is Cancelled -> "Cancelled"
- is CompletedExceptionally -> "CompletedExceptionally"
- else -> "Completed"
- }
- }
-
- /**
- * @suppress **This is unstable API and it is subject to change.**
- */
- internal interface Incomplete {
- val isActive: Boolean
- val list: NodeList? // is null only for Empty and JobNode incomplete state objects
- }
-
- // Cancelling or Completing
- private class Finishing(
- override val list: NodeList,
- val cancelled: Cancelled?, /* != null when cancelling */
- val completing: Boolean /* true when completing */
- ) : Incomplete {
- override val isActive: Boolean get() = cancelled == null
- }
-
- private val Incomplete.isCancelling: Boolean
- get() = this is Finishing && cancelled != null
-
- /**
- * @suppress **This is unstable API and it is subject to change.**
- */
- internal class NodeList(
- override var isActive: Boolean
- ) : LinkedListHead(), Incomplete {
- override val list: NodeList get() = this
-
- fun makeActive(): Boolean {
- if (isActive) return false
- isActive = true
- return true
- }
-
- override fun toString(): String = buildString {
- append("List")
- append(if (isActive) "{Active}" else "{New}")
- append("[")
- var first = true
- this@NodeList.forEach<JobNode<*>> { node ->
- if (first) first = false else append(", ")
- append(node)
- }
- append("]")
- }
- }
-
- /*
- * =================================================================================================
- * This is ready-to-use implementation for Deferred interface.
- * However, it is not type-safe. Conceptually it just exposes the value of the underlying
- * completed state as `Any?`
- * =================================================================================================
- */
-
- public actual val isCompletedExceptionally: Boolean get() = state is CompletedExceptionally
-
- public actual fun getCompletionExceptionOrNull(): Throwable? {
- val state = this.state
- check(state !is Incomplete) { "This job has not completed yet" }
- return state.exceptionOrNull
- }
-
- /**
- * @suppress **This is unstable API and it is subject to change.**
- */
- internal fun getCompletedInternal(): Any? {
- val state = this.state
- check(state !is Incomplete) { "This job has not completed yet" }
- if (state is CompletedExceptionally) throw state.exception
- return state
- }
-
- /**
- * @suppress **This is unstable API and it is subject to change.**
- */
- internal suspend fun awaitInternal(): Any? {
- val state = this.state
- if (state !is Incomplete) {
- // already complete -- just return result
- if (state is CompletedExceptionally) throw state.exception
- return state
- }
- start()
- return awaitSuspend() // slow-path
- }
-
- private suspend fun awaitSuspend(): Any? = suspendCancellableCoroutine { cont ->
- val handle = invokeOnCompletion {
- val state = this.state
- check(state !is Incomplete) { "State should be complete "}
- if (state is CompletedExceptionally)
- cont.resumeWithException(state.exception)
- else
- cont.resume(state)
- }
- cont.invokeOnCompletion { handle.dispose() }
- }
-}
-
-internal const val ON_CANCEL_MAKE_CANCELLED = 0
-internal const val ON_CANCEL_MAKE_CANCELLING = 1
-internal const val ON_CANCEL_MAKE_COMPLETING = 2
-
-private const val COMPLETING_ALREADY_COMPLETING = 0
-private const val COMPLETING_COMPLETED = 1
-private const val COMPLETING_WAITING_CHILDREN = 2
-
-@Suppress("PrivatePropertyName")
-private val EmptyNew = Empty(false)
-@Suppress("PrivatePropertyName")
-private val EmptyActive = Empty(true)
-
-private class Empty(override val isActive: Boolean) : JobSupport.Incomplete {
- override val list: JobSupport.NodeList? get() = null
- override fun toString(): String = "Empty{${if (isActive) "Active" else "New" }}"
-}
-
-private class JobImpl(parent: Job? = null) : JobSupport(true) {
- init { initParentJobInternal(parent) }
- override val onCancelMode: Int get() = ON_CANCEL_MAKE_COMPLETING
-}
-
-// -------- invokeOnCompletion nodes
-
-internal abstract class JobNode<out J : Job>(
- val job: J
-) : LinkedListNode(), DisposableHandle, JobSupport.Incomplete {
- final override val isActive: Boolean get() = true
- final override val list: JobSupport.NodeList? get() = null
- final override fun dispose() = (job as JobSupport).removeNode(this)
- abstract fun invoke(reason: Throwable?) // CompletionHandler -- invoked on completion
-}
-
-private class InvokeOnCompletion(
- job: Job,
- private val handler: CompletionHandler
-) : JobNode<Job>(job) {
- override fun invoke(reason: Throwable?) = handler.invoke(reason)
- override fun toString() = "InvokeOnCompletion"
-}
-
-// -------- invokeOnCancellation nodes
-
-/**
- * Marker for node that shall be invoked on cancellation (in _cancelling_ state).
- * **Note: may be invoked multiple times during cancellation.**
- */
-internal abstract class JobCancellationNode<out J : Job>(job: J) : JobNode<J>(job)
-
-private class InvokeOnCancellation(
- job: Job,
- private val handler: CompletionHandler
-) : JobCancellationNode<Job>(job) {
- // delegate handler shall be invoked at most once, so here is an additional flag
- private var invoked = false
- override fun invoke(reason: Throwable?) {
- if (invoked) return
- invoked = true
- handler.invoke(reason)
- }
- override fun toString() = "InvokeOnCancellation"
-}
-
-internal class Child(
- parent: JobSupport,
- val childJob: Job
-) : JobCancellationNode<JobSupport>(parent) {
- override fun invoke(reason: Throwable?) {
- // Always materialize the actual instance of parent's completion exception and cancel child with it
- childJob.cancel(job.getCancellationException())
- }
- override fun toString(): String = "Child[$childJob]"
-}
-
diff --git a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/NonCancellable.kt b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/NonCancellable.kt
deleted file mode 100644
index 09a8789..0000000
--- a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/NonCancellable.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2016-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package kotlinx.coroutines.experimental
-
-import kotlinx.coroutines.experimental.NonCancellable.isActive
-import kotlin.coroutines.experimental.AbstractCoroutineContextElement
-
-/**
- * A non-cancelable job that is always [active][isActive]. It is designed to be used with [run] builder
- * to prevent cancellation of code blocks that need to run without cancellation.
- *
- * Use it like this:
- * ```
- * run(NonCancellable) {
- * // this code will not be cancelled
- * }
- * ```
- */
-public actual object NonCancellable : AbstractCoroutineContextElement(Job), Job {
- /** Always returns `true`. */
- actual override val isActive: Boolean get() = true
-
- /** Always returns `false`. */
- actual override val isCompleted: Boolean get() = false
-
- /** Always returns `false`. */
- actual override val isCancelled: Boolean get() = false
-
- /** Always returns `false`. */
- actual override fun start(): Boolean = false
-
- /** Always throws [UnsupportedOperationException]. */
- actual suspend override fun join() {
- throw UnsupportedOperationException("This job is always active")
- }
-
- /** Always throws [IllegalStateException]. */
- actual override fun getCancellationException(): CancellationException = throw IllegalStateException("This job is always active")
-
- /** Always returns [NonDisposableHandle]. */
- actual override fun invokeOnCompletion(onCancelling: Boolean, invokeImmediately: Boolean, handler: CompletionHandler): DisposableHandle =
- NonDisposableHandle
-
- /** Always returns `false`. */
- actual override fun cancel(cause: Throwable?): Boolean = false
-
- /** Always returns [emptySequence]. */
- actual override val children: Sequence<Job>
- get() = emptySequence()
-
- /** Always returns [NonDisposableHandle] and does not do anything. */
- @Suppress("OverridingDeprecatedMember")
- actual override fun attachChild(child: Job): DisposableHandle = NonDisposableHandle
-}
diff --git a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/JobTest.kt b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Runnable.kt
similarity index 64%
copy from core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/JobTest.kt
copy to js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Runnable.kt
index 06f7c68..e9e1b7d 100644
--- a/core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/JobTest.kt
+++ b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Runnable.kt
@@ -16,14 +16,20 @@
package kotlinx.coroutines.experimental
-import kotlin.test.*
+/**
+ * A runnable task for [CoroutineDispatcher.dispatch].
+ */
+public actual interface Runnable {
+ public actual fun run()
+}
-class JobTest : TestBase() {
- @Test
- fun testMemoryRelease() {
- val job = Job()
- val n = 10_000_000 * stressTestMultiplier
- var fireCount = 0
- for (i in 0 until n) job.invokeOnCompletion { fireCount++ }.dispose()
+/**
+ * Creates [Runnable] task instance.
+ */
+@Suppress("FunctionName")
+public actual inline fun Runnable(crossinline block: () -> Unit): Runnable =
+ object : Runnable {
+ override fun run() {
+ block()
+ }
}
-}
\ No newline at end of file
diff --git a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Scheduled.kt b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Scheduled.kt
deleted file mode 100644
index a884a52..0000000
--- a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Scheduled.kt
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright 2016-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package kotlinx.coroutines.experimental
-
-import kotlinx.coroutines.experimental.intrinsics.*
-import kotlin.coroutines.experimental.*
-import kotlin.coroutines.experimental.intrinsics.*
-
-/**
- * Runs a given suspending [block] of code inside a coroutine with a specified timeout and throws
- * [TimeoutCancellationException] if timeout was exceeded.
- *
- * The code that is executing inside the [block] is cancelled on timeout and the active or next invocation of
- * cancellable suspending function inside the block throws [TimeoutCancellationException].
- * Even if the code in the block suppresses [TimeoutCancellationException], it
- * is still thrown by `withTimeout` invocation.
- *
- * The sibling function that does not throw exception on timeout is [withTimeoutOrNull].
- *
- * This function delegates to [Delay.invokeOnTimeout] if the context [CoroutineDispatcher]
- * implements [Delay] interface, otherwise it tracks time using a built-in single-threaded scheduled executor service.
- *
- * @param time timeout time in milliseconds.
- */
-public actual suspend fun <T> withTimeout(time: Int, block: suspend CoroutineScope.() -> T): T {
- require(time >= 0) { "Timeout time $time cannot be negative" }
- if (time <= 0L) throw CancellationException("Timed out immediately")
- return suspendCoroutineOrReturn { cont: Continuation<T> ->
- setupTimeout(TimeoutCoroutine(time, cont), block)
- }
-}
-
-private fun <U, T: U> setupTimeout(
- coroutine: TimeoutCoroutine<U, T>,
- block: suspend CoroutineScope.() -> T
-): Any? {
- // schedule cancellation of this coroutine on time
- val cont = coroutine.cont
- val context = cont.context
- coroutine.disposeOnCompletion(context.delay.invokeOnTimeout(coroutine.time, coroutine))
- // restart block using new coroutine with new job,
- // however start it as undispatched coroutine, because we are already in the proper context
- return coroutine.startUndispatchedOrReturn(coroutine, block)
-}
-
-private open class TimeoutCoroutine<U, in T: U>(
- val time: Int,
- val cont: Continuation<U>
-) : AbstractCoroutine<T>(cont.context, active = true), Runnable, Continuation<T> {
- override val defaultResumeMode: Int get() = MODE_DIRECT
-
- @Suppress("LeakingThis")
- override fun run() {
- cancel(TimeoutCancellationException(time, this))
- }
-
- @Suppress("UNCHECKED_CAST")
- internal override fun onCompletionInternal(state: Any?, mode: Int) {
- if (state is CompletedExceptionally)
- cont.resumeWithExceptionMode(state.exception, mode)
- else
- cont.resumeMode(state as T, mode)
- }
-
- override fun toString(): String =
- "TimeoutCoroutine($time)"
-}
-
-/**
- * Runs a given suspending block of code inside a coroutine with a specified timeout and returns
- * `null` if this timeout was exceeded.
- *
- * The code that is executing inside the [block] is cancelled on timeout and the active or next invocation of
- * cancellable suspending function inside the block throws [TimeoutCancellationException].
- * Even if the code in the block suppresses [TimeoutCancellationException], this
- * invocation of `withTimeoutOrNull` still returns `null`.
- *
- * The sibling function that throws exception on timeout is [withTimeout].
- *
- * This function delegates to [Delay.invokeOnTimeout] if the context [CoroutineDispatcher]
- * implements [Delay] interface, otherwise it tracks time using a built-in single-threaded scheduled executor service.
- *
- * @param time timeout time in milliseconds.
- */
-public actual suspend fun <T> withTimeoutOrNull(time: Int, block: suspend CoroutineScope.() -> T): T? {
- require(time >= 0) { "Timeout time $time cannot be negative" }
- if (time <= 0L) return null
- return suspendCoroutineOrReturn { cont: Continuation<T?> ->
- setupTimeout(TimeoutOrNullCoroutine(time, cont), block)
- }
-}
-
-private class TimeoutOrNullCoroutine<T>(
- time: Int,
- cont: Continuation<T?>
-) : TimeoutCoroutine<T?, T>(time, cont) {
- @Suppress("UNCHECKED_CAST")
- internal override fun onCompletionInternal(state: Any?, mode: Int) {
- if (state is CompletedExceptionally) {
- val exception = state.exception
- if (exception is TimeoutCancellationException && exception.coroutine === this)
- cont.resumeMode(null, mode) else
- cont.resumeWithExceptionMode(exception, mode)
- } else
- cont.resumeMode(state as T, mode)
- }
-
- override fun toString(): String =
- "TimeoutOrNullCoroutine($time)"
-}
-
diff --git a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Yield.kt b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Yield.kt
deleted file mode 100644
index 833a728..0000000
--- a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/Yield.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2016-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package kotlinx.coroutines.experimental
-
-import kotlin.coroutines.experimental.CoroutineContext
-import kotlin.coroutines.experimental.intrinsics.COROUTINE_SUSPENDED
-import kotlin.coroutines.experimental.intrinsics.suspendCoroutineOrReturn
-
-/**
- * Yields a thread (or thread pool) of the current coroutine dispatcher to other coroutines to run.
- * If the coroutine dispatcher does not have its own thread pool (like [Unconfined] dispatcher) then this
- * function does nothing, but checks if the coroutine [Job] was completed.
- * This suspending function is cancellable.
- * If the [Job] of the current coroutine is cancelled or completed when this suspending function is invoked or while
- * this function is waiting for dispatching, it resumes with [CancellationException].
- */
-public actual suspend fun yield(): Unit = suspendCoroutineOrReturn sc@ { cont ->
- val context = cont.context
- context.checkCompletion()
- if (cont !is DispatchedContinuation<Unit>) return@sc Unit
- if (!cont.dispatcher.isDispatchNeeded(context)) return@sc Unit
- cont.dispatchYield(Unit)
- COROUTINE_SUSPENDED
-}
-
-internal fun CoroutineContext.checkCompletion() {
- val job = get(Job)
- if (job != null && !job.isActive) throw job.getCancellationException()
-}
diff --git a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/internal/LinkedList.kt b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/internal/LinkedList.kt
index 54aa2b3..453bf84 100644
--- a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/internal/LinkedList.kt
+++ b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/internal/LinkedList.kt
@@ -18,62 +18,121 @@
private typealias Node = LinkedListNode
-/**
- * @suppress **This is unstable API and it is subject to change.**
- */
+/** @suppress **This is unstable API and it is subject to change.** */
+@Suppress("NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS") // :TODO: Remove when fixed: https://youtrack.jetbrains.com/issue/KT-23703
+public actual typealias LockFreeLinkedListNode = LinkedListNode
+
+/** @suppress **This is unstable API and it is subject to change.** */
+public actual typealias LockFreeLinkedListHead = LinkedListHead
+
+/** @suppress **This is unstable API and it is subject to change.** */
public open class LinkedListNode {
- public var next = this
- private set
- public var prev = this
- private set
- public var isRemoved: Boolean = false
- private set
- public val isFresh: Boolean = next === this
+ @PublishedApi internal var _next = this
+ @PublishedApi internal var _prev = this
+ @PublishedApi internal var _removed: Boolean = false
+
+ public inline val nextNode get() = _next
+ public inline val prevNode get() = _prev
+ public inline val isRemoved get() = _removed
public fun addLast(node: Node) {
- val prev = this.prev
- node.next = this
- node.prev = prev
- prev.next = node
- this.prev = node
+ val prev = this._prev
+ node._next = this
+ node._prev = prev
+ prev._next = node
+ this._prev = node
}
public open fun remove(): Boolean {
- if (isRemoved) return false
- val prev = this.prev
- val next = this.next
- prev.next = next
- next.prev = prev
- isRemoved = true
+ if (_removed) return false
+ val prev = this._prev
+ val next = this._next
+ prev._next = next
+ next._prev = prev
+ _removed = true
return true
}
+
+ public fun addOneIfEmpty(node: Node): Boolean {
+ if (_next !== this) return false
+ addLast(node)
+ return true
+ }
+
+ public inline fun addLastIf(node: Node, crossinline condition: () -> Boolean): Boolean {
+ if (!condition()) return false
+ addLast(node)
+ return true
+ }
+
+ public inline fun addLastIfPrev(node: Node, predicate: (Node) -> Boolean): Boolean {
+ if (!predicate(_prev)) return false
+ addLast(node)
+ return true
+ }
+
+ public inline fun addLastIfPrevAndIf(
+ node: Node,
+ predicate: (Node) -> Boolean, // prev node predicate
+ crossinline condition: () -> Boolean // atomically checked condition
+ ): Boolean {
+ if (!predicate(_prev)) return false
+ if (!condition()) return false
+ addLast(node)
+ return true
+ }
+
+ public fun removeFirstOrNull(): Node? {
+ val next = _next
+ if (next === this) return null
+ check(next.remove()) { "Should remove" }
+ return next
+ }
+
+ public inline fun <reified T> removeFirstIfIsInstanceOfOrPeekIf(predicate: (T) -> Boolean): T? {
+ val next = _next
+ if (next === this) return null
+ if (next !is T) return null
+ if (predicate(next)) return next
+ check(next.remove()) { "Should remove" }
+ return next
+ }
}
-/**
- * @suppress **This is unstable API and it is subject to change.**
- */
+/** @suppress **This is unstable API and it is subject to change.** */
+public actual open class AddLastDesc<T : Node> actual constructor(
+ actual val queue: Node,
+ actual val node: T
+) : AbstractAtomicDesc() {
+ protected override val affectedNode: Node get() = queue._prev
+ protected actual override fun onPrepare(affected: Node, next: Node): Any? = null
+ protected override fun onComplete() = queue.addLast(node)
+}
+
+/** @suppress **This is unstable API and it is subject to change.** */
+public actual abstract class AbstractAtomicDesc : AtomicDesc() {
+ protected abstract val affectedNode: Node
+ protected actual abstract fun onPrepare(affected: Node, next: Node): Any?
+ protected abstract fun onComplete()
+ actual final override fun prepare(op: AtomicOp<*>): Any? = onPrepare(affectedNode, affectedNode._next)
+ actual final override fun complete(op: AtomicOp<*>, failure: Any?) = onComplete()
+}
+
+/** @suppress **This is unstable API and it is subject to change.** */
public open class LinkedListHead : LinkedListNode() {
- public val isEmpty get() = next === this
+ public val isEmpty get() = _next === this
/**
* Iterates over all elements in this list of a specified type.
*/
public inline fun <reified T : Node> forEach(block: (T) -> Unit) {
- var cur: Node = next
+ var cur: Node = _next
while (cur != this) {
if (cur is T) block(cur)
- cur = cur.next
+ cur = cur._next
}
}
// just a defensive programming -- makes sure that list head sentinel is never removed
public final override fun remove() = throw UnsupportedOperationException()
-
- fun removeFirstOrNull(): Node? {
- val node = next
- if (node === this) return null
- node.remove()
- return node
- }
}
-
diff --git a/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/timeunit/TimeUnit.kt b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/timeunit/TimeUnit.kt
new file mode 100644
index 0000000..5bf8eae
--- /dev/null
+++ b/js/kotlinx-coroutines-core-js/src/main/kotlin/kotlinx/coroutines/experimental/timeunit/TimeUnit.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2016-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package kotlinx.coroutines.experimental.timeunit
+
+/**
+ * Time unit. This class is provided for better JVM interoperability.
+ * **It is available for common code, but its use in common code is not recommended.**
+ */
+@Deprecated("Using this TimeUnit enum in JS code is not recommended, use functions without it")
+public actual enum class TimeUnit {
+ /** Milliseconds. */
+ MILLISECONDS,
+ /** Seconds. */
+ SECONDS;
+
+ /**
+ * Converts time in this time unit to milliseconds.
+ */
+ public actual fun toMillis(time: Long): Long = when(this) {
+ MILLISECONDS -> time
+ SECONDS -> when {
+ time >= Long.MAX_VALUE / 1000L -> Long.MAX_VALUE
+ time <= Long.MIN_VALUE / 1000L -> Long.MIN_VALUE
+ else -> time * 1000L
+ }
+ }
+}
\ No newline at end of file
diff --git a/js/kotlinx-coroutines-core-js/src/test/kotlin/kotlinx/coroutines/experimental/TestBase.kt b/js/kotlinx-coroutines-core-js/src/test/kotlin/kotlinx/coroutines/experimental/TestBase.kt
index fccde08..7a3da04 100644
--- a/js/kotlinx-coroutines-core-js/src/test/kotlin/kotlinx/coroutines/experimental/TestBase.kt
+++ b/js/kotlinx-coroutines-core-js/src/test/kotlin/kotlinx/coroutines/experimental/TestBase.kt
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-// :todo: Remove after transition to Kotlin 1.2.30+
-@file:Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
-
package kotlinx.coroutines.experimental
import kotlin.js.*
@@ -34,6 +31,7 @@
* complete successfully even if this exception is consumed somewhere in the test.
*/
public actual fun error(message: Any, cause: Throwable? = null): Nothing {
+ if (cause != null) console.log(cause)
val exception = IllegalStateException(
if (cause == null) message.toString() else "$message; caused by $cause")
if (error == null) error = exception