resource scopes and close actions

Michael Zucchi notzed at gmail.com
Wed Feb 9 04:23:38 UTC 2022


On 9/2/22 13:12, Maurizio Cimadamore wrote:
>
> 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).

What if you have a NativeSymbol instead?

>>
>> 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?

Only because it needs to be stored somewhere, either in the lambda or 
the object.  I was thinking of having the ability to implement an 
idempotent close at the time so you can't just have it in the lambda, 
but I don't think that will be possible with scopes - unless you have 
scope-per-object which as you mentioned isn't optimal. Otherwise you're 
basically just back to a stale object on an open scope and would need 
more checks every time you use it.  I was also thinking broader such as 
the ffmpeg AVFormatContext example earlier where some other object state 
defines which close to use.

An implementation that used a cleaner would have to do something else 
anyway.

>
>>
>> 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).
>

The case i'm trying to solve is where you don't really know the 
lifecycle of the object.  With opencl you use reference counting to hold 
objects as long as you need them so they can change ownership and/or be 
shared amongst different functions and libraries in ways the caller 
can't control.  My JNI wrapping hid all that by just using java 
reference gc to effectively do the same thing as far as java was concerned.


I've also made an embarrassing mistake, closing the context doesn't 
release it's objects, in opencl they all have a reference count on the 
context so the other discussion is a little moot.  I believe it does in 
vulkan although it's an error to close the instance before all of it's 
objects are closed.

Oh well i'll have to about it more.  Implicit scopes wont be enough in 
the case of such an api unless each object also retains a java reference 
to it's parent - leaving it up to the application isn't really safe 
enough is it?


Just FYI command queues are not short lived, a simple app might be:

create a context
create a small number of queues
create your kernels

then set up a processing pipeline - could be multiple, could come and go:
create some memory buffers and images
maybe create some specific kernels

then execute a processing pipeline - this will happen multiple times, 
and an individual execution could span multiple threads:
execute kernels, track execution using events - some will have the 
lifetime of the pipeline, others a fragment of it, perhaps allocate/free 
temp image/memory along the way

close everything in reverse

Given the realisation of my mistake on how the refcounting works in 
opencl i think this should be covered by a few relatively simple scope 
mechanisms under application control.

  !Z



More information about the panama-dev mailing list