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