resource scopes and close actions
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Wed Feb 9 02:42:15 UTC 2022
On 09/02/2022 02:19, Michael Zucchi wrote:
>
> Ok cheers that should probably do. I only just noticed you can use it
> as an addressable a couple of days ago but using it for non-symbol
> things isn't obvious especially given it's name and it's documentation.
>
> So it seems a 'typed' handle-like object that wanted to use a scope on
> close action will have to:
>
> - use NativeSymbol as the native 'this', which contains a copy of the
> MemoryAddress, a string and the scope handle.
> - keep a private copy of the MemoryAddress so any close function can
> use it after the scope is closed (whether it be in the object or in a
> lambda, it may as well be in the object)
yes
> - (based on the proposed api change) also keep a copy of the
> ResourceScope if it wants to be able to propagate it automatically
> which would often be desirable (... i think).
stay tuned on this. We're looking into ways to mitigate the impact of
the proposed changes which will not require you to keep a ResourceScope
around (and use a segment instead as a "proxy" of the temporal bounds
you want to use e.g. to create another segment).
>
> Doesn't seem too elegant but that should work? I'll explore that with
> opencl as i've the most experience with that api and it has some
> consistent conventions that i think will mesh well.
>
> e.g. as a first thought, does this look about right?
>
> An example case is CLCommandQueue which is an object which is created
> by and belongs to a CLContext and can be closed either by an explicit
> unref or when the context is unreffed.
>
> public CLCommandQueue {
> NativeSymbol symbol;
> ResourceScope scope;
> MemoryAddress addr;
>
> // package private
> close() {
> clReleaseCommandQueue$MH.invokeExact((Addressable)addr);
> }
> }
This looks mostly ok - not sure whether you need to keep the `addr`
around. I would have expected `addr` to only really be used in a close
action, so when you create CLCommandQueue (e.g. CLCommandQueue::create).
Am I missing something?
>
> public CLContext {
> NativeSymbol symbol;
> ResourceScope scope;
> MemoryAddress addr;
>
> public static CLContext create(..., ResourceScope scope) { ... }
>
> public CLCommandQueue createCommandQueue(, ...scope) {
> MemoryAddress addr = clContextCreateCommandQueue(...);
> CLCommandQueue q = CLCommandQueue.create(addr, scope);
>
> if (scope != this.scope)
> this.scope.addCloseAction(() -> scope.close());
> scope.addOnCloseAction(()-> q.close());
>
> return q;
> }
> }
>
> I have to think about it a bit more though, there are some cases where
> this might not be a good fit. Or you end up having create a scope for
> single objects and now have to keep track of two references (or add
> some public close() which calls scope.close()). Also, dynamic method
> handles add a wrinkle if they are a release() function.
Creating a scope for single objects doesn't seem optimal - scopes are
mostly intended to aggregate resources together (which share a common
lifecycle).
Overall though, there's no real right or wrong - the API gives you
option when it comes to dealing with lifetime of entities - it is mostly
a choice of the API designer whether to take advantage of that, or
ignore that (in order to expose a simpler API). I also expect that some
libraries might be more scope-friendly than others. Given that C is so
varied when it comes to management of resource lifecycle, that should
also not come too much as a surprise.
>
> My JNI code uses reference queues so both explicit and gc-release are
> possible, this is particularly handy for lambda-based processing
> chains where there's no 'object' to hold references around and
> ownership is basically passed by argument in a graph of unpredictable
> ways. A common shared scope with a cleaner should work i think but
> that would require a different design.
In general, starting off with single scope, GC-managed, sounds like a
good default choice - and then sprinkle scopes as you see needs to
deallocate more promptly and/or add more fine-grained lifecycle
management to your library (assuming that is required). Btw, note that a
GC-backed scope can still be released explicitly, if you so wish (so you
have both options at once).
Cheers
Maurizio
>
> !Z
>
> On 7/2/22 21:19, Maurizio Cimadamore wrote:
>> If you want a "scoped" memory address, please look at NativeSymbol.
>> This is used by the lookup functions provided by the JDK, but is also
>> useful to capture an address and associate a scope with it. This
>> allows you to model everything with one field (NativeSymbol) and then
>> no liveness check is needed, just pass the symbol to the method
>> handle, and standard safety checks will apply.
>>
>> Maurizio
>>
>> On 05/02/2022 09:16, Michael Zucchi wrote:
>>> OpenCL (& Vulkan) does everything via handles (typed anonymous
>>> pointers), so presumably represented by MemoryAddress. What
>>> approach to use here to make them scope-safe? Have explicit scope
>>> tests before using them in calls?
>>>
>>> If one wanted a scope-protected MemoryAddress this seems to be the
>>> only solution in the present api:
>>>
>>> CLCommandQueue {
>>> ResourceScope scope;
>>> MemoryAddress handle;
>>>
>>> void enqueueNDRangeKernel(...) {
>>> try (ResourceScope scope = ResourceScope.newConfinedScope()) {
>>> scope.keepAlive(this.scope);
>>> enqueueNDRangeKernel$MH.invokeExact((Addressable)handle, ...);
>>> } catch (IllegalStateException ex) {
>>> }
>>> }
>>> }
>>>
>>> Actually it's a bit worse because there will be handles (possibly in
>>> different scopes) in the argument list and they would all need to be
>>> checked in the same way.
>>>
>>> So for practicality they would have to be MemorySegment (ideally
>>> length=0) since they are always checked?
>
More information about the panama-dev
mailing list