resource scopes and close actions

Michael Zucchi notzed at gmail.com
Wed Feb 9 02:19:24 UTC 2022


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

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);
     }
}

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.

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.

  !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