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