Add delivery executor in callbackinfo structs

Bug: b/374925431
Change-Id: I51954dd020d3ccb5345aa438ea1ef04fc2061583
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/262655
Reviewed-by: Loko Kung <lokokung@google.com>
Commit-Queue: Nikita Gupta <nikigupta@google.com>
Reviewed-by: Jim Blackler <jimblackler@google.com>
diff --git a/generator/templates/art/JNIClasses.cpp b/generator/templates/art/JNIClasses.cpp
index c92f237..e4a6612 100644
--- a/generator/templates/art/JNIClasses.cpp
+++ b/generator/templates/art/JNIClasses.cpp
@@ -36,6 +36,9 @@
     //* Time of lookups measured on Pixel 6 at 1.7ms.
     {% for entity in has_kotlin_classes %}
         {{ entity.name.camelCase() }} = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass("{{ jni_name(entity) }}")));
+        {%if entity.category == 'callback function' %}
+              {{ entity.name.camelCase() }}Runnable = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass("{{ jni_name(entity) }}Runnable")));
+        {%endif %}
     {% endfor %}
 }
 
diff --git a/generator/templates/art/JNIClasses.h b/generator/templates/art/JNIClasses.h
index 915320f..762b06a 100644
--- a/generator/templates/art/JNIClasses.h
+++ b/generator/templates/art/JNIClasses.h
@@ -38,8 +38,10 @@
          static JNIClasses* getInstance(JNIEnv* env);
         {% for entity in has_kotlin_classes %}
             jclass {{ entity.name.camelCase() }};
+            {%if entity.category == 'callback function' %}
+                jclass {{ entity.name.camelCase() }}Runnable;
+            {%endif %}
         {% endfor %}
-
     private:
         JNIClasses(JNIEnv *env);
 };
diff --git a/generator/templates/art/api_kotlin_function_pointer.kt b/generator/templates/art/api_kotlin_function_pointer.kt
index 143ef58..ff6a3b3 100644
--- a/generator/templates/art/api_kotlin_function_pointer.kt
+++ b/generator/templates/art/api_kotlin_function_pointer.kt
@@ -37,3 +37,21 @@
         {{ as_varName(arg.name) }}: {{ kotlin_declaration(arg) }},{{ ' ' }}
     {%- endfor -%});
 }
+
+{% set args_list = kotlin_record_members(function_pointer.arguments) | list %}
+
+internal class {{ function_pointer.name.CamelCase() }}Runnable(
+private val callback: {{ function_pointer.name.CamelCase() }},
+{% for arg in args_list %}
+    private val {{ as_varName(arg.name) }}: {{ kotlin_declaration(arg) }}{{ ','
+    if not loop.last }}
+{% endfor %}
+) : Runnable {
+    override fun run() {
+        callback.{{ callbackName }}(
+            {%- for arg in args_list -%}
+            {{ as_varName(arg.name) }}{{ ', ' if not loop.last }}
+            {%- endfor -%}
+        )
+    }
+}
diff --git a/generator/templates/art/api_kotlin_structure.kt b/generator/templates/art/api_kotlin_structure.kt
index 7b277e2..374e6e6 100644
--- a/generator/templates/art/api_kotlin_structure.kt
+++ b/generator/templates/art/api_kotlin_structure.kt
@@ -27,8 +27,17 @@
 package {{ kotlin_package }}
 {% from 'art/api_kotlin_types.kt' import kotlin_definition with context %}
 
+{% for member in kotlin_record_members(structure.members) %}
+    import java.util.concurrent.Executor
+    import java.util.concurrent.ForkJoinPool
+    {% break %}
+{% endfor %}
+
 public class {{ structure.name.CamelCase() }}(
     {% for member in kotlin_record_members(structure.members) %}
+        {% if member.type.category in ['callback function']%}
+            public var executor: Executor? = ForkJoinPool.commonPool(),
+        {% endif %}
         {# We supply a getter that is excluded from name mangling to allow Inline Value Classed
            enums/bitmasks to be accessible as integers from the JVM adapter layer. #}
         {% if member.type.category in ['bitmask', 'enum'] %}
diff --git a/generator/templates/art/kotlin_record_conversion.cpp b/generator/templates/art/kotlin_record_conversion.cpp
index 8302bf2..012b9b9 100644
--- a/generator/templates/art/kotlin_record_conversion.cpp
+++ b/generator/templates/art/kotlin_record_conversion.cpp
@@ -37,6 +37,9 @@
             {% else %}
                 {{ arg_to_jni_type(member) }} {{ as_varName(member.name) }};
             {% endif %}
+            {% if as_varName(member.name) == 'callback' %}
+                jobject executor;
+            {% endif %}
         {% endfor%}
     };
 {% endmacro %}
@@ -50,6 +53,9 @@
         {% for member in kotlin_record_members(members) %}
             {
                 auto& in = inStruct.{{member.name.camelCase()}};
+                {% if as_varName(member.name) == 'callback' %}
+                    auto& in_executor = inStruct.executor;
+                {% endif %}
                 auto& out = outStruct->{{member.name.camelCase()}};
                 {% if member.constant_length == 1 %}
                     {% if member.type.category == 'structure' %}
@@ -170,21 +176,30 @@
 
                         //* Get the client (Kotlin) callback so we can call it.
                         {% set callbackName = 'on' + member.type.name.chunks[:-1] | map('title') | join %}
-                        jmethodID callbackMethod = env->GetMethodID(
-                                classes->{{ member.type.name.camelCase() }}, "{{ callbackName }}", "(
+                        jclass executorClass = env->FindClass("java/util/concurrent/Executor");
+                        jmethodID executeMethodID = env->GetMethodID(executorClass,
+                                                                     "execute",
+                                                                     "(Ljava/lang/Runnable;)V");
+                        jmethodID methodId = env->GetMethodID(
+                            classes->{{ member.type.name.camelCase() }}Runnable,
+                            "<init>", "(L{{ jni_name(member.type) }};
                             {%- for callbackArg in kotlin_record_members(member.type.arguments) -%}
                                 {{- jni_signature(callbackArg) -}}
                             {%- endfor %})V");
 
-                        //* Call the callback with all converted parameters.
-                        env->CallVoidMethod(userData1->callback, callbackMethod
-                        {%- for callbackArg in kotlin_record_members(member.type.arguments) %}
-                             {{- ', ' }}_{{ callbackArg.name.camelCase() }}
-                        {%- endfor %});
+                        jobject runnable =
+                            env->NewObject(classes->{{ member.type.name.camelCase() }}Runnable,
+                                           methodId,
+                                           userData1->callback
+                                           {%- for callbackArg in kotlin_record_members(member.type.arguments) %}
+                                               {{- ', ' }}_{{ callbackArg.name.camelCase() }}
+                                           {%- endfor %});
+
+                        env->CallVoidMethod(userData1->executor, executeMethodID, runnable);
                     };
                     //* TODO(b/330293719): free associated resources.
                     outStruct->{{ userdata }} = new UserData(
-                            {.callback = env->NewGlobalRef(in), .jvm = c->jvm});
+                        {.callback = env->NewGlobalRef(in), .executor = inStruct.executor, .jvm = c->jvm});
 
                 {% else %}
                     {{ unreachable_code() }}
diff --git a/generator/templates/art/structures.cpp b/generator/templates/art/structures.cpp
index 5ef2dfd..0373164 100644
--- a/generator/templates/art/structures.cpp
+++ b/generator/templates/art/structures.cpp
@@ -170,6 +170,10 @@
                 CallGetter(env, getter, obj, &kotlinRecord.{{as_varName(member.name)}});
             }
         {% endfor %}
+        {% if structure.category == 'callback info' %}
+                jmethodID getter = env->GetMethodID(clz, "getExecutor", "()Ljava/util/concurrent/Executor;");
+                CallGetter(env, getter, obj, &kotlinRecord.executor);
+        {% endif %}
 
         //* Fill all struct members from the Kotlin record.
         ConvertInternal(c, kotlinRecord, converted);
diff --git a/generator/templates/art/structures.h b/generator/templates/art/structures.h
index b7fcce4..ae8fe10 100644
--- a/generator/templates/art/structures.h
+++ b/generator/templates/art/structures.h
@@ -33,6 +33,7 @@
 
 struct UserData {
     jobject callback;
+    jobject executor;
     JavaVM *jvm;
 };