[HN Gopher] FFM (Foreign Function and Memory API) Goes Final
       ___________________________________________________________________
        
       FFM (Foreign Function and Memory API) Goes Final
        
       Author : lichtenberger
       Score  : 47 points
       Date   : 2023-12-10 19:15 UTC (3 hours ago)
        
 (HTM) web link (mail.openjdk.org)
 (TXT) w3m dump (mail.openjdk.org)
        
       | Deukhoofd wrote:
       | I'm not a Java dev, so forgive me if I misunderstand it, but is
       | this basically just a different API for calling C functions? Java
       | already was able to do so was it not? How does this improve on
       | it?
        
         | Racing0461 wrote:
         | I was interested in why also since there was already JNI. This
         | is what i found
         | 
         | >>> The FFM API allows Java programs to interoperate safely and
         | more easily with code and data outside the Java runtime. Unlike
         | JNI, which often involves brittle and error-prone boilerplate
         | code, the FFM API aims to provide a more straightforward and
         | safer method for calling native libraries and manipulating
         | native data.
        
           | TeMPOraL wrote:
           | That sounds like a zero-information marketing blurb, though.
           | The interesting bits would be not the claims, but how they're
           | realized - i.e. how FFM is implemented and what's the
           | difference between how it works and how the implementation is
           | different from JNI.
        
             | kaba0 wrote:
             | They have low-overhead temporal-, spatial-, and thread-
             | safety guarantees. Spatial in that every "C
             | pointer"=MemorySegment is associated with bounds, which is
             | checked at runtime. Temporal, in that such a MemorySegment
             | has a scope (usually created in a try-with resource block,
             | being closed right afterwards, or alternatively at a random
             | later point, up to the GC), outside of which it can't be
             | called (also, freeing actions can be associated with
             | these). Thread safety, in that a scope also determines the
             | availability of a given segment to other threads (must be
             | explicitly specified to be accessible mutably from multiple
             | threads).
        
         | simtel20 wrote:
         | I am not a java dev either, but as it's named, this focuses on
         | memory and ffi,and e.g. in the linked message talks about
         | passing heap memory to a foreign function in a zero copy
         | manner, which sounds like a great trick which would make a lot
         | of calling c APIs from java much easier and faster.
        
         | mwilliamson wrote:
         | The JEP itself has a motivation section, including issues with
         | the existing mechanism for interop with C, JNI:
         | https://openjdk.org/jeps/454
        
         | fweimer wrote:
         | If you want to use JNI to interface with an existing C library,
         | you need to write a thin C or C++ wrapper that is called via
         | JNI and interfaces with that C library. This new Java API
         | allows you to call C functions directly and manipulate data
         | structures on the C side, too. It's also possible to create a C
         | function pointer for a Java method, to enable callbacks/upcalls
         | from C.
        
           | dkersten wrote:
           | JNA exists which already let you do that. What does this
           | change or add over JNA?
           | 
           | https://en.m.wikipedia.org/wiki/Java_Native_Access
        
             | thayne wrote:
             | Well for one thing, it is part of OpenJDK, instead of a
             | third party library built on top of JNI, and can thus, at
             | least in theory, integrate better with the runtime.
        
             | MrBuddyCasino wrote:
             | JNI: fast but tedious
             | 
             | JNA: comfortable but slow
             | 
             | Panama: comfortable & fast
        
         | cstrahan wrote:
         | JNI allows one to write C code that bridges the gap between the
         | Java VM and existing C libraries. That C code must be compiled,
         | and must follow some rather tedious patterns (regarding method
         | signatures and other little details). It's a bit of a pain to
         | write and compile, if all you want to do is quickly call some
         | function from an existing C library.
         | 
         | Also, because symbol resolution is done at compile-time, this
         | is a non starter for something like JRuby, wherein one would
         | want to write plain Ruby code to call a C function -- rather
         | than writing Ruby, Java _and_ JNI flavored C code. Ruby is an
         | interpreted language, so calling into or out of C requires
         | dynamic symbol resolution, and for the C- >Ruby callback case,
         | a closure must be created on the fly (that is, a C ABI function
         | must be JIT compiled for that method or proc). This isn't
         | particular to Ruby -- any interpreted language will run into
         | this issue on the JVM (picture Python and its ctypes and/or
         | cffi libraries).
         | 
         | You can see some of the hoops that JRuby had to jump through to
         | support this use case, in order to provide a JRuby
         | implementation of the 'ffi' gem interface:
         | 
         | https://javadoc.io/doc/org.jruby/jruby-core/9.2.13.0/org/jru...
         | 
         | https://www.igvita.com/2009/01/15/bridging-mri-jruby-rubiniu...
         | 
         | It's been a minute, so my recollection may be wrong, but IIRC
         | JRuby uses Java and JNI to call into a C shim that then calls
         | into libffi to handle things like creating C ABI conforming
         | closures and such on the fly (the ffi gem on CRuby also uses
         | libffi for the same purposes).
         | 
         | Correction: I believe JRuby uses JNA, which in turn is Java+JNI
         | to call C that then calls into libffi, which is then leveraged
         | to call whatever other functions (or create C callbacks back
         | into the JVM). So I'm pretty sure FFM would be an officially
         | supported replacement (maybe superset?) of JNA. (I'm on
         | vacation and typed all of this out on my phone, so forgive me
         | if I got some details a bit off)
         | 
         | I'm pretty sure that just about all of that complexity can be
         | ripped out once FFM is generally available.
        
       | SillyUsername wrote:
       | Well this might be a fun one to search for when you need to learn
       | the new API at work...
        
         | sgift wrote:
         | Search for "Project Panama" if your work has problems with
         | normal terms. That's the development name.
        
       | stickfigure wrote:
       | I have misgivings about making interop with native code easier.
       | 
       | In the node, python, and ruby ecosystems, native code
       | dependencies are a horrorshow of brittle builds. The amount of my
       | life that has been wasted on stupid build issues is significant
       | (damn you nokogiri).
       | 
       | The JVM has been a relative sea of tranquility. The ecosystem is
       | so large, and JNI so unpleasant, that everything important has
       | been built JVM-native. Builds just work, even when you walk away
       | for two years.
       | 
       | I don't want native code in my projects, and I fear this will
       | encourage it.
        
         | irq-1 wrote:
         | Golang followed the same path; no dynamic linking and
         | everything was 're'-created in Go. It's effective.
         | 
         | For FFM won't C access be isolated to the JAR? Maybe it's not
         | normal to get pre-built JAR files?
        
           | malkia wrote:
           | It's effective... Until you need sqlite, or lmdb, or
           | something like this.
        
         | kaba0 wrote:
         | I think Java is just so insanely big, that this Java-pureness
         | will stick, and JNI/Panama will only be used, when absolutely
         | necessary. This is a unique trait thanks to the way the
         | language grew.
        
         | geokon wrote:
         | Overall I agree.. I like not having native code.. I run the JVM
         | so I don't have to worry about platform specific stuff. But
         | this would be more-so an issue in the Java 8 world where your
         | uberjar needs to have lib blobs for every platform it may be
         | run on
         | 
         | In the Java 11+ world you are no longer in the build-once-run-
         | everywhere model and you unfortunately are semi-required to
         | build platform specific bundles. And adding a C++ build step
         | here wouldn't be a disaster - in theory. You could actually do
         | it cleanly/safely with CMake + Hunter + platform-specific
         | toolchain files. Unfortunately the C++ world hasn't converged
         | to CMake/Hunter so if you're using a uncommon/weird lib then it
         | might take work to make it work.
         | 
         | But someone could then in theory clone your project in 20 years
         | running BSDinux on a RISC-VI chip and it should build and run
        
           | koito17 wrote:
           | > you unfortunately are semi-required to build platform
           | specific bundles
           | 
           | Can you elaborate on this? It seems like a build tool
           | specific issue to me. In Clojure, most projects use Leiningen
           | (a build tool) and uberjars regardless of the target Java
           | version will bundle native libraries for every platform you
           | run on. You can exclude some of the libraries from your
           | uberjar, but it's a manual process. This has an obvious
           | drawback (e.g. depending on the SQLite JDBC driver bloats
           | your JAR with dozens of native libraries), but it's still
           | very much "build once, run everywhere".
           | 
           | The closest I've been to "platform specific bundles" was
           | dealing with a Gradle project where the developer hard-coded
           | a target platform into a property, thus making it impossible
           | to bundle libraries for multiple platforms.
        
             | geokon wrote:
             | Right, both lein and depstar (or whatever the latest
             | deps.edn one is) make uberjars. And this works great on
             | Linux - which I'm pretty sure is the dev platform for
             | almost all Clojurists. Linux distros basically always come
             | with a OpenJDK JRE/JVM. But installing a JRE is actually
             | discouraged by the Java people. You will notice on java.com
             | you can't actually download a Java 11/17/+ JRE. You can
             | only get a Java 8 JRE!
             | 
             | You're supposed to use `jpackage`. It basically packages up
             | the uberjar with all the necessary pieces of the JRE to run
             | the uberjar (so a pared down JRE) and does platform
             | specific bundling like adding meta data and creating an
             | installer (so your application can have a place to save
             | settings and whatnot between runs, have an icon in the
             | Start menu/desktop and stuff like that)
             | 
             | You can still do uberjars and get a JRE for Windows/Mac
             | from third parties like Adaptium, but it's very unergonomic
             | and looks kinda sketch (b/c it doesn't look official)
             | 
             | My own anecdotal experience:
             | 
             | I distributed a JavaFX/Java-17 app as an uberjar. My
             | rational was:
             | 
             | - I didn't need any app persistence between runs, so I just
             | wanted a small double-clickage ".exe/bin" equivalent that
             | people could download and try out (no one wants install
             | some random app to try out from online).
             | 
             | - `jpackage` doesn't allow you to make a simple double-
             | clickable .exe (they can make something confusingly called
             | an appimgage, which is not an Linux appimage - but it's
             | similar!)
             | 
             | - I had no way to do testing on Mac b/c I don't own an
             | Apple machine. So I don't wanna be generating Mac
             | executables I can't even test.. At least with the uberjar I
             | can be semi-confident if it runs on my laptop it'll run on
             | a Mac later (sorta true)
             | 
             | The end result was a disaster.. 90% of user issues were
             | from people that would who would go to java.com, "install
             | Java" (ending up with a Java 8 JRE!) and then the app would
             | just silently not run. The JRE doesn't produce any friendly
             | error or warning saying anything about versions - it just
             | silently fails. I have in bold on my landing page YOU NEED
             | A JAVA 17+ JRE. No use.. people would still do it wrong
        
               | dtech wrote:
               | Yeah java isn't really used for Consumer apps and this is
               | one of the reasons. The ones that do evade this problem
               | by bundeling their own JVM.
        
         | dongping wrote:
         | Bazel streamlines the build process with native code (C++/Rust)
         | in Python, making the experience a breeze. However, the
         | drawback is that you would need to transition to Bazel.
        
           | malkia wrote:
           | I'm not sure if it's still true, but I believe back at Google
           | the interop with C/C++ was even more integrated - if I'm not
           | mistaken the "java" binary got recompiled from scratch (but
           | thanks for caching, not much hit), and your C/C++ code got
           | compiled in with it - so no ".so" loading - you still have
           | single binary (and I think the .jar also went at the end of
           | the file).
           | 
           | Or maybe that was for python (e.g. compile CPython along with
           | any ffi modules as one) + .zip file with classes.
           | 
           | In any case, even if above was not 100% true, it's doable
           | with system like bazel/buck/etc., and still allow you for
           | smooth incremental development (or say in the default
           | "fastbuild" .so files may still be used - locally, but in
           | "opt" the full static link, or through a --config)
        
         | thayne wrote:
         | But native code dependencies in those languages usually work
         | similarly to JNI in java, where you write wrapper code in c or
         | c++ to convert from native apis to the interpretor's interface.
         | So interfacing with native code isn't fundamentally easier than
         | for java. Python does have ctypes, but that isn't what
         | libraries like numpy use, probably because of performance.
         | 
         | I think there are a couple of other reasons why Java doesn't
         | have the same problems with brittle builds. One is that for
         | python and ruby and to a lesser extent node, it is more often
         | necessary to use native code to get desirable performance, so
         | there are more cases where native code is used. Another is that
         | in the JVM ecosystem, it is more common for packages to be
         | distributed in binary form rather than as sources, so you don't
         | run into problems compiling dependencies, because you download
         | a package that already has the compiled library in it.
        
           | deepsun wrote:
           | > it is more common for packages to be distributed in binary
           | form rather than as sources, so you don't run into problems
           | compiling dependencies
           | 
           | Not sure what you mean here. Java bytecode is similar to
           | source code, just easier for machines to parse, also easy to
           | decompile. Mainstream Java is an interpreted language
           | (requires JVM), not compiled.
           | 
           | Don't see what's the difference with packaging, say, python
           | code to a zip archive.
        
         | pron wrote:
         | Note that FFM requires that you explicitly allow the use of
         | native code with --enable-native-access (and soon JNI will,
         | too: https://openjdk.org/jeps/8307341).
         | 
         | As the FFM JEP (https://openjdk.org/jeps/454) states:
         | 
         | > To allow code in a module M to use unsafe methods without
         | warnings, specify the --enable-native-access=M option on the
         | java launcher command line. Specify multiple modules with a
         | comma-separated list
         | 
         | The warnings will become errors in a later release.
         | 
         | This means that no library you're using (even some transitive
         | dependency) can call native code without you, the application
         | author, knowing about it. This restriction on native code is
         | part of the work on "integrity by default":
         | https://openjdk.org/jeps/8305968
        
       | jay-barronville wrote:
       | Some years ago, I wrote a custom JNI wrapper over libuv [0] for a
       | Java/Kotlin project I was working on. It's mind-blowing how much
       | work it took to get cross-compilation and everything else
       | working. That said, as someone who's been writing C code for
       | years, it wasn't the worst experience I've had; it was mostly
       | just tedious.
       | 
       | [0]: https://github.com/1791-labs/carlie
        
       | zylepe wrote:
       | I'm looking forward to be able to memory-map an entire large file
       | without having to split it up into 2gb segments, and to be able
       | to reliably unmap it when done. So many hacks to work around this
       | lack of functionality today...
        
       ___________________________________________________________________
       (page generated 2023-12-10 23:00 UTC)