[foreign-memaccess+abi] RFR: Update foreign memory doc
Jorn Vernee
jvernee at openjdk.org
Tue Dec 13 18:55:36 UTC 2022
On Fri, 9 Dec 2022 17:30:21 GMT, Maurizio Cimadamore <mcimadamore at openjdk.org> wrote:
> This patch updates the foreign memory document to reflect the latest API changes in Java 20.
>
> I've streamlined the document, removed some obsolete sections (as well as sections that didn't seem to add much), and re-ordered the various sections so that the narrative is now easier to follow.
Marked as reviewed by jvernee (Committer).
doc/panama_memaccess.md line 29:
> 27: Segments associated with an automatic scope are alive as long as they are determined to be reachable by the garbage collector. In other words, the above snippet creates a native segment whose behavior closely matches that of a `ByteBuffer` allocated with the `allocateDirect` factory.
> 28:
> 29: There are cases, however, where automatic deallocation is not enough: consider the case where a large memory segment is mapped from a file (this is possible using `MemorySegment::map`); in this case, an application would probably prefer to release (e.g. `unmap`) the memory associated with this segment in a *deterministic* fashion, to ensure that memory doesn't remain available for longer than in needs to.
Suggestion:
There are cases, however, where automatic deallocation is not enough: consider the case where a large memory segment is mapped from a file (this is possible using `MemorySegment::map`); in this case, an application would probably prefer to release (e.g. `unmap`) the memory associated with this segment in a *deterministic* fashion, to ensure that memory doesn't remain available for longer than it needs to.
doc/panama_memaccess.md line 42:
> 40: ```
> 41:
> 42: When the arena is closed (above, this is done with the *try-with-resources* construct) the arena scope is no longer alive, all the segments associated with it are invalidated, and the regions of memory backing the segments are deallocated atomically.
We don't deallocate atomically, but we invalidate atomically. So I think this should be:
Suggestion:
When the arena is closed (above, this is done with the *try-with-resources* construct) the arena scope is no longer alive, all the segments associated with it are invalidated atomically, and the regions of memory backing the segments are deallocated.
doc/panama_memaccess.md line 46:
> 44: ### Thread-confinement
> 45:
> 46: Arenas provide a strong temporal safety guarantee: memory segments associated with an arena scope cannot be accessed after the arena is closed, since the arena scope is no longer alive. If an arena is opened and closed by the same thread, and all memory segments allocated with the arena's scope are accessed only by that thread, then ensuring correctness is straightforward. However, if memory segments allocated with the arena scope are accessed by multiple threads then ensuring correctness is complex. For example, a segment allocated with arena scope might be accessed by one thread while another thread attempts to close the arena. To guarantee temporal safety without making single-threaded clients pay undue cost, there are two kinds of arenas: *confined* and *shared*.
Suggestion:
Arenas provide a strong temporal safety guarantee: memory segments associated with an arena scope cannot be accessed after the arena is closed, since the arena scope is no longer alive. If an arena is opened and closed by the same thread, and all memory segments allocated with the arena's scope are accessed only by that thread, then ensuring correctness is straightforward. However, if memory segments allocated with the arena scope are accessed by multiple threads then ensuring correctness is complex. For example, a segment allocated with an arena scope might be accessed by one thread while another thread attempts to close the arena. To guarantee temporal safety without making single-threaded clients pay undue cost, there are two kinds of arenas: *confined* and *shared*.
doc/panama_memaccess.md line 162:
> 160: The above code creates a memory access var handle which reads/writes `int` values at a certain byte offset in a segment. To create this var handle we have to specify a carrier type — the type we want to use e.g. to extract values from memory, as well as whether any byte swapping should be applied when contents are read from or stored to memory. Additionally, the user might want to impose additional constraints on how memory dereferences should occur; for instance, a client might want to prevent access to misaligned 32 bit values. Of course, all this information can be succinctly derived from the provided value layout (`JAVA_INT` in the above example).
> 161:
> 162: The attentive reader might have noted how rich the var handles obtains from memory layouts are, compared to the simple memory access var handle we have constructed here. How do we go from a simple access var handle that takes a byte offset to a var handle that can dereference a complex layout path? The answer is, by using var handle *combinators*. Developers familiar with the method handles know how simpler method handles can be combined into more complex ones using the various combinator methods in the `MethodHandles` class. These methods allow, for instance, to insert (or bind) arguments into a target method handle, filter return values, permute arguments and much more.
Suggestion:
The attentive reader might have noted how rich the var handles obtained from memory layouts are, compared to the simple memory access var handle we have constructed here. How do we go from a simple access var handle that takes a byte offset to a var handle that can dereference a complex layout path? The answer is, by using var handle *combinators*. Developers familiar with the method handles know how simpler method handles can be combined into more complex ones using the various combinator methods in the `MethodHandles` class. These methods allow, for instance, to insert (or bind) arguments into a target method handle, filter return values, permute arguments and much more.
doc/panama_memaccess.md line 183:
> 181: ### Unsafe segments
> 182:
> 183: The FFM API provides basic safety guarantees for all memory segments created using the API. More specifically, a memory dereference operation should either succeed, or result in a runtime exception — but, crucially, should never result in a VM crash, or, more subtly, in memory corruption occurring *outside* the region of memory associated with a memory segment. This is indeed the case, as all memory segments feature immutable *spatial bounds*, and, as we have seen, are associated with a segment scope which make sure that segments cannot be dereferenced after their backing region of memory have been deallocated.
Suggestion:
The FFM API provides basic safety guarantees for all memory segments created using the API. More specifically, a memory dereference operation should either succeed, or result in a runtime exception — but, crucially, should never result in a VM crash, or, more subtly, in memory corruption occurring *outside* the region of memory associated with a memory segment. This is indeed the case, as all memory segments feature immutable *spatial bounds*, and, as we have seen, are associated with a segment scope which make sure that segments cannot be dereferenced after their backing regions of memory have been deallocated.
doc/panama_memaccess.md line 205:
> 203: For these reasons, `MemorySegment::ofAddress` is a *restricted method* in the FFM API. The first time a restricted method is invoked, a runtime warning is generated. Developers can get rid of warnings by specifying the set of modules that are allowed to call restricted methods. This is done by specifying the option `--enable-native-access=M`, where `M` is a module name. Multiple module names can be specified in a comma-separated list, where the special name `ALL-UNNAMED` is used to enable restricted access for all code on the class path. If the `--enable-native-access` option is specified, any attempt to call restricted operations from a module not listed in the option will fail with a runtime exception.
> 204:
> 205: * <a id="1"/>(<sup>1</sup>):<small> Shared arenas rely on VM thread-local handshakes (JEP [312](https://openjdk.java.net/jeps/312)) to implement lock-free, safe, shared memory access; that is, when it comes to memory access, there should no difference in performance between a shared segment and a confined segment. On the other hand, `Arena::close` might be slower on shared arenas than on confined ones.</small>
Suggestion:
* <a id="1"/>(<sup>1</sup>):<small> Shared arenas rely on VM thread-local handshakes (JEP [312](https://openjdk.java.net/jeps/312)) to implement lock-free, safe, shared memory access; that is, when it comes to memory access, there should be no difference in performance between a shared segment and a confined segment. On the other hand, `Arena::close` might be slower on shared arenas than on confined ones.</small>
-------------
PR: https://git.openjdk.org/panama-foreign/pull/758
More information about the panama-dev
mailing list