Summary
-------
This CSR refers to the third iteration of the Foreign Memory Access (an incubating Java API) originally targeted for Java 14 (and later re-incubated in 15), with the goal of refining some of the API rough edges, as well as addressing the feedback received so far from developers.
Problem
-------
Real-world use of the Foreign Memory Access API revealed some remaining usability issues, listed below:
* The API's bias towards confinement is still too prominent, despite the addition (in 15) of an API point to cooperatively change thread ownership of a given segment (serial confinement); the main issue is that, even with the new API, it is still not possible to *close* a memory segment from a thread other than the owner thread, and that makes memory segment hard to use with certain API idioms
* While deterministic deallocation is one of the core foundations of the Foreign Memory Access API, users have expressed interest in having the ability to optionally register segments against a `Cleaner` instance, so that segment deallocation could be guaranteed regardless of whether an explicit call to `MemorySegment::close` is made
* The distinction between checked and unchecked memory addresses is too subtle. Checked addresses have a reference to the owning segment, and can be safely dereferenced, whereas unchecked addresses can't. This leads to verbosity in clients attempting to dereference a `MemoryAddress` instance, as the client has first to query if a segment is available, and then act accordingly.
* Forcing developers to exclusively use `VarHandle` when it comes to dereference memory is a bit too much; there are cases where the expressive power of var handles come in handy (e.g. in concurrent contexts, when fencing is required, or when structural access is needed), but in simpler cases it would be nice to have a ready-made collections of dereference API points which can be used in a type-safe way.
* While a memory address and a memory segment generally represent separate concepts, it is possible to go from the latter to the former (e.g. by calling the `MemorySegment::baseAddress` method). Our experiments with the `jextract` tool revealed that the need for this explicit conversion has often a pretty big impact in the verbosity of clients interacting with the native bindings generated by jextract.
Solution
--------
Here we describe the main ideas behind the API changes brought forward in this CSR:
* First, the notion of shared memory segment is introduced. A shared memory segment is created by invoking the `MemorySegment::share` method that returns a new memory segment instance, which is backed by the same memory region, and has no owner thread. This means that the segment will be effectively accessible (*and* closeable) from multiple threads (possibly in a concurrent fashion). The safety promises of the Foreign Memory Access API are preserved by a sophisticated lock-free synchronization scheme which relies on thread-local handshakes (see JEP 312).
* A new method is added to memory segments, `MemorySegment::registerCleaner(Cleaner)` that returns a new segment instance, which is backed by the same memory region, and supports implicit deallocation.
* Memory access var handles now use memory segments as the basic dereference unit. That is, the most basic memory access var handle accepts two coordinates: the segment to be dereferenced and the offset within the segment at which the dereference should occur. All other, structural memory access var handles can be composed from this basic one. As a result, `MemoryAddress` has reverted back to being a dumb carrier for an object/offset unsafe addressing coordinate pair. That is, there is no way to go from an address back to a segment (except *unsafely* via `MemoryAddress::asSegmentRestricted` but that creates a *new* segment).
* A new class, namely `MemoryAccess` is introduced. This class contains several dereference methods to get and set values in memory, with different Java carriers. There are different access modes supported: a basic one which simply takes a memory segment and dereferences it at its base address; other, more sophisticated, modes allows a byte offset or a logical index to be passed as well, to support common array-like access idioms.
* To better capture the commonality between a memory segment and a memory address a new interface, namely `Addressable` has been introduced. This interface describes entities that can be mapped into a memory address. This interface is implemented, trivially, by `MemoryAddress` and also by `MemorySegment` (a memory segment can always be projected to its base address). We plan, with subsequent JEPs (e.g. JEP 389) to add more implementations of this interface.
* The hierarchy of interface `MappedMemorySegment <: MemorySegment` poses issues when it comes to `VarHandle` invocations; since now the memory access var handle take a `MemorySegment` access coordinate, calling such var handles with a `MappedMemorySegment` coordinate will result in a non-exact var handle call, thus greatly degrading performances; we have decided to drop the `MappedMemorySegment` interface and instead introduce an helper class, namely `MappedMemorySegments`, which contains _static_ methods which provide the same functionality as what was provided by `MappedMemorySegment`.
Specification
-------------
Here are some useful links which should help in navigating through the changes in the API.
Javadoc:
http://cr.openjdk.java.net/~mcimadamore/8254162_v5/javadoc
Specdiff:
http://cr.openjdk.java.net/~mcimadamore/8254162_v5/specdiff_out
Pull request:
https://github.com/openjdk/jdk/pull/548
In addition, a specdiff of the changes as of November 9th 2020 has been attached to this CSR.