dissecting the Scope API
David Holmes
david.holmes at oracle.com
Sat Dec 22 22:57:01 UTC 2018
On 23/12/2018 8:02 am, Maurizio Cimadamore wrote:
> Hi David,
> yes, I knew about these articles as Alex pointed me at them few months
> ago - but thanks for the reference.
Okay. I have a lot of experience with this in RTSJ. It really was a
minefield.
> For the records, Loom is also exploring similar directions for managing
> lifecycle of groups of logically connected fibers.
Yes have touched based there as well.
Cheers,
David
> Maurizio
>
> On 22/12/2018 06:53, David Holmes wrote:
>> Hi Maurizio,
>>
>> On a casual read aspects of this are sounding like RTSJ MemoryAreas
>> [1][2][3], which define allocation contexts which allow for a
>> bounded/scoped lifetime (and very strong rules about allowing things
>> to be referenced from other scopes).
>>
>> Are you aware of those? John may have mentioned them.
>>
>> Cheers,
>> David
>>
>> [1] http://www.rtsj.org/specjavadoc/book_index.html
>> [2]
>> https://docs.oracle.com/javase/realtime/doc_2.1/release/rtsj-docs/javax/realtime/MemoryArea.html
>>
>> [3] http://janvitek.org/pubs/isorc04.pdf "Real-Time Java Scoped
>> Memory: Design Patterns and Semantics", by Vitek, Pizlo, Fox & Holmes
>>
>>
>> On 22/12/2018 10:18 am, Maurizio Cimadamore wrote:
>>> Hi,
>>> as mentioned in a recent email, I think the Scope API is conflating
>>> several aspects together, which makes it difficult to discuss it
>>> and/or evolve it into a more stable API. This email is an attempt at
>>> teasing apart the various aspects of the Scope API; while it's not
>>> meant to be a full proposal, I think there are many ideas in here
>>> worthy of more consideration. Much of the credits for the ideas in
>>> here go to Steve Dohrmann from Intel and Jorn Vernee who recently
>>> started a discussion on this [1]; Henry Jen also provided some
>>> insights into the dual nature played by the Scope API.
>>>
>>> So, before jumping into some pseudocode, let's review some basic key
>>> points:
>>>
>>> * I believe Scope is condensing two aspects: it has an allocation
>>> interface (Scope::allocateXYZ), but it also serves as a life-cycle
>>> management. While in the end we might (or not) end up to conflate the
>>> two, let's try to keep them separated for now
>>>
>>> * Let's try to get there in layers (as we did for the SystemABI);
>>> there are clearly low level allocators - e.g. things that just give
>>> you a slab of memory - called memory regions - and high-level
>>> allocators, e.g. things that can allocate user defined data (structs,
>>> arrays, etc.)
>>>
>>> * We'd like the approach to scale, if possible, to different kind of
>>> memories (heap, offheap, NVM, ...)
>>>
>>> * Let's double down on the Pointer abstraction to do the memory
>>> dereference; e.g. let's not add methods to a memory abstraction a la
>>> Unsafe (get/putLong, ...) instead, let's have a way to get a Pointer
>>> out of a memory region
>>>
>>> So, let's start... the first thing we need is something that creates
>>> memory regions:
>>>
>>> interface MemoryStore {
>>> MemoryRegion allocate(long size);
>>> MemoryRegion allocate(Access access, long size);
>>> void free(MemoryRegion region);
>>>
>>> interface Access {
>>> READ, WRITE, READ_WRITE;
>>> }
>>>
>>> static MemoryStore offheapStore() { ... }
>>> static MemoryStore heapStore() { ... }
>>> ... //others?
>>> }
>>>
>>> So far so good, we can create regions, with given access etc. Note
>>> that here I'm trying to hide the addressing model used by Unsafe
>>> (Object + long offset); after all, the addressing model depends on
>>> the kind of memory you are operating on, so I don't think it's good
>>> to expose it via the API (if we can avoid doing so).
>>>
>>> What is a memory region? Here:
>>>
>>>
>>> interface MemoryRegion {
>>> MemoryStore store();
>>> Pointer<?> basePointer();
>>> //maybe ByteBuffer accessor too?
>>> boolean checkAlive();
>>> boolean checkAccess(Memory.Access access);
>>> long size();
>>> }
>>>
>>> As Jorn (and separately, Steve) suggested, this could be the place
>>> where we add the logic for liveness check - more specifically, a
>>> memory region is considered alive unless freed on its corresponding
>>> store. Interestingly, a memory region has a way to retrieve a pointer
>>> to its base location. We can assume it will be a void pointer and
>>> that the client will have to cast accordingly; this is, after all, a
>>> low level API. This is also a very general API - it's not really
>>> specific to Panama - other than the fact that a memory region can be
>>> projected to a Pointer - but we can imagine other useful projections
>>> too (e.g. Bytebuffers).
>>>
>>> What about high-level allocation? This is where Panama-related
>>> concepts start to kick in:
>>>
>>>
>>> abstract class Allocator {
>>> Allocator(MemoryStore store);
>>>
>>> public <Z> Pointer<Z> allocate(LayoutType<Z> type);
>>> public <Z extends Struct<Z>> allocateStruct(Class<Z> strClass);
>>> ... //other allocation factories
>>> }
>>>
>>> So, an allocator takes a store, and then implements the various
>>> allocation functions; it is easy to see how such allocation functions
>>> can be implemented: they will typically obtain a region (using the
>>> store) of a given size (given by some LayoutType), then obtain a
>>> pointer from the region and cast it to the desired type.
>>>
>>> What about scope-ness? Let's start from here:
>>>
>>> abstract class ScopedAllocator extends Allocator implements
>>> AutoCloseable {
>>> ScopedAllocator(MemoryStore store);
>>> public void close() //this makes sure that all regions created
>>> within this 'scope' are automatically freed
>>> }
>>>
>>> So, this is a special allocator that also supports the AutoCloseable
>>> interface, and can therefore be used with a try-with-resources, in
>>> the usual way. In other words, clients will often do things like:
>>>
>>> try (Scope sc = new ScopedAllocator(MemoryStore.offheapStore())) {
>>> sc.allocateStruct(...)
>>> }
>>>
>>> You can think of this as an allocator that keeps track of the regions
>>> it creates, and frees them as soon as you call close().
>>>
>>> I think this looks more or less equivalent to what we have now, but
>>> we have captured and broken down the primitive concepts more clearly
>>> now. There's a *store* that is responsible for allocating regions
>>> (all relatively Panama-agnostic), and high level allocators, built on
>>> top of stores, which provide the user facing allocation facilities.
>>> Scoped allocators keep track of memory region usages, to allow for
>>> automatic collection upon closing.
>>>
>>> One remarkable thing is that Scope/Resource have disappeared from
>>> this API - the features available in the current Scope interface have
>>> been split between memory regions and allocators. I think there could
>>> be ways to add back explicit scopes into this flavor of the API - but
>>> I think that, before we jump to that, it's better to stop and ask -
>>> what are the use cases that would not be covered by this proposal?
>>>
>>> Maurizio
>>>
>>> [1] -
>>> http://mail.openjdk.java.net/pipermail/panama-dev/2018-December/003572.html
>>>
>>>
>>>
More information about the panama-dev
mailing list