JDK-8243496 : Implementation of Foreign-Memory Access API (Second Incubator)
  • Type: CSR
  • Component: core-libs
  • Priority: P3
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 15
  • Submitted: 2020-04-23
  • Updated: 2020-07-13
  • Resolved: 2020-05-12
Related Reports
CSR :  
Relates :  
Relates :  
Description
Summary
-------

This CSR refers to second iteration of the Foreign Memory Access (an incubating Java API) originally targeted for Java 14, 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 usability issues, listed below:

 * converting `MemoryAddress` to/from `long` values is too hard
 * The API's bias towards confinement would benefit from an API point to cooperatively change thread owership of a given segment (serial confinement)
 * The `MemorySegment::acquire` is too complex, as it leads to *trees* of segments where the enclosing segment cannot be closed before the nested ones are; at the same time, this API fails to provide enough support for clients that wish for a truly unconfined memory segment
 * While the API supports mapped memory segments via its `MemorySegment::mapFromPath` factory, the support is partial, since there's no way to *force* the mapped segment contents back into the mapped file.
 * When implementing frameworks on top of memory access var handle, it is often handy to be able to perform *deep* adaptation of the var handles so that e.g. additional access coordinates can be inserted, dropped, or bound
 * An unsafe API point is required to create a native segment out of an existing `MemoryAddress` instance; this is the moral equivalent of the JNI NewDirectByteBuffer function.

Solution
--------

Here we describe the main ideas behind the API changes brought forward in this CSR:

 * not all `MemoryAddress` instances are created equal; some addresses are *checked* --- that is, they are associated with an underlying `MemorySegment`, some are *unchecked* e.g. they are just wrappers around some native address. Dereference on a `MemoryAddress` instance can only be considered safe if the address being dereferenced is *checked* - since the associated segment would provide enough context (spatial, temporal, thread confinement bounds) to validate the access. With this distinction in mind, it is then possible to add API points to e.g. safely create a `NULL` address (which would be *unchecked*) or an address wrapping a given long value.
 * Remove the `MemorySegment::acquire` method, whose complexity was ultimately not helping the use cases for which it was created. In its place, to allow for parallel processing of a segment content, we instead offer the ability to slice and dice a memory segment using a segment `Spliterator`.
 * To make serial thread confinement more useful in producer/consumer use cases, a new API point `MemorySegment::withOwnerThread` is added so that one thread can give up ownership on a given segment and transfer ownership to a second thread.
 * A new interface, namely `MappedMemorySegment` (which extends from `MemorySegment`) is provided, which adds functionalities equivalent to `MappedByteBuffer::force` and `MappedByteBuffer::load`.
 * The new API also provides a number of new `VarHandle` adapters; while these are temporarily defined in the `MemoryHandles` class, in reality these adapters are general (pretty much like method handle adapters in the `MethodHandles` class) and will be moved into the `MethodHandles` class when the API exits incubation.
 * A new memory segment factory has been added, namely `MemorySegment::ofNativeRestricted` which allows developers to take an existing memory address and create a segment out of it. Since an address is not guaranteed to have any associated spatial and temporal bounds (as is the case for unchecked addresses) such an operation is inherently unsafe. To limit the exposure of this API point, we have opted to guard calls to this method with a read-only JDK property, namely `-Dforeign.restricted`; this property can assume several values - the default value is `deny`, which will trigger an hard exception each time such a method is called. Developers can override this property value from the command line, to e.g. `permit`, which will allow calls to this method to succeed. Note: this way of accessing restricted foreign functionalities through a runtime property is a pragmatic compromise, which will be replaced by a more robust mechanism (perhaps based on the module system) by the time the API exits the incubation stage.

Specification
-------------

Here are some useful links which should help in navigating through the changes in the API.

Webrev:

http://cr.openjdk.java.net/~mcimadamore/8243491_v3/webrev

Javadoc:

http://cr.openjdk.java.net/~mcimadamore/8243491_v3/javadoc

Specdiff:

http://cr.openjdk.java.net/~mcimadamore/8243491_v3/specdiff/overview-summary.html

In addition, a specdiff of the changes as of May 1st 2020 has been attached to this CSR.



Comments
Thanks - I will update the doc/code to reflect your suggestion to use IllegalArgumentException, rather than UnsupportedOperationException. I agree that's clearer.
12-05-2020

Moving to Approved. Some additional comments for your consideration: In MemorySegment::withAccessModes IllegalArgumentException to me seems a little better than UnsupportedOperationException in the case where there are bits for undefined access modes. I think it is acceptable to leave it as-is, but if you want to change it, either update this CSR for a comment stating that or file a follow-up bug. In various places, when the javadoc says something like "This [method] is equivalent to the following code: withFoo(BAR, baz);" the text could be placed into an implSpec tag as it specifies an operational requirement of the implementation. In several places in the API, using an inline @jls tag might help the reader get more information. There are example usages of this new tag in core libs (JDK-8237805). Places I noticed where this could be used include discussion of try-with-resources (JLS 14.20.3. try-with-resources) in the package-level docs and happens before (JLS 17.4.5. Happens-before Order ) in MemorySegment. Also for the package-level docs, the concept of spacial safety could be compared to the familiar array bounds check (JLS 15.10.4. Run-Time Evaluation of Array Access Expressions).
12-05-2020

Comments addressed and moved to Finalized. Some other minor javadoc changes around access modes were also added as part of the RFR process. The changes are aimed at specifying with which access modes are memory segments created when using the various factories.
01-05-2020

Thanks for the comments Joe. I'm sorry the protected fields leaked in the javadoc; I did a final sanity check and I missed that. The implementation does check for access modes that are invalid - I should make that clearer in the doc. I'll implement your comments and finalize this CSR.
30-04-2020

Moving to Provisional. A few code review comments: GroupLayout, SequenceLayout, and ValueLayout have a protected attributes field without any documentation. I assume these fields should have a reduced accessibility so it does not appear in the javadoc output. Wording suggestion in MemorySegment, changing "The set of supported access modes can only be made stricter (by supporting less access modes)." to "The set of supported access modes can only be made stricter (by supporting fewer access modes)." There are no general checks for the invalidity of the int holding access modes in terms of passing in undefined bit positions?
30-04-2020

It may be useful to provide some extra context on the `foreign.restricted` system property, and whether this is a temporary solution during incubation until something more established leveraging the module system can be defined.
23-04-2020