[foreign-memaccess] on shared segments
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Tue Oct 15 17:54:57 UTC 2019
Fixed and pushed.
Cheers
Maurizio
On 15/10/2019 16:02, Maurizio Cimadamore wrote:
>
> On 15/10/2019 15:58, Jorn Vernee wrote:
>> Hi,
>>
>> Some comments;
>>
>> - MemorySegment's class javadoc mentions confined segments, but not
>> shared segments. Maybe there should be a short sentence like "Shared
>> segments can not be closed explicitly", with a link back to the
>> packag-info javadoc as well.
>> - In MemorySegment::asConfined javadoc, the language "Obtains a
>> confined copy of this memory segment" might be interpreted as the
>> underlying memory being copied. Maybe you could use; "Transfers the
>> underlying memory to a new MemorySegment instance confined to the
>> given Thread"? I think "Transfer" is more descriptive of what's
>> happening (I noticed the package javadoc also uses "transfer").
>> - Similarly for asShared.
> Yeah - I think I'll replace this with 'Obtains a new confined memory
> segment instance...' or something like that
>> - MemorySegment::isAccessible -> typo: "or is the segment is a shared
>> segment" -> "or if the segment is a shared segment". The sentence
>> right after that is also redundant with the following sentence.
>>
> Will fix
>
> Thanks
> Maurizio
>
>> Rest looks good,
>> Jorn
>>
>> On 14/10/2019 18:12, Maurizio Cimadamore wrote:
>>> New revision:
>>>
>>> http://cr.openjdk.java.net/~mcimadamore/panama/shared-segments_v4
>>>
>>> (although all openjdk websites are experiencing some troubles right
>>> now)
>>>
>>> This changes the API to:
>>>
>>> - add an 'isAccessible()' method which checks
>>> "isConfined(Thread.currentThread()) || isShared"
>>> - changes the spec for asShared and asCOnfined, allowing them to
>>> throw exception when an invalid state transition takes place
>>>
>>> Maurizio
>>>
>>> On 27/09/2019 14:48, Maurizio Cimadamore wrote:
>>>> Uploaded new revision:
>>>>
>>>> http://cr.openjdk.java.net/~mcimadamore/panama/shared-segments_v3/
>>>>
>>>> Changes:
>>>>
>>>> * Added full fences on asShared, asConfined
>>>> * Added new predicate for testing confinement
>>>> * restructured the calls to checkValidState - now basic accessors
>>>> and predicates like byteSize, baseAddress, isPinned etc. do NOT
>>>> check for confinement
>>>> * changed the javadoc of asConfined/asShared to reflect the option
>>>> (2) - see below
>>>>
>>>>
>>>> /**
>>>> * Obtains a confined copy of this memory segment whose owner
>>>> thread is set to the given thread. If the new owner thread
>>>> * differs from the current owner thread, as a side-effect,
>>>> this segment will be marked as <em>not alive</em>,
>>>> * and subsequent operations on this segment will result in
>>>> runtime errors.
>>>> * @param newOwner the new owner thread.
>>>> * @return a confined copy of this segment with given owner
>>>> thread.
>>>> * @throws UnsupportedOperationException if the segment is a
>>>> shared segment (see {@link MemorySegment#isShared()}).
>>>> */
>>>> MemorySegment asConfined(Thread newOwner) throws
>>>> UnsupportedOperationException;
>>>>
>>>> /**
>>>> * Obtains a shared copy of this memory segment which can be
>>>> accessed across multiple threads. If the current segment
>>>> * is not already shared (see {@link
>>>> MemorySegment#isShared()}), as a side-effect, this segment will be
>>>> marked as
>>>> * <em>not alive</em>, and subsequent operations on this
>>>> segment will result in runtime errors.
>>>> * The shared copy will also be marked as <em>pinned</em> (see
>>>> {@link MemorySegment#isPinned()});
>>>> * as such, any attempt to close the returned segment will
>>>> result in a runtime error.
>>>> * @return a shared copy of this segment.
>>>> */
>>>> MemorySegment asShared();
>>>>
>>>>
>>>> I think we can't do much better if we go with (2) - that is we
>>>> _have_ to say when the invalidating side-effect takes place. I'm
>>>> still not 100% that (2) is the way to go; IMHO (2) supports a
>>>> fiction that 'asConfined' can be used to assert confinement, rather
>>>> than to proactively change it. I think clients should use it
>>>> sparingly, especially now that we have full testing capabilities.
>>>>
>>>> Maurizio
>>>>
>>>> On 26/09/2019 19:10, Maurizio Cimadamore wrote:
>>>>> Hi,
>>>>> in a previous document [1] I explored the problem of allowing
>>>>> concurrent access to a memory segment in a safe fashion. From that
>>>>> exploration, it emerged that there was one type of race that was
>>>>> particularly nasty: that is, a race between a thread A attempting
>>>>> to close a segment S while a thread B is attempting to access
>>>>> (read or write) S.
>>>>>
>>>>> The presence of this race makes it really hard to generalize the
>>>>> existing memory access API to cases where concurrent/shared access
>>>>> is needed. Of course one naive solution would be to synchronize
>>>>> every access on the liveness check, but that makes performance
>>>>> really poor - which would defeat the point of having such an API
>>>>> in the first place.
>>>>>
>>>>> Instead, to solve that problem, in the document I posit about a
>>>>> solution which uses an explicit acquire/release mechanism - that
>>>>> is clients of a shared segment will need to explicitly acquire the
>>>>> segment in order to be able to operate on it, and release it when
>>>>> done. A shared segment can only be closed when all clients are
>>>>> done with the segment - this is what ensures temporal safety.
>>>>> Moreover, since each client works on its own 'acquired' copy of
>>>>> the shared segment, everything is a constant and the JIT can see
>>>>> through the code and optimize it in the same way as it does for
>>>>> confined access. That said, we never fully committed to that
>>>>> solution, since the resulting API was very complex: for things to
>>>>> work, part of the MemorySegment API has to be moved under a new
>>>>> abstraction (in the document called MemoryHandle) - more
>>>>> specifically the bits that are responsible for creating addresses.
>>>>> While it's possible to devise a confined segment that is both a
>>>>> MemorySegment and a MemoryHandle (thus giving us back the old
>>>>> API), the general feedback I've received is that this solution
>>>>> seems a bit too convoluted.
>>>>>
>>>>> When discussing about this problem with Jim, he pointed out a
>>>>> useful connection and a possible way out: after all, all these
>>>>> acquire/release and reference counting schemes are there to
>>>>> perform a job that a JVM knows exactly how to do at speed:
>>>>> determining whether an object is still used or not. So, instead of
>>>>> inventing new machinery, we could simply piggy back on the
>>>>> mechanisms we already have - that is GC and Cleaners.
>>>>>
>>>>> The key realization, in the shared case, can be summarized as:
>>>>> performance, safety, deterministic deallocation, pick two! Since
>>>>> we're not willing to compromise on safety, or on performance,
>>>>> letting go of the deterministic de-allocation goal (only for
>>>>> shared segments) seems a reasonable conclusion.
>>>>>
>>>>> In other words, there are now two kinds of segments: /confined/
>>>>> segment and /shared/ segments. A segment always starts off as
>>>>> confined, and has an owning thread. You can update the owning
>>>>> thread - effectively nuking the existing segment and obtaining a
>>>>> new segment that is confined on a new thread. This allows clients
>>>>> to achieve serialized thread-confinement use cases - where
>>>>> multiple threads operate on a piece of memory one at a time.
>>>>> Confined segments are operated upon as usual: you allocate a
>>>>> segment, you use it, you close it (or you use a try with resources
>>>>> to do it all automagically).
>>>>>
>>>>> If clients want more - e.g. full concurrent access, an API point
>>>>> is provided to turn a confined segment into a shared one. Again,
>>>>> what happens here is that the existing segment will be nuked, and
>>>>> a new shared segment will be created. But, this shared segment
>>>>> _cannot be closed_ (e.g. it is pinned, using the existing API
>>>>> terminology). So, how are off-heap resources released if we can't
>>>>> close the segment? Well, we let the GC take care of it - by
>>>>> registering the segment on a Cleaner, and have the cleaner call
>>>>> some cleanup code once the segment is no longer referenced (in
>>>>> reality, things are a bit different, in the sense that what we
>>>>> really key on is the _scope_ of a segment, which might be shared
>>>>> across multiple views, but the essence is the same). In other
>>>>> words, deallocation for shared segments works pretty much the same
>>>>> way deallocation of direct buffer work.
>>>>>
>>>>> With this move, we are able to retain the simplicity of the
>>>>> existing API, while also being able to support efficient and safe
>>>>> concurrent access.
>>>>>
>>>>> A webrev implementing this change is available here:
>>>>>
>>>>> http://cr.openjdk.java.net/~mcimadamore/panama/shared-segments_v2/
>>>>>
>>>>> Implementation-wise things are, I think, quite straightforward. I
>>>>> took sometime to refactor the code, to make the various scope
>>>>> subclasses disappear. We now have a single memory segment
>>>>> implementation and two scopes: shared and confined. The confined
>>>>> scope takes a 'Runnable' cleanup action which is used (i) when
>>>>> closing the confined segment or (ii) passed onto the Cleaner by
>>>>> the shared scope if the segment is upgraded to 'shared' state.
>>>>> Also, since shared segment now can now be picked up by Cleaner
>>>>> when no longer referenced, it is crucial that we add in
>>>>> reachability fences around Unsafe operations (same way as direct
>>>>> buffer does really). This is because sometimes the GC can
>>>>> aggressively collect unused objects stored in local variables
>>>>> during method execution. Adding these fences doesn't negatively
>>>>> impact performances (in fact, I'm told these fences are a no-op in
>>>>> Hotspot).
>>>>>
>>>>> I also took some effort to update some of the javadoc which are
>>>>> rendered invalid by this change.
>>>>>
>>>>> Comments welcome
>>>>>
>>>>> Maurizio
>>>>>
>>>>> [1] - http://cr.openjdk.java.net/~mcimadamore/panama/confinement.html
>>>>>
More information about the panama-dev
mailing list