Skip to content

feat(java): add Panama FFM fast-path for hot byte ops#358

Merged
pratyush618 merged 8 commits into
masterfrom
feat/java-ffm-fastpath
Jul 2, 2026
Merged

feat(java): add Panama FFM fast-path for hot byte ops#358
pratyush618 merged 8 commits into
masterfrom
feat/java-ffm-fastpath

Conversation

@pratyush618

@pratyush618 pratyush618 commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

What

Adds a Project Panama (FFM) fast-path for the hot native byte ops — enqueue, enqueueMany, getResult — selected automatically on JDK 22+, with JNI as the default and fallback. Perf-only and additive: the public floor stays Java 17 and every other native call keeps using JNI.

How

  • internal.NativeTransport seam over the three hot ops. create(handle) reflectively resolves the FFM overlay on JDK 22+ (via the multi-release jar) and falls back to JniTransport on any ReflectiveOperationException/LinkageError — the seam can never break the 17 floor.
  • FfmTransport (overlay, src/main/java22) calls new taskito_ffi_* C-ABI exports through java.lang.foreign: confined Arena per call, off-heap args, results copied out then freed via taskito_ffi_free. Batch payloads/ids cross as one length-prefixed LE frame.
  • Rust ffi_c.rs#[no_mangle] extern "C" exports parallel to the jni-rs surface, sharing one cdylib and the same QueueHandle. Every body is panic-guarded (catch_unwind) so a panic never unwinds across FFI; each returns an OK/ERR/ABSENT status with a heap buffer the caller frees.
  • Scoped MRJAR — base classes target --release 17; on a build JDK ≥ 22 the FFM overlay compiles at --release 22 into META-INF/versions/22. Manifest gets Multi-Release: true + Enable-Native-Access: ALL-UNNAMED. Older build JDKs simply omit the overlay — same public API, faster impl where available (not feature divergence).

Testing

  • TransportParityTest — JNI and FFM agree on all three hot ops over one shared handle (binary payload spanning every byte value); skips the FFM assertions on JDKs without the overlay.
  • FfmRoundTripTest — end-to-end payload fidelity (empty, full-Unicode, 256 KiB) through the active transport, plus the batch path.
  • Full ./gradlew build on JDK 22: Spotless/Checkstyle + suite green; both FFM tests ran (not skipped) exercising the FFM path; verified META-INF/versions/22/.../FfmTransport.class and the manifest attrs in the jar. Rust fmt/check/clippy clean.

Summary by CodeRabbit

  • New Features

    • Added a faster native integration path for Java 22+.
    • Enabled the Java SDK to automatically use the newer native bridge when available, with a fallback to the existing approach on older runtimes.
    • Added support for batch task submission and result retrieval through the improved native layer.
  • Bug Fixes

    • Improved handling of missing results so “not found” cases are reported cleanly instead of as errors.
    • Strengthened native boundary handling to prevent crashes from unexpected failures.

@github-actions github-actions Bot added the rust label Jul 2, 2026
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 30 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ca5381c7-0e05-49d6-a2ec-4308cd73c625

📥 Commits

Reviewing files that changed from the base of the PR and between 7256a22 and 74c0bf4.

📒 Files selected for processing (5)
  • .github/workflows/publish-java.yml
  • sdks/java/README.md
  • sdks/java/build.gradle.kts
  • sdks/java/src/main/java22/org/byteveda/taskito/internal/FfmTransport.java
  • sdks/java/src/test/java/org/byteveda/taskito/FfmRoundTripTest.java
📝 Walkthrough

Walkthrough

Adds a Rust C-ABI FFI fast path (ffi_c.rs) exposing enqueue, enqueue_many, get_result, and free exports for Java's Panama/FFM on JDK 22+. Introduces a NativeTransport abstraction with FfmTransport and JniTransport implementations, updates JniQueueBackend to route through it, adds Gradle Multi-Release JAR build support, and adds parity tests.

Changes

FFM Fast Path Implementation

Layer / File(s) Summary
Rust C-ABI FFI exports
crates/taskito-java/src/ffi_c.rs, crates/taskito-java/src/lib.rs
Adds status codes, buffer/frame helpers, and taskito_ffi_enqueue/taskito_ffi_enqueue_many/taskito_ffi_get_result/taskito_ffi_free exports with panic-safety, registered as a new module.
NativeTransport interface and selection
.../internal/NativeTransport.java, .../internal/JniTransport.java, .../internal/JniQueueBackend.java
Adds NativeTransport interface with create(handle) selecting FfmTransport reflectively and falling back to JniTransport; JniQueueBackend routes enqueue/enqueueMany/getResult through the new transport.
FfmTransport Panama implementation
.../java22/.../FfmTransport.java
Resolves native symbols via SymbolLookup, implements enqueue/enqueueMany/getResult using Arena-allocated segments, batch framing/unframing, native buffer copy/free, and exception translation.
Multi-Release JAR build and parity tests
sdks/java/build.gradle.kts, .../test/.../TransportParityTest.java
Adds a conditional java22 source set compiled with --release 22, packages it under META-INF/versions/22 with Multi-Release manifest attributes, updates test classpath/JVM args, and adds tests verifying FFM/JNI parity.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant JniQueueBackend
  participant NativeTransport
  participant FfmTransport
  participant RustFFI as Rust taskito_ffi_*

  JniQueueBackend->>NativeTransport: create(handle)
  NativeTransport->>FfmTransport: reflective create(handle)
  alt FFM unavailable
    NativeTransport-->>JniQueueBackend: JniTransport fallback
  end
  JniQueueBackend->>NativeTransport: enqueue(task, payload, options)
  NativeTransport->>FfmTransport: enqueue(...)
  FfmTransport->>RustFFI: taskito_ffi_enqueue(handle, args)
  RustFFI-->>FfmTransport: status + job id bytes
  FfmTransport-->>JniQueueBackend: job id string
Loading

Poem

A rabbit hops through native land,
FFM and JNI hand in hand,
Panama's bridges, framed so neat,
Byte by byte, results complete,
Twenty-two's the door we found. 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately summarizes the main change: a Java Panama FFM fast-path for hot byte operations.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/java-ffm-fastpath

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
sdks/java/src/test/java/org/byteveda/taskito/internal/TransportParityTest.java (1)

22-27: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider failing hard instead of skipping when FFM should be active.

assumeFalse treats "JDK <22, FFM absent" and "JDK 22+, FFM overlay present but broken" identically — both just skip. Since the Gradle test task only adds java22.output to the classpath when building on JDK 22+, a broken FFM init on such a run would silently report "skipped" rather than failing, hiding a real regression on the exact JDK this feature targets.

♻️ Suggested tightening
     `@Test`
     void ffmSelectedOnJava22() {
         NativeTransport transport = NativeTransport.create(0L);
-        assumeFalse(transport instanceof JniTransport, "FFM transport not active on this JDK");
-        assertEquals("FfmTransport", transport.getClass().getSimpleName());
+        assumeTrue(Runtime.version().feature() < 22 || !(transport instanceof JniTransport),
+                "FFM transport unexpectedly inactive on JDK 22+");
+        if (Runtime.version().feature() >= 22) {
+            assertEquals("FfmTransport", transport.getClass().getSimpleName());
+        }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@sdks/java/src/test/java/org/byteveda/taskito/internal/TransportParityTest.java`
around lines 22 - 27, The ffmSelectedOnJava22 test currently skips in both “JDK
<22” and “JDK 22+ but FFM broken” cases because of assumeFalse, which can hide
regressions. Update TransportParityTest.ffmSelectedOnJava22 to explicitly check
the runtime/JDK condition before creating NativeTransport, and only skip when
FFM is genuinely unavailable on this JVM; if the JDK 22+ FFM path should be
active, let the test fail when NativeTransport.create(0L) does not produce an
FfmTransport. Keep the existing class-name assertion and use the
NativeTransport/JniTransport/FfmTransport types to make the intent clear.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@sdks/java/build.gradle.kts`:
- Around line 158-168: The native-access manifest setting in the Jar task is
being described too broadly for SDK consumers. Update the comment near the
manifest attributes in jar so it clearly states that Enable-Native-Access:
ALL-UNNAMED only applies when the artifact is launched directly with java -jar,
not when used as a classpath dependency; also add a note in the Java SDK README
that downstream users must pass --enable-native-access=ALL-UNNAMED themselves.
- Around line 145-146: The FFM overlay is currently controlled by
JavaVersion.current() in build.gradle.kts, so the java22 source set only
activates when Gradle itself runs on JDK 22+, which prevents the Multi-Release
overlay from being included in the published jar during the JDK 21 publish
workflow. Update the ffmCapable gating so the java22 overlay is driven by a JDK
22+ toolchain or change the release job to run on JDK 22+, and keep the relevant
build logic around the java22 source set and Multi-Release packaging aligned
with that choice.

In `@sdks/java/src/main/java22/org/byteveda/taskito/internal/FfmTransport.java`:
- Around line 211-223: The take(MemorySegment outData, MemorySegment outLen)
helper currently frees the native buffer only after the byte[] copy succeeds, so
a failure in the reinterpret/toArray path can leak the Rust allocation. Update
the copy/free flow in FfmTransport.take so the taskito_ffi_free call (via
FREE.invokeExact) runs in a finally block after reading the pointer and length,
ensuring the native buffer is always released even if copying throws.

---

Nitpick comments:
In
`@sdks/java/src/test/java/org/byteveda/taskito/internal/TransportParityTest.java`:
- Around line 22-27: The ffmSelectedOnJava22 test currently skips in both “JDK
<22” and “JDK 22+ but FFM broken” cases because of assumeFalse, which can hide
regressions. Update TransportParityTest.ffmSelectedOnJava22 to explicitly check
the runtime/JDK condition before creating NativeTransport, and only skip when
FFM is genuinely unavailable on this JVM; if the JDK 22+ FFM path should be
active, let the test fail when NativeTransport.create(0L) does not produce an
FfmTransport. Keep the existing class-name assertion and use the
NativeTransport/JniTransport/FfmTransport types to make the intent clear.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7f35cdbb-19f9-45e2-99fd-8d3e41d776f6

📥 Commits

Reviewing files that changed from the base of the PR and between 756f5ff and 7256a22.

📒 Files selected for processing (9)
  • crates/taskito-java/src/ffi_c.rs
  • crates/taskito-java/src/lib.rs
  • sdks/java/build.gradle.kts
  • sdks/java/src/main/java/org/byteveda/taskito/internal/JniQueueBackend.java
  • sdks/java/src/main/java/org/byteveda/taskito/internal/JniTransport.java
  • sdks/java/src/main/java/org/byteveda/taskito/internal/NativeTransport.java
  • sdks/java/src/main/java22/org/byteveda/taskito/internal/FfmTransport.java
  • sdks/java/src/test/java/org/byteveda/taskito/FfmRoundTripTest.java
  • sdks/java/src/test/java/org/byteveda/taskito/internal/TransportParityTest.java

Comment thread sdks/java/build.gradle.kts
Comment thread sdks/java/build.gradle.kts
Comment thread sdks/java/src/main/java22/org/byteveda/taskito/internal/FfmTransport.java Outdated
@pratyush618 pratyush618 changed the title feat(java): Panama FFM fast-path for hot byte ops feat(java): add Panama FFM fast-path for hot byte ops Jul 2, 2026
@pratyush618
pratyush618 merged commit 03213e5 into master Jul 2, 2026
36 checks passed
@pratyush618
pratyush618 deleted the feat/java-ffm-fastpath branch July 2, 2026 16:10
@pratyush618 pratyush618 self-assigned this Jul 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant