Skip to content

[Java] Add in-process FFI runtime host lifecycle and stream transport primitives - #2233

Merged
edburns merged 3 commits into
edburns/1917-java-embed-rust-cli-runtime-dd-3039924-agentic-run-02from
copilot/edburns1917-java-embed-rust-cli-runtime-dd-3039924
Aug 3, 2026
Merged

[Java] Add in-process FFI runtime host lifecycle and stream transport primitives#2233
edburns merged 3 commits into
edburns/1917-java-embed-rust-cli-runtime-dd-3039924-agentic-run-02from
copilot/edburns1917-java-embed-rust-cli-runtime-dd-3039924

Conversation

Copilot AI commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

This PR implements task 4.5 of the Java embedded runtime plan: a dedicated FfiRuntimeHost that owns the full in-process FFI lifecycle and bridges runtime I/O through Java streams compatible with JsonRpcClient. It also adds the MR-JAR thread-factory swap point and focused tests for callback, shutdown, and concurrency behavior.

  • FFI runtime host lifecycle (com.github.copilot.ffi)

    • Added FfiRuntimeHost to manage:
      • host_start (on a dedicated blocking thread)
      • connection_open with outbound callback registration
      • graceful shutdown: closing flag → connection_close → callback drain → host_shutdown
    • Implements AutoCloseable with best-effort teardown and non-throwing close().
    • Contains callback error containment (Throwable catch + WARNING logging) and callback exception handler registration.
  • Transport stream bridge

    • Added QueueInputStream (BlockingQueue<byte[]>-backed) for native callback → InputStream delivery.
    • Added FfiOutputStream for OutputStreamconnection_write, including write/close race protection via shared operation lock and closed-state checks.
  • MR-JAR reader thread factory

    • Added baseline ReaderThreadFactory (src/main/java/.../ffi) using daemon platform threads.
    • Added JDK 25 overlay ReaderThreadFactory (src/main/java25/.../ffi) using Thread.ofVirtual().
  • JSON-RPC integration seam

    • Added JsonRpcClient.fromStreams(InputStream, OutputStream) factory so in-process transport can reuse existing JSON-RPC framing/reader logic.
  • Focused tests

    • Added QueueInputStreamTest for chunked read semantics, blocking behavior, and EOF/close behavior.
    • Added FfiRuntimeHostTest for lifecycle, callback delivery, shutdown drain behavior, callback exception containment, and write/close concurrency safety (including spike test library integration path).
// New direct stream wiring path for in-process transport
JsonRpcClient rpc = JsonRpcClient.fromStreams(
    ffiHost.getReceiveStream(),
    ffiHost.getSendStream()
);

Co-authored-by: edburns <75821+edburns@users.noreply.github.com>
Copilot AI changed the title [WIP] Create FfiRuntimeHost and stream classes for FFI lifecycle [Java] Add in-process FFI runtime host lifecycle and stream transport primitives Aug 3, 2026
Copilot AI requested a review from edburns August 3, 2026 18:06
@github-actions

This comment has been minimized.

@edburns
edburns marked this pull request as ready for review August 3, 2026 18:24
@edburns
edburns requested a review from a team as a code owner August 3, 2026 18:24
Copilot AI review requested due to automatic review settings August 3, 2026 18:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Java in-process FFI lifecycle management and stream-based JSON-RPC transport primitives.

Changes:

  • Adds FFI host startup, callback handling, I/O, and shutdown.
  • Adds queue-backed streams and MR-JAR thread factories.
  • Adds focused lifecycle, concurrency, and stream tests.
Show a summary per file
File Description
java/sdk/src/main/java/com/github/copilot/JsonRpcClient.java Adds stream-based client construction.
java/sdk/src/main/java/com/github/copilot/ffi/FfiRuntimeHost.java Manages native runtime lifecycle.
java/sdk/src/main/java/com/github/copilot/ffi/FfiOutputStream.java Bridges writes to native connections.
java/sdk/src/main/java/com/github/copilot/ffi/QueueInputStream.java Queues native callback data for reading.
java/sdk/src/main/java/com/github/copilot/ffi/ReaderThreadFactory.java Provides the JDK 17 thread implementation.
java/sdk/src/main/java25/com/github/copilot/ffi/ReaderThreadFactory.java Provides the JDK 25 virtual-thread overlay.
java/sdk/src/test/java/com/github/copilot/ffi/FfiRuntimeHostTest.java Tests lifecycle, callbacks, and concurrency.
java/sdk/src/test/java/com/github/copilot/ffi/QueueInputStreamTest.java Tests queue-backed stream behavior.

Review details

Suppressed comments (3)

java/sdk/src/main/java/com/github/copilot/ffi/FfiRuntimeHost.java:123

  • Rollback only handles a returned zero. If connectionOpen throws (for example, a JNA invocation failure), start() exits while serverId and callbackRef remain live and hostShutdown is never called, leaking the native worker. Apply the same best-effort rollback for every unsuccessful open, including exceptions.
        OutboundCallback callback = createOutboundCallback();
        callbackRef = callback;
        int connHandle = nativeBinding.connectionOpen(hostHandle, callback, Pointer.NULL, null, 0, null, 0, null, 0);
        if (connHandle == 0) {
            try {

java/sdk/src/main/java/com/github/copilot/ffi/FfiRuntimeHost.java:232

  • ReaderThreadFactory is only used for host_start here, so the JDK 25 overlay virtualizes the startup call rather than the QueueInputStream consumer. The actual consumer still comes from the hard-coded platform thread in JsonRpcClient.java:60-64, so the promised MR-JAR reader swap never takes effect. Wire this factory into JSON-RPC reader creation and use a separate blocking executor for host_start.
        ReaderThreadFactory readerThreadFactory = new ReaderThreadFactory();
        ExecutorService executor = Executors
                .newSingleThreadExecutor(runnable -> readerThreadFactory.create(runnable, "copilot-ffi-host-start"));

java/sdk/src/main/java/com/github/copilot/ffi/FfiRuntimeHost.java:197

  • An interrupt currently abandons the drain, after which close() calls hostShutdown and releases the callback reference even though callbacks are still active. That can free native callback state while Java is still using it. Drain uninterruptibly and restore the interrupt status only after the active count reaches zero.
                try {
                    callbackDrainMonitor.wait(10L);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    return;
  • Files reviewed: 8/8 changed files
  • Comments generated: 3
  • Review effort level: Balanced

Comment thread java/sdk/src/main/java/com/github/copilot/ffi/FfiRuntimeHost.java
Comment thread java/sdk/src/main/java/com/github/copilot/JsonRpcClient.java Outdated
@useher84u2

useher84u2 commented Aug 3, 2026 via email

Copy link
Copy Markdown

1. FfiRuntimeHost: serialize start() with close() via operationLock to
   prevent race where close() completes while hostStart blocks, then
   start() publishes handles after teardown.

2. FfiRuntimeHostTest: add Javadoc explaining the native integration
   test is intentionally skipped in CI and how to run it locally.

3. JsonRpcClient.fromStreams(): take ownership of supplied streams and
   close them in close() to prevent resource leaks.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Cross-SDK Consistency Review ✅

This PR improves cross-SDK consistency by bringing Java up to parity with all other SDK implementations.

Summary of findings:

The FfiRuntimeHost and associated in-process FFI transport primitives (QueueInputStream, FfiOutputStream, ReaderThreadFactory) added in this PR already exist in all other SDK implementations:

SDK FfiRuntimeHost
Node.js nodejs/src/ffiRuntimeHost.ts
Python python/copilot/_ffi_runtime_host.py
.NET dotnet/src/FfiRuntimeHost.cs
Go go/internal/ffihost/ffihost.go
Rust rust/src/ffi.rs
Java Added by this PR 🆕

The JsonRpcClient.fromStreams(InputStream, OutputStream) factory is an internal (package-private) seam that enables the FFI host to reuse existing JSON-RPC framing — it's an implementation detail not exposed in the public API surface, so there's no cross-SDK concern there.

No consistency gaps identified. This PR closes the Java parity gap on in-process FFI hosting.

Generated by SDK Consistency Review Agent for #2233 · sonnet46 32.5 AIC · ⌖ 5.41 AIC · ⊞ 6.6K ·

@edburns
edburns merged commit cc0084e into edburns/1917-java-embed-rust-cli-runtime-dd-3039924-agentic-run-02 Aug 3, 2026
26 checks passed
@edburns
edburns deleted the copilot/edburns1917-java-embed-rust-cli-runtime-dd-3039924 branch August 3, 2026 19:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Java] Embed Rust CLI runtime 4.5: FFI runtime host and transport streams

4 participants