From 3e0ddd9fbfa6b37aea8019b9ea31040518b4f955 Mon Sep 17 00:00:00 2001 From: LTFan Date: Sat, 20 Apr 2024 00:41:28 +0800 Subject: [PATCH] change exceptions handling --- client-py/src/nativeMain/kotlin/Callbacks.kt | 5 ++- client-py/src/nativeMain/kotlin/Client.kt | 40 ++++++++++++-------- client-py/src/nativeMain/kotlin/Logger.kt | 4 +- client-py/src/nativeMain/kotlin/Throwable.kt | 26 ++++++++----- 4 files changed, 48 insertions(+), 27 deletions(-) diff --git a/client-py/src/nativeMain/kotlin/Callbacks.kt b/client-py/src/nativeMain/kotlin/Callbacks.kt index 9c01b53..93f8d1b 100644 --- a/client-py/src/nativeMain/kotlin/Callbacks.kt +++ b/client-py/src/nativeMain/kotlin/Callbacks.kt @@ -21,7 +21,10 @@ package xyz.xfqlittlefan.fhraise.py import kotlinx.cinterop.* @ExperimentalForeignApi -typealias OnError = CPointer Unit>> +typealias OnError = CPointer Unit>> @ExperimentalForeignApi typealias OnMessage = CPointer, ref: COpaquePointer) -> CPointer<*>>> + +@ExperimentalForeignApi +typealias OnClose = CPointer Unit>> diff --git a/client-py/src/nativeMain/kotlin/Client.kt b/client-py/src/nativeMain/kotlin/Client.kt index bb0726c..7bd2daa 100644 --- a/client-py/src/nativeMain/kotlin/Client.kt +++ b/client-py/src/nativeMain/kotlin/Client.kt @@ -31,10 +31,8 @@ import kotlin.experimental.ExperimentalNativeApi class Client(private val host: String, private val port: UShort) { private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default) - private val messageChannel = Channel(3) + private val messageChannel = Channel(3) - @OptIn(ExperimentalForeignApi::class) - private val messageErrorChannel = Channel(3) private val resultChannel = Channel(3) @OptIn(ExperimentalSerializationApi::class) @@ -45,20 +43,33 @@ class Client(private val host: String, private val port: UShort) { } @OptIn(ExperimentalForeignApi::class, ExperimentalNativeApi::class) - fun connect(onError: OnError) = runBlocking { + fun connect(onError: OnError, onClose: OnClose) = runBlocking { logger.debug("Connecting to $host:$port.") suspendCancellableCoroutine { continuation -> logger.debug("Launching coroutine.") scope.launch(Dispatchers.IO) { - runCatching(onError) { + runCatchingC(onError) { logger.debug("Starting connection.") client.webSocket(host = host, port = port.toInt(), path = pyWsPath) { continuation.resume(true) logger.debug("Connected.") while (true) { + if (!isActive) { + logger.info("Connection closed.") + onClose() + break + } logger.debug("Waiting for message...") - val receiveResult = - runCatching(onError) { messageChannel.send(receiveDeserialized()) } + val receiveResult = runCatching { + val message = receiveDeserialized() + logger.debug("Received message.") + messageChannel.send(message) + }.onFailure { throwable -> + throwable.logger.debug("Caught throwable. Callback address: ${onError.rawValue}.") + throwable.cThrowable { + onError(it) + } + } if (receiveResult.isFailure) continue logger.debug("Waiting for result...") sendSerialized(resultChannel.receive()) @@ -80,21 +91,18 @@ class Client(private val host: String, private val port: UShort) { @OptIn(ExperimentalForeignApi::class, ExperimentalNativeApi::class) fun receive(onMessage: OnMessage, onError: OnError): Boolean { val message = runBlocking { messageChannel.receive() } - if (message == null) { - return false - } + + logger.debug("Sending message to C.") return runBlocking { - runCatching(onError) { + val ref = StableRef.create(message) + runCatchingC(onError) { memScoped { val type = message::class.qualifiedName!!.cstr.ptr - val ref = StableRef.create(message) - resultChannel.send(onMessage(type, ref.asCPointer()).asStableRef().get()) - - ref.dispose() + logger.debug("Received result from C.") } - }.isSuccess + }.isSuccess.also { ref.dispose() } } } } diff --git a/client-py/src/nativeMain/kotlin/Logger.kt b/client-py/src/nativeMain/kotlin/Logger.kt index a2940b2..7cc5893 100644 --- a/client-py/src/nativeMain/kotlin/Logger.kt +++ b/client-py/src/nativeMain/kotlin/Logger.kt @@ -19,6 +19,8 @@ package xyz.xfqlittlefan.fhraise.py import kotlinx.datetime.Clock +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toLocalDateTime class Logger internal constructor(private val tag: Any) { @Deprecated("This constructor is for calling from C code only.", level = DeprecationLevel.HIDDEN) @@ -42,7 +44,7 @@ class Logger internal constructor(private val tag: Any) { private fun println(level: String, message: String) { message.split("\n").forEach { - println("${Clock.System.now()} [$tag] $level: $it") + println("${Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault())} [$tag] $level: $it") } } } diff --git a/client-py/src/nativeMain/kotlin/Throwable.kt b/client-py/src/nativeMain/kotlin/Throwable.kt index aeba08e..5e4519b 100644 --- a/client-py/src/nativeMain/kotlin/Throwable.kt +++ b/client-py/src/nativeMain/kotlin/Throwable.kt @@ -59,27 +59,35 @@ class ThrowableVar(rawPtr: NativePtr) : CStructVar(rawPtr) { @ExperimentalNativeApi @ExperimentalForeignApi -internal inline fun Throwable.sendToC(block: (ThrowableVar) -> R) = memScoped { - logger.debug("Wrapping throwable.") - logger.warn(getStackTrace().joinToString("\n")) +internal inline fun Throwable.cThrowable(block: (ThrowableVar) -> R) = memScoped { + this@cThrowable.logger.debug("Sending throwable to C.") val throwable = alloc() val ref = StableRef.create(this) throwable.type = this::class.qualifiedName?.cstr?.ptr throwable.ref = ref.asCPointer() - throwable.message = this@sendToC.message?.cstr?.ptr - val stacktraceList = this@sendToC.getStackTrace().map { it.cstr.ptr.pointed } + throwable.message = this@cThrowable.message?.cstr?.ptr + val stacktraceList = this@cThrowable.getStackTrace().map { it.cstr.ptr.pointed } val stacktraceArray = allocArrayOfPointersTo(stacktraceList) throwable.stacktrace = stacktraceArray throwable.stacktraceSize = stacktraceList.size - block(throwable).also { ref.dispose() } + block(throwable).also { + ref.dispose() + this@cThrowable.logger.debug("Throwable sent.") + } } @ExperimentalNativeApi @ExperimentalForeignApi -internal inline fun runCatching( - onError: OnError, block: () -> R +internal inline fun runCatchingC( + onError: (ThrowableVar) -> E, block: () -> R ) = runCatching(block).onFailure { throwable -> - throwable.sendToC { onError(it) } + throwable.cThrowable(onError) } + +@ExperimentalNativeApi +@ExperimentalForeignApi +internal inline fun runCatchingC( + onError: OnError, block: () -> R +) = runCatchingC({ onError(it) }, block)