CoroutineStart enum is introduced in launch/async/actor
diff --git a/coroutines-guide.md b/coroutines-guide.md
index abc4acf..9c136f4 100644
--- a/coroutines-guide.md
+++ b/coroutines-guide.md
@@ -652,7 +652,7 @@
### Lazily started async
-There is a laziness option to [async] with `start = false` parameter.
+There is a laziness option to [async] with [CoroutineStart.LAZY] parameter.
It starts coroutine only when its result is needed by some
[await][Deferred.await] or if a [start][Job.start] function
is invoked. Run the following example that differs from the previous one only by this option:
@@ -660,8 +660,8 @@
```kotlin
fun main(args: Array<String>) = runBlocking<Unit> {
val time = measureTimeMillis {
- val one = async(CommonPool, start = false) { doSomethingUsefulOne() }
- val two = async(CommonPool, start = false) { doSomethingUsefulTwo() }
+ val one = async(CommonPool, CoroutineStart.LAZY) { doSomethingUsefulOne() }
+ val two = async(CommonPool, CoroutineStart.LAZY) { doSomethingUsefulTwo() }
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
@@ -2174,6 +2174,7 @@
[withTimeout]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/with-timeout.html
[async]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/async.html
[Deferred]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-deferred/index.html
+[CoroutineStart.LAZY]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-coroutine-start/-l-a-z-y.html
[Deferred.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-deferred/await.html
[Job.start]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-job/start.html
[CommonPool]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-common-pool/index.html
diff --git a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Builders.kt b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Builders.kt
index 833824e..cc392bd 100644
--- a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Builders.kt
+++ b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Builders.kt
@@ -16,6 +16,7 @@
package kotlinx.coroutines.experimental
+import kotlinx.coroutines.experimental.intrinsics.startCoroutineUndispatched
import java.util.concurrent.locks.LockSupport
import kotlin.coroutines.experimental.*
import kotlin.coroutines.experimental.intrinsics.startCoroutineUninterceptedOrReturn
@@ -32,7 +33,8 @@
* The [context][CoroutineScope.context] of the parent coroutine from its [scope][CoroutineScope] may be used,
* in which case the [Job] of the resulting coroutine is a child of the job of the parent coroutine.
*
- * An optional [start] parameter can be set to `false` to start coroutine _lazily_. When `start = false`,
+ * By default, the coroutine is immediately started.
+ * 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].
*
@@ -41,18 +43,34 @@
* 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
+ * @param start coroutine start option
+ * @param block the coroutine code
*/
-fun launch(context: CoroutineContext, start: Boolean = true, block: suspend CoroutineScope.() -> Unit): Job {
+public fun launch(
+ context: CoroutineContext,
+ start: CoroutineStart = CoroutineStart.DEFAULT,
+ block: suspend CoroutineScope.() -> Unit
+): Job {
val newContext = newCoroutineContext(context)
- val coroutine = if (start)
- StandaloneCoroutine(newContext, active = true) else
- LazyStandaloneCoroutine(newContext, block)
+ val coroutine = if (start.isLazy)
+ LazyStandaloneCoroutine(newContext, block) else
+ StandaloneCoroutine(newContext, active = true)
coroutine.initParentJob(context[Job])
- if (start) block.startCoroutine(coroutine, coroutine)
+ start(block, coroutine, coroutine)
return coroutine
}
/**
+ * @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)
+
+/**
* Calls the specified suspending block with a given coroutine context, suspends until it completes, and returns
* the result.
*
diff --git a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineContext.kt b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineContext.kt
index c65be7a..b056fbf 100644
--- a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineContext.kt
+++ b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineContext.kt
@@ -44,6 +44,11 @@
* 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
diff --git a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineDispatcher.kt b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineDispatcher.kt
index 574b0d2..04f77d8 100644
--- a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineDispatcher.kt
+++ b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineDispatcher.kt
@@ -66,6 +66,10 @@
* 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 open fun isDispatchNeeded(context: CoroutineContext): Boolean = true
diff --git a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineStart.kt b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineStart.kt
new file mode 100644
index 0000000..6389d53
--- /dev/null
+++ b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineStart.kt
@@ -0,0 +1,74 @@
+/*
+ * 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.startCoroutineUndispatched
+import kotlin.coroutines.experimental.Continuation
+import kotlin.coroutines.experimental.startCoroutine
+
+/**
+ * Defines start option for coroutines builders.
+ * It is used in `start` parameter of [launch], [async], and [actor][kotlinx.coroutines.experimental.channels.actor]
+ * coroutine builder functions.
+ */
+public enum class CoroutineStart {
+ /**
+ * Default -- schedules coroutine for execution according to its context.
+ *
+ * If the [CoroutineDispatcher] of the coroutine context returns `true` from [CoroutineDispatcher.isDispatchNeeded]
+ * function as most dispatchers do, then the coroutine code is dispatched for execution later, while the code that
+ * invoked the coroutine builder continues execution.
+ *
+ * Note, that [Unconfined] dispatcher always returns `false` from its [CoroutineDispatcher.isDispatchNeeded]
+ * function, so starting coroutine with [Unconfined] dispatcher by [DEFAULT] is the same as using [UNDISPATCHED].
+ */
+ DEFAULT,
+
+ /**
+ * Starts coroutine lazily, only when it is needed.
+ *
+ * See the documentation for the corresponding coroutine builders for details:
+ * [launch], [async], and [actor][kotlinx.coroutines.experimental.channels.actor].
+ */
+ LAZY,
+
+ /**
+ * Immediately executes coroutine until its first suspension point _in the current thread_ as if it the
+ * coroutine was started using [Unconfined] dispatcher. However, when coroutine is resumed from suspension
+ * it is dispatched according to the [CoroutineDispatcher] in its context.
+ */
+ UNDISPATCHED;
+
+ /**
+ * Starts the corresponding block as a coroutine with this coroutine start strategy.
+ *
+ * * [DEFAULT] uses [startCoroutine].
+ * * [UNDISPATCHED] uses [startCoroutineUndispatched].
+ * * [LAZY] does nothing.
+ */
+ public operator fun <R, T> invoke(block: suspend R.() -> T, receiver: R, completion: Continuation<T>) =
+ when (this) {
+ CoroutineStart.DEFAULT -> block.startCoroutine(receiver, completion)
+ CoroutineStart.UNDISPATCHED -> block.startCoroutineUndispatched(receiver, completion)
+ CoroutineStart.LAZY -> Unit // will start lazily
+ }
+
+ /**
+ * Returns `true` when [LAZY].
+ */
+ public val isLazy: Boolean get() = this === LAZY
+}
diff --git a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Deferred.kt b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Deferred.kt
index a795d8b..e36a74b 100644
--- a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Deferred.kt
+++ b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Deferred.kt
@@ -41,7 +41,7 @@
* Usually, a deferred value is created in _active_ state (it is created and started), so its only visible
* states are _active_ and _completed_ (_resolved_, _failed_, or _cancelled_) state.
* However, [async] coroutine builder has an optional `start` parameter that creates a deferred value in _new_ state
- * when this parameter is set to `false`.
+ * when this parameter is set to [CoroutineStart.LAZY].
* Such a deferred can be be made _active_ by invoking [start], [join], or [await].
*/
public interface Deferred<out T> : Job {
@@ -105,29 +105,43 @@
* The [context][CoroutineScope.context] of the parent coroutine from its [scope][CoroutineScope] may be used,
* in which case the [Job] of the resulting coroutine is a child of the job of the parent coroutine.
*
- * An optional [start] parameter can be set to `false` to start coroutine _lazily_. When `start = false`,
+ * By default, the coroutine is immediately started.
+ * 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].
*
- * By default, the coroutine is immediately started. Set an optional [start] parameters to `false`
- * to create coroutine without starting it. In this case it will be _lazy_ and will start
+ * @param context context of the coroutine
+ * @param start coroutine start option
+ * @param block the coroutine code
*/
-public fun <T> async(context: CoroutineContext, start: Boolean = true, block: suspend CoroutineScope.() -> T) : Deferred<T> {
+public fun <T> async(
+ context: CoroutineContext,
+ start: CoroutineStart = CoroutineStart.DEFAULT,
+ block: suspend CoroutineScope.() -> T
+): Deferred<T> {
val newContext = newCoroutineContext(context)
- val coroutine = if (start)
- DeferredCoroutine<T>(newContext, active = true) else
- LazyDeferredCoroutine(newContext, block)
+ val coroutine = if (start.isLazy)
+ LazyDeferredCoroutine(newContext, block) else
+ DeferredCoroutine<T>(newContext, active = true)
coroutine.initParentJob(context[Job])
- if (start) block.startCoroutine(coroutine, coroutine)
+ start(block, coroutine, coroutine)
return coroutine
}
/**
+ * @suppress **Deprecated**: Use `start = CoroutineStart.XXX` parameter
+ */
+@Deprecated(message = "Use `start = CoroutineStart.XXX` parameter",
+ replaceWith = ReplaceWith("async(context, if (start) CoroutineStart.DEFAULT else CoroutineStart.LAZY, block)"))
+public fun <T> async(context: CoroutineContext, start: Boolean, block: suspend CoroutineScope.() -> T): Deferred<T> =
+ async(context, if (start) CoroutineStart.DEFAULT else CoroutineStart.LAZY, block)
+
+/**
* @suppress **Deprecated**: `defer` was renamed to `async`.
*/
@Deprecated(message = "`defer` was renamed to `async`", level = DeprecationLevel.WARNING,
replaceWith = ReplaceWith("async(context, block = block)"))
-public fun <T> defer(context: CoroutineContext, block: suspend CoroutineScope.() -> T) : Deferred<T> =
+public fun <T> defer(context: CoroutineContext, block: suspend CoroutineScope.() -> T): Deferred<T> =
async(context, block = block)
private open class DeferredCoroutine<T>(
diff --git a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Job.kt b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Job.kt
index 43a7886..912ff34 100644
--- a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Job.kt
+++ b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Job.kt
@@ -45,7 +45,7 @@
*
* Usually, a job is created in _active_ state (it is created and started), so its only visible
* states are _active_ and _completed_. However, coroutine builders that provide an optional `start` parameter
- * create a coroutine in _new_ state when this parameter is set to `false`. Such a job can
+ * 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 in the coroutine [context][CoroutineScope.context] represents the coroutine itself.
diff --git a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/Actor.kt b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/Actor.kt
index 1d1abc6..1b95716 100644
--- a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/Actor.kt
+++ b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/Actor.kt
@@ -16,10 +16,8 @@
package kotlinx.coroutines.experimental.channels
-import kotlinx.coroutines.experimental.CoroutineDispatcher
-import kotlinx.coroutines.experimental.CoroutineScope
-import kotlinx.coroutines.experimental.Job
-import kotlinx.coroutines.experimental.newCoroutineContext
+import kotlinx.coroutines.experimental.*
+import kotlinx.coroutines.experimental.selects.SelectInstance
import kotlin.coroutines.experimental.CoroutineContext
import kotlin.coroutines.experimental.startCoroutine
@@ -65,6 +63,12 @@
* The [context][CoroutineScope.context] of the parent coroutine from its [scope][CoroutineScope] may be used,
* in which case the [Job] of the resulting coroutine is a child of the job of the parent coroutine.
*
+ * By default, the coroutine is immediately started.
+ * 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] or on a first message
+ * [sent][SendChannel.send] to this coroutine's mailbox channel.
+ *
* Uncaught exceptions in this coroutine close the channel with this exception as a cause and
* the resulting channel becomes _failed_, so that any attempt to send to such a channel throws exception.
*
@@ -72,19 +76,55 @@
*
* @param context context of the coroutine
* @param capacity capacity of the channel's buffer (no buffer by default)
+ * @param start coroutine start option
* @param block the coroutine code
*/
public fun <E> actor(
context: CoroutineContext,
capacity: Int = 0,
+ start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend ActorScope<E>.() -> Unit
): ActorJob<E> {
+ val newContext = newCoroutineContext(context)
val channel = Channel<E>(capacity)
- return ActorCoroutine(newCoroutineContext(context), channel).apply {
- initParentJob(context[Job])
+ val coroutine = if (start.isLazy)
+ LazyActorCoroutine(newContext, channel, block) else
+ ActorCoroutine(newContext, channel, active = true)
+ coroutine.initParentJob(context[Job])
+ start(block, coroutine, coroutine)
+ return coroutine
+}
+
+private open class ActorCoroutine<E>(
+ parentContext: CoroutineContext,
+ channel: Channel<E>,
+ active: Boolean
+) : ChannelCoroutine<E>(parentContext, channel, active), ActorScope<E>, ActorJob<E>
+
+private class LazyActorCoroutine<E>(
+ parentContext: CoroutineContext,
+ channel: Channel<E>,
+ private val block: suspend ActorScope<E>.() -> Unit
+) : ActorCoroutine<E>(parentContext, channel, active = false) {
+ override val channel: Channel<E> get() = this
+
+ override fun onStart() {
block.startCoroutine(this, this)
}
+
+ suspend override fun send(element: E) {
+ start()
+ return super.send(element)
+ }
+
+ override fun offer(element: E): Boolean {
+ start()
+ return super.offer(element)
+ }
+
+ override fun <R> registerSelectSend(select: SelectInstance<R>, element: E, block: suspend () -> R) {
+ start()
+ return super.registerSelectSend(select, element, block)
+ }
}
-private class ActorCoroutine<E>(parentContext: CoroutineContext, channel: Channel<E>) :
- ChannelCoroutine<E>(parentContext, channel), ActorScope<E>, ActorJob<E>
diff --git a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/ChannelCoroutine.kt b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/ChannelCoroutine.kt
index 484774b..e1b64b2 100644
--- a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/ChannelCoroutine.kt
+++ b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/ChannelCoroutine.kt
@@ -23,8 +23,9 @@
internal open class ChannelCoroutine<E>(
override val parentContext: CoroutineContext,
- val channel: Channel<E>
-) : AbstractCoroutine<Unit>(active = true), Channel<E> by channel {
+ open val channel: Channel<E>,
+ active: Boolean
+) : AbstractCoroutine<Unit>(active), Channel<E> by channel {
override fun afterCompletion(state: Any?, mode: Int) {
val cause = (state as? JobSupport.CompletedExceptionally)?.cause
if (!channel.close(cause) && cause != null)
diff --git a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/Produce.kt b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/Produce.kt
index d4b0834..0590ebe 100644
--- a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/Produce.kt
+++ b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/Produce.kt
@@ -110,4 +110,4 @@
produce(context, capacity, block)
private class ProducerCoroutine<E>(parentContext: CoroutineContext, channel: Channel<E>) :
- ChannelCoroutine<E>(parentContext, channel), ProducerScope<E>, ProducerJob<E>
+ ChannelCoroutine<E>(parentContext, channel, active = true), ProducerScope<E>, ProducerJob<E>
diff --git a/kotlinx-coroutines-core/src/test/kotlin/guide/example-compose-03.kt b/kotlinx-coroutines-core/src/test/kotlin/guide/example-compose-03.kt
index 3d125f3..cd56a18 100644
--- a/kotlinx-coroutines-core/src/test/kotlin/guide/example-compose-03.kt
+++ b/kotlinx-coroutines-core/src/test/kotlin/guide/example-compose-03.kt
@@ -32,8 +32,8 @@
fun main(args: Array<String>) = runBlocking<Unit> {
val time = measureTimeMillis {
- val one = async(CommonPool, start = false) { doSomethingUsefulOne() }
- val two = async(CommonPool, start = false) { doSomethingUsefulTwo() }
+ val one = async(CommonPool, CoroutineStart.LAZY) { doSomethingUsefulOne() }
+ val two = async(CommonPool, CoroutineStart.LAZY) { doSomethingUsefulTwo() }
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
diff --git a/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/AsyncLazyTest.kt b/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/AsyncLazyTest.kt
index 72b6377..415252b 100644
--- a/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/AsyncLazyTest.kt
+++ b/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/AsyncLazyTest.kt
@@ -23,7 +23,7 @@
@Test
fun testSimple(): Unit = runBlocking {
expect(1)
- val d = async(context, start = false) {
+ val d = async(context, CoroutineStart.LAZY) {
expect(3)
42
}
@@ -39,7 +39,7 @@
@Test
fun testLazyDeferAndYield(): Unit = runBlocking {
expect(1)
- val d = async(context, start = false) {
+ val d = async(context, CoroutineStart.LAZY) {
expect(3)
yield() // this has not effect, because parent coroutine is waiting
expect(4)
@@ -57,7 +57,7 @@
@Test
fun testLazyDeferAndYield2(): Unit = runBlocking {
expect(1)
- val d = async(context, start = false) {
+ val d = async(context, CoroutineStart.LAZY) {
expect(7)
42
}
@@ -84,7 +84,7 @@
@Test(expected = IOException::class)
fun testSimpleException(): Unit = runBlocking {
expect(1)
- val d = async(context, start = false) {
+ val d = async(context, CoroutineStart.LAZY) {
finish(3)
throw IOException()
}
@@ -96,7 +96,7 @@
@Test(expected = IOException::class)
fun testLazyDeferAndYieldException(): Unit = runBlocking {
expect(1)
- val d = async(context, start = false) {
+ val d = async(context, CoroutineStart.LAZY) {
expect(3)
yield() // this has not effect, because parent coroutine is waiting
finish(4)
@@ -110,7 +110,7 @@
@Test
fun testCatchException(): Unit = runBlocking {
expect(1)
- val d = async(context, start = false) {
+ val d = async(context, CoroutineStart.LAZY) {
expect(3)
throw IOException()
}
@@ -128,7 +128,7 @@
@Test
fun testStart(): Unit = runBlocking {
expect(1)
- val d = async(context, start = false) {
+ val d = async(context, CoroutineStart.LAZY) {
expect(4)
42
}
@@ -148,7 +148,7 @@
@Test(expected = CancellationException::class)
fun testCancelBeforeStart(): Unit = runBlocking {
expect(1)
- val d = async(context, start = false) {
+ val d = async(context, CoroutineStart.LAZY) {
expectUnreached()
42
}
@@ -166,7 +166,7 @@
@Test(expected = CancellationException::class)
fun testCancelWhileComputing(): Unit = runBlocking {
expect(1)
- val d = async(context, start = false) {
+ val d = async(context, CoroutineStart.LAZY) {
expect(4)
yield() // yield to main, that is going to cancel us
expectUnreached()
diff --git a/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/LaunchLazyTest.kt b/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/LaunchLazyTest.kt
index a24678b..28d7e82 100644
--- a/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/LaunchLazyTest.kt
+++ b/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/LaunchLazyTest.kt
@@ -22,7 +22,7 @@
@Test
fun testLaunchAndYieldJoin() = runBlocking {
expect(1)
- val job = launch(context, start = false) {
+ val job = launch(context, CoroutineStart.LAZY) {
expect(4)
yield() // does nothing -- main waits
expect(5)
@@ -39,7 +39,7 @@
@Test
fun testStart() = runBlocking {
expect(1)
- val job = launch(context, start = false) {
+ val job = launch(context, CoroutineStart.LAZY) {
expect(5)
yield() // yields back to main
expect(7)
diff --git a/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/channels/ActorLazyTest.kt b/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/channels/ActorLazyTest.kt
new file mode 100644
index 0000000..a292f5c
--- /dev/null
+++ b/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/channels/ActorLazyTest.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.channels
+
+import kotlinx.coroutines.experimental.CoroutineStart
+import kotlinx.coroutines.experimental.TestBase
+import kotlinx.coroutines.experimental.runBlocking
+import kotlinx.coroutines.experimental.yield
+import org.hamcrest.core.IsEqual
+import org.junit.Assert.assertThat
+import org.junit.Test
+
+class ActorLazyTest : TestBase() {
+ @Test
+ fun testEmptyStart() = runBlocking<Unit> {
+ expect(1)
+ val actor = actor<String>(context, start = CoroutineStart.LAZY) {
+ expect(5)
+ }
+ assertThat(actor.isActive, IsEqual(false))
+ assertThat(actor.isCompleted, IsEqual(false))
+ assertThat(actor.channel.isClosedForSend, IsEqual(false))
+ expect(2)
+ yield() // to actor code --> nothing happens (not started!)
+ assertThat(actor.isActive, IsEqual(false))
+ assertThat(actor.isCompleted, IsEqual(false))
+ assertThat(actor.channel.isClosedForSend, IsEqual(false))
+ expect(3)
+ // start actor explicitly
+ actor.start()
+ expect(4)
+ yield() // to started actor
+ assertThat(actor.isActive, IsEqual(false))
+ assertThat(actor.isCompleted, IsEqual(true))
+ assertThat(actor.channel.isClosedForSend, IsEqual(true))
+ finish(6)
+ }
+
+ @Test
+ fun testOne() = runBlocking<Unit> {
+ expect(1)
+ val actor = actor<String>(context, start = CoroutineStart.LAZY) {
+ expect(4)
+ assertThat(receive(), IsEqual("OK"))
+ expect(5)
+ }
+ assertThat(actor.isActive, IsEqual(false))
+ assertThat(actor.isCompleted, IsEqual(false))
+ assertThat(actor.channel.isClosedForSend, IsEqual(false))
+ expect(2)
+ yield() // to actor code --> nothing happens (not started!)
+ assertThat(actor.isActive, IsEqual(false))
+ assertThat(actor.isCompleted, IsEqual(false))
+ assertThat(actor.channel.isClosedForSend, IsEqual(false))
+ expect(3)
+ // send message to actor --> should start it
+ actor.send("OK")
+ assertThat(actor.isActive, IsEqual(false))
+ assertThat(actor.isCompleted, IsEqual(true))
+ assertThat(actor.channel.isClosedForSend, IsEqual(true))
+ finish(6)
+ }
+}
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/channels/ActorTest.kt b/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/channels/ActorTest.kt
new file mode 100644
index 0000000..ce637b2
--- /dev/null
+++ b/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/channels/ActorTest.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.channels
+
+import kotlinx.coroutines.experimental.TestBase
+import kotlinx.coroutines.experimental.runBlocking
+import kotlinx.coroutines.experimental.yield
+import org.hamcrest.core.IsEqual
+import org.junit.Assert.assertThat
+import org.junit.Test
+
+class ActorTest : TestBase() {
+ @Test
+ fun testEmpty() = runBlocking<Unit> {
+ expect(1)
+ val actor = actor<String>(context) {
+ expect(3)
+ }
+ assertThat(actor.isActive, IsEqual(true))
+ assertThat(actor.isCompleted, IsEqual(false))
+ assertThat(actor.channel.isClosedForSend, IsEqual(false))
+ expect(2)
+ yield() // to actor code
+ assertThat(actor.isActive, IsEqual(false))
+ assertThat(actor.isCompleted, IsEqual(true))
+ assertThat(actor.channel.isClosedForSend, IsEqual(true))
+ finish(4)
+ }
+
+ @Test
+ fun testOne() = runBlocking<Unit> {
+ expect(1)
+ val actor = actor<String>(context) {
+ expect(3)
+ assertThat(receive(), IsEqual("OK"))
+ expect(6)
+ }
+ assertThat(actor.isActive, IsEqual(true))
+ assertThat(actor.isCompleted, IsEqual(false))
+ assertThat(actor.channel.isClosedForSend, IsEqual(false))
+ expect(2)
+ yield() // to actor code
+ assertThat(actor.isActive, IsEqual(true))
+ assertThat(actor.isCompleted, IsEqual(false))
+ assertThat(actor.channel.isClosedForSend, IsEqual(false))
+ expect(4)
+ // send message to actor
+ actor.send("OK")
+ expect(5)
+ yield() // to actor code
+ assertThat(actor.isActive, IsEqual(false))
+ assertThat(actor.isCompleted, IsEqual(true))
+ assertThat(actor.channel.isClosedForSend, IsEqual(true))
+ finish(7)
+ }
+}
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectDeferredTest.kt b/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectDeferredTest.kt
index a39a719..b53878b 100644
--- a/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectDeferredTest.kt
+++ b/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectDeferredTest.kt
@@ -70,7 +70,7 @@
@Test
fun testSelectIncompleteLazy() = runBlocking<Unit> {
expect(1)
- val d1 = async<Int>(context, start = false) {
+ val d1 = async(context, CoroutineStart.LAZY) {
expect(5)
42
}
diff --git a/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectJobTest.kt b/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectJobTest.kt
index 1faf5bd..0231fd1 100644
--- a/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectJobTest.kt
+++ b/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/selects/SelectJobTest.kt
@@ -64,7 +64,7 @@
@Test
fun testSelectLazy() = runBlocking<Unit> {
expect(1)
- val job = launch(context, start = false) {
+ val job = launch(context, CoroutineStart.LAZY) {
expect(2)
}
val res = select<String> {