[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)