Quasar integration
diff --git a/integration/README.md b/integration/README.md
index 8349cd0..9845b07 100644
--- a/integration/README.md
+++ b/integration/README.md
@@ -7,6 +7,7 @@
* [kotlinx-coroutines-jdk8](kotlinx-coroutines-jdk8/README.md) -- integration with JDK8 `CompletableFuture` (Android API level 24).
* [kotlinx-coroutines-nio](kotlinx-coroutines-nio/README.md) -- integration with asynchronous IO on JDK7+ (Android O Preview).
* [kotlinx-coroutines-guava](kotlinx-coroutines-guava/README.md) -- integration with Guava [ListenableFuture](https://github.com/google/guava/wiki/ListenableFutureExplained).
+* [kotlinx-coroutines-quasar](kotlinx-coroutines-quasar/README.md) -- integration with [Quasar](http://docs.paralleluniverse.co/quasar/).
## Contributing
diff --git a/integration/kotlinx-coroutines-quasar/README.md b/integration/kotlinx-coroutines-quasar/README.md
new file mode 100644
index 0000000..99fd973
--- /dev/null
+++ b/integration/kotlinx-coroutines-quasar/README.md
@@ -0,0 +1,36 @@
+# Module kotlinx-coroutines-quasar
+
+Integration with [Quasar](http://docs.paralleluniverse.co/quasar/).
+It supports invoking Quasar-instrumented suspendable code from within Kotlin
+coroutines via [runSuspendable] and invoking Kotlin suspending code from
+Quasar-instrumented code via [runFiberBlocking].
+
+## Example
+
+Invoke Quasar-instrumented suspendable code from Kotlin coroutine via [runSuspendable]:
+
+```kotlin
+runSuspendable(SuspendableCallable {
+ // Your suspendable code that will be instrumented by Quasar here
+})
+```
+
+Invoke Kotlin suspending function from Quasar-instrumented suspendable code via [runFiberBlocking]:
+
+```kotlin
+runFiberBlocking {
+ // Your Kotlin suspending code here
+}
+```
+
+# Package kotlinx.coroutines.experimental.quasar
+
+Integration with [Quasar](http://docs.paralleluniverse.co/quasar/).
+
+<!--- MODULE kotlinx-coroutines-core -->
+<!--- INDEX kotlinx.coroutines.experimental -->
+<!--- MODULE kotlinx-coroutines-quasar -->
+<!--- INDEX kotlinx.coroutines.experimental.quasar -->
+[runSuspendable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-quasar/kotlinx.coroutines.experimental.quasar/run-suspendable.html
+[runFiberBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-quasar/kotlinx.coroutines.experimental.quasar/run-fiber-blocking.html
+<!--- END -->
diff --git a/integration/kotlinx-coroutines-quasar/pom.xml b/integration/kotlinx-coroutines-quasar/pom.xml
new file mode 100644
index 0000000..4666ad0
--- /dev/null
+++ b/integration/kotlinx-coroutines-quasar/pom.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.jetbrains.kotlinx</groupId>
+ <artifactId>kotlinx-coroutines</artifactId>
+ <version>0.17-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>kotlinx-coroutines-quasar</artifactId>
+ <packaging>jar</packaging>
+
+ <build>
+ <sourceDirectory>src/main/kotlin</sourceDirectory>
+ <testSourceDirectory>src/test/kotlin</testSourceDirectory>
+
+ <plugins>
+ <plugin>
+ <groupId>org.jetbrains.dokka</groupId>
+ <artifactId>dokka-maven-plugin</artifactId>
+ <version>${dokka.version}</version>
+ <configuration>
+ <externalDocumentationLinks combine.children="append">
+ <link>
+ <url>${core.docs.url}</url>
+ <packageListUrl>file:///${project.parent.basedir}/kotlinx-coroutines-core/target/dokka/kotlinx-coroutines-core/package-list</packageListUrl>
+ </link>
+ <link>
+ <url>https://google.github.io/guava/releases/18.0/api/docs/</url>
+ </link>
+ </externalDocumentationLinks>
+ <skip>false</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>getClasspathFilenames</id>
+ <goals>
+ <goal>properties</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <!-- add -Dco.paralleluniverse.fibers.writeInstrumentedClasses=QuasarTest to dump classes -->
+ <!-- add =v for verbose -->
+ <argLine>-javaagent:@{co.paralleluniverse:quasar-core:jar}</argLine>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <!-- dependency on coroutines core -->
+ <dependency>
+ <groupId>org.jetbrains.kotlinx</groupId>
+ <artifactId>kotlinx-coroutines-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <!-- coroutines test framework dependency -->
+ <dependency>
+ <groupId>org.jetbrains.kotlinx</groupId>
+ <artifactId>kotlinx-coroutines-core</artifactId>
+ <version>${project.version}</version>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
+ <!-- 3rd party dependencies -->
+ <dependency>
+ <groupId>co.paralleluniverse</groupId>
+ <artifactId>quasar-core</artifactId>
+ <version>0.7.9</version>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/integration/kotlinx-coroutines-quasar/src/main/kotlin/kotlinx/coroutines/experimental/quasar/KotlinSuspendableClassifier.kt b/integration/kotlinx-coroutines-quasar/src/main/kotlin/kotlinx/coroutines/experimental/quasar/KotlinSuspendableClassifier.kt
new file mode 100644
index 0000000..40006fd
--- /dev/null
+++ b/integration/kotlinx-coroutines-quasar/src/main/kotlin/kotlinx/coroutines/experimental/quasar/KotlinSuspendableClassifier.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.quasar
+
+import co.paralleluniverse.fibers.instrument.MethodDatabase
+import co.paralleluniverse.fibers.instrument.SuspendableClassifier
+
+/**
+ * @suppress **Internal implementation**.
+ */
+class KotlinSuspendableClassifier : SuspendableClassifier {
+ override fun isSuspendable(
+ db: MethodDatabase,
+ sourceName: String?,
+ sourceDebugInfo: String?,
+ isInterface: Boolean,
+ className: String?,
+ superClassName: String?,
+ interfaces: Array<out String>,
+ methodName: String,
+ methodDesc: String,
+ methodSignature: String?,
+ methodExceptions: Array<out String>?
+ ): MethodDatabase.SuspendableType? {
+ if (methodName == "run" &&
+ methodDesc.startsWith("()") &&
+ interfaces.contains("co/paralleluniverse/strands/SuspendableCallable"))
+ return MethodDatabase.SuspendableType.SUSPENDABLE
+ return null
+ }
+}
\ No newline at end of file
diff --git a/integration/kotlinx-coroutines-quasar/src/main/kotlin/kotlinx/coroutines/experimental/quasar/Quasar.kt b/integration/kotlinx-coroutines-quasar/src/main/kotlin/kotlinx/coroutines/experimental/quasar/Quasar.kt
new file mode 100644
index 0000000..a88ef8e
--- /dev/null
+++ b/integration/kotlinx-coroutines-quasar/src/main/kotlin/kotlinx/coroutines/experimental/quasar/Quasar.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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.quasar
+
+import co.paralleluniverse.fibers.Fiber
+import co.paralleluniverse.fibers.FiberAsync
+import co.paralleluniverse.fibers.SuspendExecution
+import co.paralleluniverse.fibers.Suspendable
+import co.paralleluniverse.strands.SuspendableCallable
+import kotlinx.coroutines.experimental.asCoroutineDispatcher
+import kotlinx.coroutines.experimental.cancelFutureOnCompletion
+import kotlinx.coroutines.experimental.suspendCancellableCoroutine
+import kotlin.coroutines.experimental.Continuation
+import kotlin.coroutines.experimental.CoroutineContext
+import kotlin.coroutines.experimental.startCoroutine
+
+/**
+ * Runs Quasar-instrumented suspendable code from Kotlin coroutine.
+ */
+suspend fun <T> runSuspendable(callable: SuspendableCallable<T>): T = suspendCancellableCoroutine { cont ->
+ val fiber = object : Fiber<Unit>() {
+ @Throws(SuspendExecution::class)
+ override fun run() {
+ val result = try { callable.run() }
+ catch (e: Throwable) {
+ cont.resumeWithException(e)
+ return
+ }
+ cont.resume(result)
+ }
+ }
+ cont.cancelFutureOnCompletion(fiber)
+ fiber.start()
+}
+
+/**
+ * Runs Kotlin suspending function from Quasar-instrumented suspendable code.
+ */
+@Suspendable
+fun <T> runFiberBlocking(block: suspend () -> T): T =
+ CoroutineAsync(block).run()
+
+private class CoroutineAsync<T>(
+ private val block: suspend () -> T
+) : FiberAsync<T, Throwable>(), Continuation<T> {
+ override val context: CoroutineContext = Fiber.currentFiber().scheduler.executor.asCoroutineDispatcher()
+ override fun resume(value: T) { asyncCompleted(value) }
+ override fun resumeWithException(exception: Throwable) { asyncFailed(exception) }
+
+ override fun requestAsync() {
+ block.startCoroutine(completion = this)
+ }
+}
diff --git a/integration/kotlinx-coroutines-quasar/src/main/resources/META-INF/services/co.paralleluniverse.fibers.instrument.SuspendableClassifier b/integration/kotlinx-coroutines-quasar/src/main/resources/META-INF/services/co.paralleluniverse.fibers.instrument.SuspendableClassifier
new file mode 100644
index 0000000..7d16870
--- /dev/null
+++ b/integration/kotlinx-coroutines-quasar/src/main/resources/META-INF/services/co.paralleluniverse.fibers.instrument.SuspendableClassifier
@@ -0,0 +1 @@
+kotlinx.coroutines.experimental.quasar.KotlinSuspendableClassifier
\ No newline at end of file
diff --git a/integration/kotlinx-coroutines-quasar/src/test/kotlin/kotlinx/coroutines/experimental/quasar/QuasarTest.kt b/integration/kotlinx-coroutines-quasar/src/test/kotlin/kotlinx/coroutines/experimental/quasar/QuasarTest.kt
new file mode 100644
index 0000000..402a880
--- /dev/null
+++ b/integration/kotlinx-coroutines-quasar/src/test/kotlin/kotlinx/coroutines/experimental/quasar/QuasarTest.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.quasar
+
+import co.paralleluniverse.fibers.Fiber
+import co.paralleluniverse.fibers.SuspendExecution
+import co.paralleluniverse.strands.SuspendableCallable
+import co.paralleluniverse.strands.dataflow.Val
+import guide.test.ignoreLostThreads
+import kotlinx.coroutines.experimental.CompletableDeferred
+import kotlinx.coroutines.experimental.TestBase
+import kotlinx.coroutines.experimental.launch
+import kotlinx.coroutines.experimental.runBlocking
+import org.junit.Before
+import org.junit.Test
+import java.util.concurrent.TimeUnit
+
+class QuasarTest : TestBase() {
+ @Before
+ fun setup() {
+ ignoreLostThreads(
+ "FiberTimedScheduler-default-fiber-pool",
+ "ForkJoinPool-default-fiber-pool-worker-",
+ "Timer-")
+ }
+
+ @Test
+ fun testRunSuspendable() = runBlocking<Unit> {
+ expect(1)
+ val started = CompletableDeferred<Unit>() // Kotlin's event
+ val x = Val<String>() // Quasar's data flow
+ launch(coroutineContext) {
+ started.await() // await Quasar's scheduler
+ expect(3) // will get scheduled when runSuspendable suspends
+ x.set("OK")
+ }
+ val result = runSuspendable(SuspendableCallable {
+ expect(2)
+ started.complete(Unit) // signal that we've started
+ x.get(10, TimeUnit.SECONDS) // will get suspended
+ })
+ finish(4)
+ check(result == "OK")
+ }
+
+ @Test
+ fun testRunFiberBlocking() = runBlocking {
+ expect(1)
+ val started = CompletableDeferred<Unit>() // Kotlin's event
+ val result = CompletableDeferred<String>() // result goes here
+ val fiber = object : Fiber<String>() {
+ @Throws(SuspendExecution::class)
+ override fun run(): String {
+ expect(3)
+ started.complete(Unit) // signal that fiber is started
+ // block fiber on suspendable await
+ val value = runFiberBlocking {
+ result.await()
+ }
+ expect(5)
+ return value
+ }
+ }
+ fiber.start()
+ expect(2)
+ started.await() // wait fiber to start
+ expect(4)
+ result.complete("OK") // send Ok to fiber
+ val answer = runSuspendable(SuspendableCallable {
+ fiber.get()
+ })
+ finish(6)
+ check(answer == "OK")
+ }
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 0912c19..bfc6b3c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -112,6 +112,7 @@
<module>integration/kotlinx-coroutines-jdk8</module>
<module>integration/kotlinx-coroutines-nio</module>
<module>integration/kotlinx-coroutines-guava</module>
+ <module>integration/kotlinx-coroutines-quasar</module>
</modules>
<dependencies>
diff --git a/site/build.xml b/site/build.xml
index 410a6b3..e67458c 100644
--- a/site/build.xml
+++ b/site/build.xml
@@ -44,6 +44,7 @@
<fileset dir="../integration/kotlinx-coroutines-jdk8/target/dokka" includes="**/*.md"/>
<fileset dir="../integration/kotlinx-coroutines-nio/target/dokka" includes="**/*.md"/>
<fileset dir="../integration/kotlinx-coroutines-guava/target/dokka" includes="**/*.md"/>
+ <fileset dir="../integration/kotlinx-coroutines-quasar/target/dokka" includes="**/*.md"/>
</copy>
<antcall target="jekyll"/>
</target>
diff --git a/site/docs/index.md b/site/docs/index.md
index 02373cc..39057bd 100644
--- a/site/docs/index.md
+++ b/site/docs/index.md
@@ -21,3 +21,4 @@
[kotlinx-coroutines-jdk8](kotlinx-coroutines-jdk8) | Integration with JDK8 `CompletableFuture` (Android API level 24)
[kotlinx-coroutines-nio](kotlinx-coroutines-nio) | Integration with asynchronous IO on JDK7+ (Android O Preview)
[kotlinx-coroutines-guava](kotlinx-coroutines-guava) | Integration with Guava [ListenableFuture](https://github.com/google/guava/wiki/ListenableFutureExplained)
+[kotlinx-coroutines-quasar](kotlinx-coroutines-quasar) | Integration with [Quasar](http://docs.paralleluniverse.co/quasar/)
diff --git a/site/pom.xml b/site/pom.xml
index ce0e03a..4ce3e16 100644
--- a/site/pom.xml
+++ b/site/pom.xml
@@ -84,6 +84,11 @@
<artifactId>kotlinx-coroutines-guava</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.jetbrains.kotlinx</groupId>
+ <artifactId>kotlinx-coroutines-quasar</artifactId>
+ <version>${project.version}</version>
+ </dependency>
</dependencies>
<build>