Skip to content

Commit

Permalink
Add initial state
Browse files Browse the repository at this point in the history
  • Loading branch information
dzharkov committed Jun 22, 2016
0 parents commit 8e4e0e4
Show file tree
Hide file tree
Showing 12 changed files with 953 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/.idea
*.iml
target
89 changes: 89 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# kotlinx.coroutines
Two libraries built upon Kotlin coroutines:
* `kotlinx-coroutines-async` with convenient interfaces/wrappers to commonly
used asynchronous API shipped with standard JDK, namely promise-like `CompletableFuture`
and asynchronous channels from `java.nio` package
* `kotlinx-coroutines-generate` provides ability to create `Sequence` objects
generated by coroutine body containing `yield` suspension points

## Examples
### Async
```kotlin
import kotlinx.coroutines.async
import java.util.concurrent.CompletableFuture

private fun startLongAsyncOperation(v: Int) =
CompletableFuture.supplyAsync {
Thread.sleep(1000)
"Result: $v"
}

fun main(args: Array<String>) {
val future = async<String> {
(1..5).map {
await(startLongAsyncOperation(it))
}.joinToString("\n")
}

println(future.get())
}
```

### Generate
```
import kotlinx.coroutines.generate
fun main(args: Array<String>) {
val sequence = generate<Int> {
for (i in 1..5) {
yield(i)
}
}
println(sequence.joinToString(" "))
}
```

For more examples you can look at `kotlinx-coroutines-async-example-ui` sample
project or in tests directories.

## Maven

Add jcenter repository (if you don't have it yet)

```xml
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>central</id>
<name>bintray</name>
<url>http://jcenter.bintray.com</url>
</repository>
```

Add a dependency:

```xml
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-generate</artifactId>
<version>0.1</version>
</dependency>
```

## Gradle

Just add a dependency:

```groovy
compile 'org.jetbrains.kotlinx:kotlinx-coroutines-generate:0.1'
```

jcenter repository should be configured by default in gradle, but if it's not you may need to include it:

```groovy
repositories {
jcenter()
}
```
43 changes: 43 additions & 0 deletions kotlinx-coroutines-async-example-ui/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<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.1-SNAPSHOT</version>
</parent>

<artifactId>kotlinx-coroutines-async-example-ui</artifactId>
<packaging>jar</packaging>

<name>Example of asyncUi usage</name>

<properties>
<kotlin.version>1.1-SNAPSHOT</kotlin.version>
</properties>

<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-async</artifactId>
<version>${version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
48 changes: 48 additions & 0 deletions kotlinx-coroutines-async-example-ui/src/main/kotlin/main.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import kotlinx.coroutines.asyncUi
import java.awt.Insets
import java.util.concurrent.CompletableFuture
import javax.swing.*

private fun createAndShowGUI() {
val frame = JFrame("Async UI example")
frame.defaultCloseOperation = JFrame.EXIT_ON_CLOSE

val jProgressBar = JProgressBar(0, 100).apply {
value = 0
isStringPainted = true
}

val jTextArea = JTextArea(11, 10)
jTextArea.margin = Insets(5, 5, 5, 5)
jTextArea.isEditable = false

val panel = JPanel()

panel.add(jProgressBar)
panel.add(jTextArea)

frame.contentPane.add(panel)
frame.pack()
frame.isVisible = true

asyncUi {
for (i in 1..10) {
// 'append' method and consequent 'jProgressBar.setValue' are called
// within Swing event dispatch thread
jTextArea.append(
await(startLongAsyncOperation(i))
)
jProgressBar.value = i * 10
}
}
}

private fun startLongAsyncOperation(v: Int) =
CompletableFuture.supplyAsync {
Thread.sleep(1000)
"Message: $v\n"
}

fun main(args: Array<String>) {
SwingUtilities.invokeLater(::createAndShowGUI)
}
35 changes: 35 additions & 0 deletions kotlinx-coroutines-async/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<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.1-SNAPSHOT</version>
</parent>

<artifactId>kotlinx-coroutines-async</artifactId>
<packaging>jar</packaging>

<name>Kotlin async library based on CompletableFuture</name>

<properties>
<kotlin.version>1.1-SNAPSHOT</kotlin.version>
</properties>

<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
</build>

<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>
159 changes: 159 additions & 0 deletions kotlinx-coroutines-async/src/main/kotlin/async.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package kotlinx.coroutines

import java.net.SocketAddress
import java.nio.ByteBuffer
import java.nio.channels.AsynchronousFileChannel
import java.nio.channels.AsynchronousServerSocketChannel
import java.nio.channels.AsynchronousSocketChannel
import java.nio.channels.CompletionHandler
import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit
import javax.swing.SwingUtilities

/**
* Run asynchronous computations based on [c] coroutine parameter
*
* Execution starts immediately within the 'async' call and it runs until
* the first suspension point is reached ('await' call with some CompletableFuture).
* Remaining part of coroutine will be executed as it's passed into 'whenComplete'
* call of awaited Future.
*
* @param c a coroutine representing asynchronous computations
* @param continuationWrapper represents a function that wraps execution parts
* between subsequent 'await' calls.
* For example it could be 'SwingUtilities.invokeLater', providing ability to
* call UI-related methods between 'await' calls
*
* @return CompletableFuture object representing result of computations
*/
fun <T> async(
continuationWrapper: ContinuationWrapper? = null,
coroutine c: FutureController<T>.() -> Continuation<Unit>
): CompletableFuture<T> {
val controller = FutureController<T>(continuationWrapper)
c(controller).resume(Unit)
return controller.future
}

/**
* Run asynchronous computations based on [c] coroutine parameter.
* Continuation parts (everything besides awaited futures)
*
* @param c a coroutine representing asynchronous computations
*
* @return CompletableFuture object representing result of computations
* @See async
*/
fun asyncUi(
coroutine c: FutureController<Unit>.() -> Continuation<Unit>
) {
if (SwingUtilities.isEventDispatchThread()) {
async({ SwingUtilities.invokeLater(it) }, c)
}
else {
SwingUtilities.invokeLater {
async({ SwingUtilities.invokeLater(it) }, c)
}
}
}

typealias ContinuationWrapper = (() -> Unit) -> Unit

open class FutureController<T>(
private val continuationWrapper: ContinuationWrapper?
) {
val future = CompletableFuture<T>()

suspend fun <V> await(f: CompletableFuture<V>, machine: Continuation<V>) {
f.whenComplete { value, throwable ->
wrapContinuationIfNeeded {
if (throwable == null)
machine.resume(value)
else
machine.resumeWithException(throwable)
}
}
}

private fun wrapContinuationIfNeeded(block: () -> Unit) {
continuationWrapper?.invoke(block) ?: block()
}

operator fun handleResult(value: T, c: Continuation<Nothing>) {
future.complete(value)
}

operator fun handleException(t: Throwable, c: Continuation<Nothing>) {
future.completeExceptionally(t)
}

//
// IO parts
//
suspend fun AsynchronousFileChannel.aRead(
buf: ByteBuffer,
position: Long,
c: Continuation<Int>
) {
this.read(buf, position, null, AsyncIOHandler(c))
}

suspend fun AsynchronousFileChannel.aWrite(
buf: ByteBuffer,
position: Long,
c: Continuation<Int>
) {
this.write(buf, position, null, AsyncIOHandler(c))
}

suspend fun AsynchronousServerSocketChannel.aAccept(
c: Continuation<AsynchronousSocketChannel>
) {
this.accept(null, AsyncIOHandler(c))
}

suspend fun AsynchronousSocketChannel.aConnect(
socketAddress: SocketAddress,
c: Continuation<Unit>
) {
this.connect(socketAddress, null, AsyncVoidIOHandler(c))
}

suspend fun AsynchronousSocketChannel.aRead(
buf: ByteBuffer,
timeout: Long = 0L,
timeUnit: TimeUnit = TimeUnit.MILLISECONDS,
c: Continuation<Int>
) {
this.read(buf, timeout, timeUnit, null, AsyncIOHandler(c))
}

suspend fun AsynchronousSocketChannel.aWrite(
buf: ByteBuffer,
timeout: Long = 0L,
timeUnit: TimeUnit = TimeUnit.MILLISECONDS,
c: Continuation<Int>
) {
this.write(buf, timeout, timeUnit, null, AsyncIOHandler(c))
}

private class AsyncIOHandler<E>(val c: Continuation<E>) : CompletionHandler<E, Nothing?> {
override fun completed(result: E, attachment: Nothing?) {
c.resume(result)
}

override fun failed(exc: Throwable, attachment: Nothing?) {
c.resumeWithException(exc)
}
}

private class AsyncVoidIOHandler(val c: Continuation<Unit>) : CompletionHandler<Void?, Nothing?> {
override fun completed(result: Void?, attachment: Nothing?) {
c.resume(Unit)
}

override fun failed(exc: Throwable, attachment: Nothing?) {
c.resumeWithException(exc)
}
}
}
Loading

0 comments on commit 8e4e0e4

Please sign in to comment.