summaryrefslogtreecommitdiffstats
path: root/third_party/rust/uniffi_bindgen/src/bindings/kotlin/templates/ForeignExecutorTemplate.kt
blob: 3544b2f9e6ecc1857b57a56b7d2fabb3f7c93f33 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
{{ self.add_import("kotlinx.coroutines.CoroutineScope") }}
{{ self.add_import("kotlinx.coroutines.delay") }}
{{ self.add_import("kotlinx.coroutines.isActive") }}
{{ self.add_import("kotlinx.coroutines.launch") }}

internal const val UNIFFI_RUST_TASK_CALLBACK_SUCCESS = 0.toByte()
internal const val UNIFFI_RUST_TASK_CALLBACK_CANCELLED = 1.toByte()
internal const val UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS = 0.toByte()
internal const val UNIFFI_FOREIGN_EXECUTOR_CALLBACK_CANCELLED = 1.toByte()
internal const val UNIFFI_FOREIGN_EXECUTOR_CALLBACK_ERROR = 2.toByte()

// Callback function to execute a Rust task.  The Kotlin code schedules these in a coroutine then
// invokes them.
internal interface UniFfiRustTaskCallback : com.sun.jna.Callback {
    fun callback(rustTaskData: Pointer?, statusCode: Byte)
}

internal object UniFfiForeignExecutorCallback : com.sun.jna.Callback {
    fun callback(handle: USize, delayMs: Int, rustTask: UniFfiRustTaskCallback?, rustTaskData: Pointer?) : Byte {
        if (rustTask == null) {
            FfiConverterForeignExecutor.drop(handle)
            return UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS
        } else {
            val coroutineScope = FfiConverterForeignExecutor.lift(handle)
            if (coroutineScope.isActive) {
                val job = coroutineScope.launch {
                    if (delayMs > 0) {
                        delay(delayMs.toLong())
                    }
                    rustTask.callback(rustTaskData, UNIFFI_RUST_TASK_CALLBACK_SUCCESS)
                }
                job.invokeOnCompletion { cause ->
                    if (cause != null) {
                        rustTask.callback(rustTaskData, UNIFFI_RUST_TASK_CALLBACK_CANCELLED)
                    }
                }
                return UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS
            } else {
                return UNIFFI_FOREIGN_EXECUTOR_CALLBACK_CANCELLED
            }
        }
    }
}

public object FfiConverterForeignExecutor: FfiConverter<CoroutineScope, USize> {
    internal val handleMap = UniFfiHandleMap<CoroutineScope>()

    internal fun drop(handle: USize) {
        handleMap.remove(handle)
    }

    internal fun register(lib: _UniFFILib) {
        {%- match ci.ffi_foreign_executor_callback_set() %}
        {%- when Some with (fn) %}
        lib.{{ fn.name() }}(UniFfiForeignExecutorCallback)
        {%- when None %}
        {#- No foreign executor, we don't set anything #}
        {% endmatch %}
    }

    // Number of live handles, exposed so we can test the memory management
    public fun handleCount() : Int {
        return handleMap.size
    }

    override fun allocationSize(value: CoroutineScope) = USize.size

    override fun lift(value: USize): CoroutineScope {
        return handleMap.get(value) ?: throw RuntimeException("unknown handle in FfiConverterForeignExecutor.lift")
    }

    override fun read(buf: ByteBuffer): CoroutineScope {
        return lift(USize.readFromBuffer(buf))
    }

    override fun lower(value: CoroutineScope): USize {
        return handleMap.insert(value)
    }

    override fun write(value: CoroutineScope, buf: ByteBuffer) {
        lower(value).writeToBuffer(buf)
    }
}