This repository has been archived by the owner on Jul 13, 2020. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add the first implementation of anko-coroutines (#327)
- Loading branch information
Showing
13 changed files
with
311 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
|
||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.jetbrains.anko.generated.coroutines"> | ||
<uses-sdk android:minSdkVersion="15" android:targetSdkVersion="23"/> | ||
<application/> | ||
</manifest> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
apply from: '../generated.gradle' | ||
|
||
dependencies { | ||
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" | ||
compile "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.12" | ||
} | ||
|
||
kotlin { | ||
experimental { | ||
coroutines "enable" | ||
} | ||
} | ||
|
||
task androidReleaseSources(type: Jar, dependsOn: assembleRelease) { | ||
from("src", "../../static/common/src") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/* | ||
* Copyright 2016 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 org.jetbrains.anko.async | ||
|
||
import kotlinx.coroutines.experimental.* | ||
import kotlin.coroutines.experimental.* | ||
|
||
internal open class AnkoCoroutine<out C : Any>( | ||
override val parentContext: CoroutineContext, | ||
active: Boolean = true | ||
) : AbstractCoroutine<Unit>(active), AnkoCoroutineScope<C> { | ||
override fun afterCompletion(state: Any?, mode: Int) { | ||
if (state is CompletedExceptionally) { | ||
handleCoroutineException(parentContext, state.exception) | ||
} | ||
} | ||
|
||
@Suppress("UNCHECKED_CAST") | ||
override val caller: C | ||
get() = this[AsyncCaller]!!.ref as C | ||
} |
49 changes: 49 additions & 0 deletions
49
anko/library/generated/coroutines/src/AnkoCoroutineDispatcher.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/* | ||
* Copyright 2016 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 org.jetbrains.anko.async | ||
|
||
import android.os.Handler | ||
import android.os.Looper | ||
import kotlinx.coroutines.experimental.* | ||
import java.util.concurrent.Executors | ||
import kotlin.coroutines.experimental.* | ||
|
||
internal object AnkoCoroutineDispatcher : CoroutineDispatcher() { | ||
override fun dispatch(context: CoroutineContext, block: Runnable) { | ||
if (CoroutineAndroidContextHelper.mainThread == Thread.currentThread()) { | ||
context[AsyncCaller]?.withHardReference( | ||
onExisting = { block.run() }, | ||
onDisposed = { context[Job]!!.cancel(CallerDisposedException()) }) | ||
} else { | ||
CoroutineAndroidContextHelper.handler.post { | ||
context[AsyncCaller]?.withHardReference( | ||
onExisting = { block.run() }, | ||
onDisposed = { context[Job]!!.cancel(CallerDisposedException()) }) | ||
} | ||
} | ||
} | ||
|
||
override fun toString() = "AnkoCoroutineDispatcher" | ||
} | ||
|
||
internal object CoroutineAndroidContextHelper { | ||
val handler = Handler(Looper.getMainLooper()) | ||
val mainThread: Thread = Looper.getMainLooper().thread | ||
} | ||
|
||
internal val DEFAULT_EXECUTOR = Executors.newScheduledThreadPool( | ||
2 * Runtime.getRuntime().availableProcessors()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/* | ||
* Copyright 2016 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 org.jetbrains.anko.async | ||
|
||
import java.lang.ref.WeakReference | ||
import kotlin.coroutines.experimental.* | ||
|
||
internal class AsyncCaller<C : Any>(ctx: C) : AbstractCoroutineContextElement(AsyncCaller) { | ||
companion object Key : CoroutineContext.Key<AsyncCaller<*>> | ||
|
||
internal val weakRef = WeakReference(ctx) | ||
private var hardRef: C? = null | ||
|
||
internal val ref: C | ||
get() = hardRef!! | ||
|
||
// This function is not thread-safe, must be called from the UI thread. | ||
internal inline fun withHardReference(onExisting: () -> Unit, onDisposed: () -> Unit) { | ||
val hardRef = this[AsyncCaller]?.weakRef?.get() ?: run { | ||
onDisposed() | ||
return | ||
} | ||
|
||
@Suppress("UNCHECKED_CAST") | ||
this.hardRef = hardRef as C | ||
|
||
onExisting() | ||
|
||
this.hardRef = null | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/* | ||
* Copyright 2016 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 org.jetbrains.anko.async | ||
|
||
import java.util.concurrent.Executor | ||
import kotlin.coroutines.experimental.* | ||
|
||
internal class AsyncExecutor(val executor: Executor) : AbstractCoroutineContextElement(AsyncExecutor) { | ||
companion object Key : CoroutineContext.Key<AsyncExecutor> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
/* | ||
* Copyright 2016 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 org.jetbrains.anko.async | ||
|
||
import kotlinx.coroutines.experimental.* | ||
import java.util.concurrent.Executor | ||
import kotlin.coroutines.experimental.* | ||
|
||
fun <C : Any> C.async( | ||
context: CoroutineContext = EmptyCoroutineContext, | ||
block: suspend AnkoCoroutineScope<C>.() -> Unit | ||
): Job { | ||
return async(DEFAULT_EXECUTOR, context, block) | ||
} | ||
|
||
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") | ||
fun <C : Any> C.async( | ||
executor: Executor, | ||
context: CoroutineContext = EmptyCoroutineContext, | ||
block: suspend AnkoCoroutineScope<C>.() -> Unit | ||
): Job { | ||
val newContext = newCoroutineContext(context) + | ||
AsyncCaller(this) + AsyncExecutor(executor) + AnkoCoroutineDispatcher | ||
|
||
val coroutine = AnkoCoroutine<C>(newContext) | ||
coroutine.initParentJob(context[Job]) | ||
block.startCoroutine(coroutine, coroutine) | ||
|
||
return coroutine | ||
} | ||
|
||
interface AnkoCoroutineScope<out C : Any> : CoroutineScope { | ||
val caller: C | ||
} | ||
|
||
suspend fun <R> AnkoCoroutineScope<*>.bg(f: () -> R): R { | ||
val executor = context[AsyncExecutor]?.executor ?: DEFAULT_EXECUTOR | ||
return suspendCoroutine { c -> | ||
executor.execute { | ||
c.resume(f()) | ||
} | ||
} | ||
} | ||
|
||
class CallerDisposedException : RuntimeException() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package test | ||
|
||
import android.app.Activity | ||
import android.os.Looper | ||
import org.jetbrains.anko.async.async | ||
import org.jetbrains.anko.async.bg | ||
import org.junit.Assert.* | ||
import org.junit.Test | ||
import org.junit.runner.RunWith | ||
import org.robolectric.Robolectric | ||
import org.robolectric.RobolectricGradleTestRunner | ||
import org.robolectric.annotation.Config | ||
import org.robolectric.shadows.ShadowLooper | ||
|
||
class AsyncTestActivity : Activity() { | ||
val result = ThreadLocal<String>() | ||
|
||
fun loadData() = async { | ||
assertSame(MAIN_THREAD, Thread.currentThread()) | ||
val data = bg { | ||
assertNotSame(MAIN_THREAD, Thread.currentThread()) | ||
loadDataSync() | ||
} | ||
assertSame(MAIN_THREAD, Thread.currentThread()) | ||
assertEquals(HELLO_WORLD, data) | ||
|
||
result.set(data) | ||
} | ||
} | ||
|
||
private val DELAY = 500L | ||
private val MAIN_THREAD = Looper.getMainLooper().thread | ||
|
||
private val HELLO_WORLD = "Hello, world!" | ||
|
||
private fun loadDataSync(): String { | ||
Thread.sleep(DELAY) | ||
return HELLO_WORLD | ||
} | ||
|
||
@RunWith(RobolectricGradleTestRunner::class) | ||
@Config(constants = BuildConfig::class) | ||
class AsyncTest { | ||
@Test | ||
fun test() { | ||
val activity = Robolectric.buildActivity(AsyncTestActivity::class.java).create().get() | ||
|
||
activity.loadData() | ||
|
||
val time = System.currentTimeMillis() + DELAY * 2 + 1000 | ||
while (System.currentTimeMillis() < time) { | ||
ShadowLooper.idleMainLooper() | ||
Thread.yield() | ||
} | ||
|
||
assertEquals(HELLO_WORLD, activity.result.get()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
sdk.dir=dependencies/android-sdk | ||
sdk.dir=dependencies/android-sdk | ||
kotlin.coroutines=enable |
Oops, something went wrong.