// Async return type handlers internal const val UNIFFI_RUST_FUTURE_POLL_READY = 0.toByte() internal const val UNIFFI_RUST_FUTURE_POLL_MAYBE_READY = 1.toByte() internal val uniffiContinuationHandleMap = UniffiHandleMap>() // FFI type for Rust future continuations internal object uniffiRustFutureContinuationCallbackImpl: UniffiRustFutureContinuationCallback { override fun callback(data: Long, pollResult: Byte) { uniffiContinuationHandleMap.remove(data).resume(pollResult) } } internal suspend fun uniffiRustCallAsync( rustFuture: Long, pollFunc: (Long, UniffiRustFutureContinuationCallback, Long) -> Unit, completeFunc: (Long, UniffiRustCallStatus) -> F, freeFunc: (Long) -> Unit, liftFunc: (F) -> T, errorHandler: UniffiRustCallStatusErrorHandler ): T { try { do { val pollResult = suspendCancellableCoroutine { continuation -> pollFunc( rustFuture, uniffiRustFutureContinuationCallbackImpl, uniffiContinuationHandleMap.insert(continuation) ) } } while (pollResult != UNIFFI_RUST_FUTURE_POLL_READY); return liftFunc( uniffiRustCallWithError(errorHandler, { status -> completeFunc(rustFuture, status) }) ) } finally { freeFunc(rustFuture) } } {%- if ci.has_async_callback_interface_definition() %} internal inline fun uniffiTraitInterfaceCallAsync( crossinline makeCall: suspend () -> T, crossinline handleSuccess: (T) -> Unit, crossinline handleError: (UniffiRustCallStatus.ByValue) -> Unit, ): UniffiForeignFuture { // Using `GlobalScope` is labeled as a "delicate API" and generally discouraged in Kotlin programs, since it breaks structured concurrency. // However, our parent task is a Rust future, so we're going to need to break structure concurrency in any case. // // Uniffi does its best to support structured concurrency across the FFI. // If the Rust future is dropped, `uniffiForeignFutureFreeImpl` is called, which will cancel the Kotlin coroutine if it's still running. @OptIn(DelicateCoroutinesApi::class) val job = GlobalScope.launch { try { handleSuccess(makeCall()) } catch(e: Exception) { handleError( UniffiRustCallStatus.create( UNIFFI_CALL_UNEXPECTED_ERROR, {{ Type::String.borrow()|lower_fn }}(e.toString()), ) ) } } val handle = uniffiForeignFutureHandleMap.insert(job) return UniffiForeignFuture(handle, uniffiForeignFutureFreeImpl) } internal inline fun uniffiTraitInterfaceCallAsyncWithError( crossinline makeCall: suspend () -> T, crossinline handleSuccess: (T) -> Unit, crossinline handleError: (UniffiRustCallStatus.ByValue) -> Unit, crossinline lowerError: (E) -> RustBuffer.ByValue, ): UniffiForeignFuture { // See uniffiTraitInterfaceCallAsync for details on `DelicateCoroutinesApi` @OptIn(DelicateCoroutinesApi::class) val job = GlobalScope.launch { try { handleSuccess(makeCall()) } catch(e: Exception) { if (e is E) { handleError( UniffiRustCallStatus.create( UNIFFI_CALL_ERROR, lowerError(e), ) ) } else { handleError( UniffiRustCallStatus.create( UNIFFI_CALL_UNEXPECTED_ERROR, {{ Type::String.borrow()|lower_fn }}(e.toString()), ) ) } } } val handle = uniffiForeignFutureHandleMap.insert(job) return UniffiForeignFuture(handle, uniffiForeignFutureFreeImpl) } internal val uniffiForeignFutureHandleMap = UniffiHandleMap() internal object uniffiForeignFutureFreeImpl: UniffiForeignFutureFree { override fun callback(handle: Long) { val job = uniffiForeignFutureHandleMap.remove(handle) if (!job.isCompleted) { job.cancel() } } } // For testing public fun uniffiForeignFutureHandleCount() = uniffiForeignFutureHandleMap.size {%- endif %}