[foreign-memaccess] on confinement
Jorn Vernee
jbvernee at xs4all.nl
Wed Jun 5 13:59:21 UTC 2019
One other thing I realized; closing the root segment through a view
segment (like proposed before) is only possible when the root segment
and _all_ view segments are confined to the same thread. At least if we
want to avoid synchronization on access when checking liveliness. I
think this gets us the following set of rules for non-shared segments:
1. Terminal operations are always thread confined (safety feature to
prevent VM crashes when resource is freed by another thread).
2. Always confined to the same thread (avoid mutable fields, complexity
in implementation).
3. We can close the root segment through a view segment.
4. We can not share a view segment with a different thread (would break
rule 1. when combined with 3.).
5. No need for the user to keep a reference to the root segment, since
we can close it through a view segment.
6. No need for subsegment tracking.
Also, shareability should be an opt-in, but it seems that supporting
lazy transition into a shared state (with asConfined()) creates too much
complexity for the simple single-threaded case, so I think it should be
an opt-in at segment creation time. That way we can keep the 'default'
single threaded implementation fast and simple.
---
We could still go with a separate ShareableSegment type, which does
allow sharing of view segments with other threads, but does not allow
closing the root segment through a view segment. To avoid mutable
confinement thread fields we can require the confinement thread to be
specified when creating the view segment. A strawman:
interface ShareableSegment extends MemorySegment {
MemorySegment resize(Thread confinementThread, long offset, long
length); // support 'divide et impera'.
default MemorySegment resize(long offset, long length) {
return resize(Thread.currentThread(), offset, length);
}
void merge(MemorySegment subsegment); // could do automatically
with GC + Cleaner as well
// need some synchronization if resize and merge can be called
by other threads then the root's confinement thread
// ... factory methods
}
Which gets us the following rules for shareable segments:
1. Terminal operations are always thread confined (safety feature to
prevent VM crashes when resource is freed by another thread).
2. Always confined to the same thread (avoid mutable fields, complexity
in implementation).
3. View segments can be confined to different threads than the root
segment.
4. We can not close the root segment through a view segment (would break
rule 1 when combined with 3).
5. The user must keep a reference to the root segment at all times to be
able to close it and avoid resource leaks.
6. Need to track subsegments in order to know whether the root segment
can be closed safely.
---
Also, overlap of subsegments will break confinement in the sense that
multiple threads can write/read to/from the same region, but since
subsegments owned by multiple threads can not free/release the
underlying resource, I don't think overlapping subsegments could crash
the VM. So, maybe it's good enough to tell the user to make sure that
subsegments owned by different thread's don't interfere which each
other, but we don't enforce that in the implementation?
If we go that route I believe we can make the subsegment tracking for
ShareableSegment a simple AtomicLong reference count. Where the
liveliness flag in a subsegment is a reference to the root segment, that
is nulled out when merging, and also used to make sure that merge is
called with an actual subsegment.
Jorn
Maurizio Cimadamore schreef op 2019-06-05 02:16:
> On 04/06/2019 17:03, Maurizio Cimadamore wrote:
>> Note: I'm not saying this will be trivial to implement correctly - but
>> what I like about this is that the programming model will look
>> relatively clean in comparison to something like (1). Essentially you
>> can slice and dice all you want, and, as long as you are asking
>> reasonable questions, things will work with decent performances.
>
> Quick update; I've been doing some experiment on this - it doesn't
> look pretty for now.
>
> Some of the issues we have to take into account:
>
> * as discussed, we want the master region to somehow keep track (via
> its mutable 'scope-like' object) of the sub-regions
>
> * if we share the same scope for all subregions (which we probably
> want to avoid too much allocation on resize) then we need to have a
> way for the sub-region to perform an efficient confinement check - one
> trick I used was to give each sub region an unique index, and then use
> the index to access a subregion 'ownership' array
>
> * we need to take into account regions being GCed - otherwise the
> lists kept into the master region will (potentially) grow w/o bounds
>
> * we need to take into account synchronization when adding/removing
> sub-regions - this is probably not a big concern given that these
> operations occur during a 'resize' or when a region is being GC, so
> the memory access itself can still be fast
>
> * since we can transfer ownership, the owner thread is not a final
> constant anymore... this will probably affect performances
> considerably
>
> * I haven't even started to look at rejecting overlapping sub regions
> with different owners...
>
> Needless to say, the resulting implementation is very finicky, and I'm
> worried about the overall performance model of this approach.
>
> Also, I don't think that what I'm seeing is an artifact of lumping
> MemoryScope and MemorySegment together - yes, in principle having a
> separate scope (with a notion of confinement in it) helps in the sense
> that resizing a segment becomes an orthogonal concern. But then you
> are back in a world where you can't give a different thread owner to
> different sub-region, and the only way around that restriction is to
> use memory copy (e.g. create a new segment and copy contents of the
> old one to the new).
>
> If that cross-subregion policy is what we realistically want to
> enforce, then I don't think it's worth doing a lot of heroics here -
> we can simply say that a segment is confined to a thread, there's no
> ownership transfer operation, but the same effects can be achieved
> through memory copy. This doesn't seem quite a rich a story as the one
> we were looking at - but if we were ok with Scope being in charge of
> thread confinement, this would have been the only story possible.
>
> So, the question becomes: do we really need a way to transfer
> ownership of a segment from thread A to thread B ? And if so, what
> granularity should be used? I think these are the possible answers:
>
> a) ownership transfer not supported - region copy should be used as a
> workaround
> b) ownership transfer supported; all subregion are constrained to have
> same owner as the root; when ownership changes, all subregions change
> ownership too
> c) ownership transfer supported; subregion ownership can set
> independently of the root
>
> I realized that, in the email I've sent this morning I picked the most
> difficult point in the design space (c) - that is, support ownership
> transfers at the subregion granularity. This seems useful to implement
> divide and conquer algorithms, but at the same time, I realized, this
> was simply not possible with the scope-based solution we had before
> (since all subregions had same scope there - hence same confinement).
>
> In other words, all the implementation strategies we've seen so far
> are capable of handling either (a) or (b) [as for (b) I'm not sure
> about the potential JIT cost in making thread owner non-final]. The
> implementation story for (c) is far more convoluted (**), and I'm very
> skeptical that, even if we can pull that off, it will perform in a way
> that will be deemed acceptable.
>
> Is (c) simply asking for too much? And, if so, is (b) something that
> could be useful still?
>
> Maurizio
>
> (**) Honestly, the overlapping region check seems the straw that
> breaks the camel's back - to implement the check it's sadly
> unavoidable to keep all subregions which share the same root in the
> same place - which then poses aforementioned problems with respect to
> such subregions being GCed, and need for synchronization when
> maintaining all the ancillary lists. And, this overlapping region
> check is needed in both the approached (1) and (2) that I have
> outlined earlier in [1], I believe.
>
> [1] -
> https://mail.openjdk.java.net/pipermail/panama-dev/2019-June/005674.html
More information about the panama-dev
mailing list