musinig on close actions
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Fri Feb 18 19:26:08 UTC 2022
Hi,
I wanted to capture some thoughts on scope close actions, which
originated from the good discussions we had in [1].
Basically, the status quo is that a close action is called _after_ the
scope is closed. This is a forced move: when we execute the close
action, we already have determined that e.g. the scope is not accessed
by any other thread. If resources were still accessible by close
actions, then closing a scope would not be safe (as they could be leaked
to other threads).
There is a minor inconvenience with this approach: if a close action
needs to refer to some of the resources associated with the scope (e.g.
a segment), the action needs to work on the resource using its memory
address, thus losing safety and also expressiveness (e.g. to use var
handles you need a memory segment). This was the issue pointed out in
[1] with the FFmpeg API. There are other situations in which working on
segments, rather than addresses, might be preferrable: for instance, one
can come up with an API which turns an heap segment into an off-heap
segment, inside a given scope (e.g. pinning). In certain cases where
pinning is not supported directly by the GC, it might be required to
copy the contents of the native segment back to the on-heap segment once
the scope is closed. But if the segment is not alive after close,
there's no way to do the copy.
I've been toying with an idea, which builds upon the following two
observations:
1. Resource-specific close actions are likely to arise when using the
`MemorySegment::ofAddress` methods (or any of the `XYZ::ofAddress`
factories)
2. A close action could safely operate on a *view* of the half-alive
resource based on a "closingScope" which is confined on the thread that
does the closing
The API move that I have in mind is this: let's assume that we added an
overload to `MemorySegment`, as follows:
```
static MemorySegment ofAddress(MemoryAddress addr, long size,
MemorySession session, Consumer<MemorySegment> cleanupAction)
```
Which can be used as follows:
```
MemoryAddress addr = ....
MemorySegment segment = MemorySegment.ofAddress(addr, 10L, session,
segmentView -> println(segmentView.get(JAVA_DOUBLE,
offset)));
```
How does this work? After a scope transitions into the closed state, but
_before_ cleanup actions are called, the scope would spawn a _new_
_confined_ scope, owned by the thread from which cleanup action are
called. That scope is then used to create resource views that will be
passed to the close actions. The new session is then closed after _all_
the close actions have been executed.
Crucially, since all views are confined and cannot be accessed after
`MemorySession::close()` completes, this works out ok safety-wise, I
believe. In a way, this is a restricted form of safe handoff of a
resource, from one scope to another. This allows clients to express
close actions at the right level of abstraction, without the need to
give up safety/expressiveness and drop down to `MemoryAddress`.
Please note that I'm not proposing to drop support for "global" close
actions (e.g. the `MemorySession::addCloseAction` method). These are
still useful in many cases, as we've seen for pooled allocators [2]. So
the above mechanism would be an addition to the API, rather than a
replacement (and one that can also be considered at a later point, if
needs be).
Any thoughts on whether something like this could help?
Cheers
Maurizio
[1] -
https://mail.openjdk.java.net/pipermail/panama-dev/2022-February/016173.html
[2] - https://github.com/openjdk/panama-foreign/pull/509
More information about the panama-dev
mailing list