Move everything under kotlin-native folder

I was forced to manually do update the following files, because otherwise
they would be ignored according .gitignore settings. Probably they
should be deleted from repo.

Interop/.idea/compiler.xml
Interop/.idea/gradle.xml
Interop/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_runtime_1_0_3.xml
Interop/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_0_3.xml
Interop/.idea/modules.xml
Interop/.idea/modules/Indexer/Indexer.iml
Interop/.idea/modules/Runtime/Runtime.iml
Interop/.idea/modules/StubGenerator/StubGenerator.iml
backend.native/backend.native.iml
backend.native/bc.frontend/bc.frontend.iml
backend.native/cli.bc/cli.bc.iml
backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt
backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt
backend.native/tests/link/lib/foo.kt
backend.native/tests/link/lib/foo2.kt
backend.native/tests/teamcity-test.property
This commit is contained in:
Stanislav Erokhin
2020-10-27 21:00:28 +03:00
parent 91e4162dad
commit f624800b84
2830 changed files with 0 additions and 0 deletions
@@ -0,0 +1,25 @@
# Non-blocking echo server demo
This sample shows how to implement multi-client server using coroutines.
IO operations are implemented using non-blocking OS calls, and instead coroutines
are being suspended and resumed whenever relevant.
Thus, while server can process multiple connections concurrently,
each individual connection handler is written in simple linear manner.
To build use `../gradlew assemble`.
To run use `../gradlew runReleaseExecutableNonBlockingEchoServer` or execute the program directly:
./build/bin/nonBlockingEchoServer/main/release/executable/nonBlockingEchoServer.kexe 3000 &
Test the server by connecting to it, for example with telnet:
telnet localhost 3000
Write something to console and watch server echoing it back.
Concurrently connect from another terminal. Note that each connection gets its own
connection id prefixed to echo response.
~~Quit telnet by pressing ctrl+] ctrl+D~~
@@ -0,0 +1,25 @@
plugins {
kotlin("multiplatform")
}
kotlin {
// Determine host preset.
val hostOs = System.getProperty("os.name")
// Create target for the host platform.
val hostTarget = when {
hostOs == "Mac OS X" -> macosX64("nonBlockingEchoServer")
hostOs == "Linux" -> linuxX64("nonBlockingEchoServer")
hostOs.startsWith("Windows") -> mingwX64("nonBlockingEchoServer")
else -> throw GradleException("Host OS '$hostOs' is not supported in Kotlin/Native $project.")
}
hostTarget.apply {
binaries {
executable {
entryPoint = "sample.nbechoserver.main"
runTask?.args(3000)
}
}
}
}
@@ -0,0 +1,2 @@
kotlin.code.style=official
kotlin.import.noCommonSourceSets=true
@@ -0,0 +1,214 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package sample.nbechoserver
import kotlinx.cinterop.*
import platform.posix.*
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
fun main(args: Array<String>) {
if (args.isEmpty()) {
println("Usage: nonBlockingEchoServer.kexe <port>")
return
}
val port = args[0].toShort()
memScoped {
val serverAddr = alloc<sockaddr_in>()
val listenFd = socket(AF_INET, SOCK_STREAM, 0)
.ensureUnixCallResult { !it.isMinusOne() }
with(serverAddr) {
memset(this.ptr, 0, sockaddr_in.size.convert())
sin_family = AF_INET.convert()
sin_addr.s_addr = posix_htons(0).convert()
sin_port = posix_htons(port).convert()
}
bind(listenFd, serverAddr.ptr.reinterpret(), sockaddr_in.size.toUInt())
.ensureUnixCallResult { it == 0 }
fcntl(listenFd, F_SETFL, O_NONBLOCK)
.ensureUnixCallResult { it == 0 }
listen(listenFd, 10)
.ensureUnixCallResult { it == 0 }
var connectionId = 0
acceptClientsAndRun(listenFd) {
memScoped {
val bufferLength = 100uL
val buffer = allocArray<ByteVar>(bufferLength.toLong())
val connectionIdString = "#${++connectionId}: ".cstr
val connectionIdBytes = connectionIdString.ptr
try {
while (true) {
val length = read(buffer, bufferLength)
if (length == 0uL)
break
write(connectionIdBytes, connectionIdString.size.toULong())
write(buffer, length)
}
} catch (e: IOException) {
println("I/O error occured: ${e.message}")
}
}
}
}
}
sealed class WaitingFor {
class Accept : WaitingFor()
class Read(val data: CArrayPointer<ByteVar>,
val length: ULong,
val continuation: Continuation<ULong>) : WaitingFor()
class Write(val data: CArrayPointer<ByteVar>,
val length: ULong,
val continuation: Continuation<Unit>) : WaitingFor()
}
class Client(val clientFd: Int, val waitingList: MutableMap<Int, WaitingFor>) {
suspend fun read(data: CArrayPointer<ByteVar>, dataLength: ULong): ULong {
val length = read(clientFd, data, dataLength)
if (length >= 0)
return length.toULong()
if (posix_errno() != EWOULDBLOCK)
throw IOException(getUnixError())
// Save continuation and suspend.
return suspendCoroutine { continuation ->
waitingList.put(clientFd, WaitingFor.Read(data, dataLength, continuation))
}
}
suspend fun write(data: CArrayPointer<ByteVar>, length: ULong) {
val written = write(clientFd, data, length)
if (written >= 0)
return
if (posix_errno() != EWOULDBLOCK)
throw IOException(getUnixError())
// Save continuation and suspend.
return suspendCoroutine { continuation ->
waitingList.put(clientFd, WaitingFor.Write(data, length, continuation))
}
}
}
open class EmptyContinuation(override val context: CoroutineContext = EmptyCoroutineContext) : Continuation<Any?> {
companion object : EmptyContinuation()
override fun resumeWith(result: Result<Any?>) { result.getOrThrow() }
}
fun acceptClientsAndRun(serverFd: Int, block: suspend Client.() -> Unit) {
memScoped {
val waitingList = mutableMapOf<Int, WaitingFor>(serverFd to WaitingFor.Accept())
val readfds = alloc<fd_set>()
val writefds = alloc<fd_set>()
val errorfds = alloc<fd_set>()
var maxfd = serverFd
while (true) {
posix_FD_ZERO(readfds.ptr)
posix_FD_ZERO(writefds.ptr)
posix_FD_ZERO(errorfds.ptr)
for ((socketFd, watingFor) in waitingList) {
when (watingFor) {
is WaitingFor.Accept -> posix_FD_SET(socketFd, readfds.ptr)
is WaitingFor.Read -> posix_FD_SET(socketFd, readfds.ptr)
is WaitingFor.Write -> posix_FD_SET(socketFd, writefds.ptr)
}
posix_FD_SET(socketFd, errorfds.ptr)
}
pselect(maxfd + 1, readfds.ptr, writefds.ptr, errorfds.ptr, null, null)
.ensureUnixCallResult { it >= 0 }
loop@for (socketFd in 0..maxfd) {
val waitingFor = waitingList[socketFd]
val errorOccured = posix_FD_ISSET(socketFd, errorfds.ptr) != 0
if (posix_FD_ISSET(socketFd, readfds.ptr) != 0
|| posix_FD_ISSET(socketFd, writefds.ptr) != 0
|| errorOccured) {
when (waitingFor) {
is WaitingFor.Accept -> {
if (errorOccured)
throw Error("Socket has been closed externally")
// Accept new client.
val clientFd = accept(serverFd, null, null)
if (clientFd.isMinusOne()) {
if (posix_errno() != EWOULDBLOCK)
throw Error(getUnixError())
break@loop
}
fcntl(clientFd, F_SETFL, O_NONBLOCK)
.ensureUnixCallResult { it == 0 }
if (maxfd < clientFd)
maxfd = clientFd
block.startCoroutine(Client(clientFd, waitingList), EmptyContinuation)
}
is WaitingFor.Read -> {
if (errorOccured)
waitingFor.continuation.resumeWithException(IOException("Connection was closed by peer"))
// Resume reading operation.
waitingList.remove(socketFd)
val length = read(socketFd, waitingFor.data, waitingFor.length)
if (length < 0) // Read error.
waitingFor.continuation.resumeWithException(IOException(getUnixError()))
waitingFor.continuation.resume(length.toULong())
}
is WaitingFor.Write -> {
if (errorOccured)
waitingFor.continuation.resumeWithException(IOException("Connection was closed by peer"))
// Resume writing operation.
waitingList.remove(socketFd)
val written = write(socketFd, waitingFor.data, waitingFor.length)
if (written < 0) // Write error.
waitingFor.continuation.resumeWithException(IOException(getUnixError()))
waitingFor.continuation.resume(Unit)
}
}
}
}
}
}
}
class IOException(message: String): RuntimeException(message)
fun getUnixError() = strerror(posix_errno())!!.toKString()
inline fun Int.ensureUnixCallResult(predicate: (Int) -> Boolean): Int {
if (!predicate(this)) {
throw Error(getUnixError())
}
return this
}
inline fun Long.ensureUnixCallResult(predicate: (Long) -> Boolean): Long {
if (!predicate(this)) {
throw Error(getUnixError())
}
return this
}
inline fun ULong.ensureUnixCallResult(predicate: (ULong) -> Boolean): ULong {
if (!predicate(this)) {
throw Error(getUnixError())
}
return this
}
private fun Int.isMinusOne() = (this == -1)
private fun Long.isMinusOne() = (this == -1L)
private fun ULong.isMinusOne() = (this == ULong.MAX_VALUE)