10. Async CompletableFuture bridge
The problem
CompletableFuture<T> is the standard Java async primitive. It completes on a Java thread pool thread, not on the calling goroutine. The bridge must deliver the completion value to Go code without blocking a JVM thread indefinitely or requiring Go code to busy-poll.
FutureRuntime callback registry
jni.FutureRuntime is a registry that maps int64 handles to FutureCallback functions. When a Mochi caller invokes a method that returns a CompletableFuture<T>, the bridge:
- Allocates a
FutureCallbackclosure that will send the value to a Go channel. - Registers the closure in
FutureRuntime, receiving anint64handle. - Passes the handle to the wrapper Java method.
Java-side wiring
The generated wrapper Java method adds a thenAccept completion handler:
future.thenAccept(v -> MochiRuntime.callback(cbHandle, v.toString()));
MochiRuntime.callback is a JNI native method declared in the wrapper class. When the future completes, the Java thread pool invokes this native method.
AsyncPolicy
AsyncPolicy selects the Go-side dispatch strategy:
AsyncBlocking: the calling goroutine blocks on a channel until the callback fires.AsyncGoroutine: a new goroutine is launched; the callback sends to a buffered channel the caller selects from.
AsyncBlocking is simpler but holds a goroutine for the duration of the Java operation. AsyncGoroutine is preferred for operations that may complete after a significant delay.
Goroutine safety
The JNI native callback fires on a JVM thread, which is not a goroutine. CGO runtime handles the transition (runtime.LockOSThread is called inside the CGO dispatch). The FutureRuntime.Invoke method dispatches the Go callback in a new goroutine to avoid executing Go code on a JVM-owned thread stack beyond what CGO allows.