OpenSSL and panama-foreign

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Mon Dec 6 14:26:38 UTC 2021


>
> After our last discussion I thought some more about this. Essentially, 
> a cleaner's cleanup action can not (directly or indirectly) reference 
> the object being cleaned up. If we were able to do that, we could 
> potentially resurrect the object being cleaned up, and any other 
> object that it references as well, which might also already be dead 
> (no guarantee on the order in which objects are cleaned).
>
> This applies to implicit scopes as well: a scope cleanup action can 
> not reference the scope. We register the upcall stub on a scope with a 
> cleaup action, so the method handle can not reference the scope 
> either. I don't think there's a way around that, even if we dropped 
> the use of a global JNI handle, there's still a need for the stub to 
> reference the method handle, and keep it alive.
Well, one might argue that, in principle, we could use weak references 
to the MH in the upcall stub. Then, when an upcall is called we check if 
the MH is there, if not we fail. The problem is that this check would 
occur _inside_ the upcall stub assembly code itself, which is cleared 
when the scope goes away. So there's no guarantee that we will have a 
chance to check that the native code is dealing with a stale function 
pointer (unless we leave the upcall stub around even after the upcall 
scope has been closed, which doesn't seem desirable).
>
> I think for cases where a cycle is an absolute requirement, where the 
> method handle of an upcall stub references the scope the upcall stub 
> is registered against, the solution is to have an 'outside observer' 
> object, which isn't a part of the cycle itself, and register a cleanup 
> action on that object instead:
>
>     observer -\
>                      scope -> ... -> upcall stub -> ... -> scope
>
> The scope would be a shared scope instead of an implicit scope, and a 
> cleanup action would be registered on the observer object which would 
> close the scope. When the cleaner runs, the entire cycle would be 
> released at the same time.

Yes, this is a workable approach.

I think implicit scopes can be handy esp. when transitioning from 
ByteBuffer code (given that ByteBuffer has a cleaner-based deallocation 
policy). But when putting together complex scopes, especially in the 
case of native interop, it might be difficult to prove that your 
application is doing exacty what you need it to be doing, when relying 
on implicit semantics. In these cases I found that using explicit scopes 
is almost always a superior solution, given that it's very clear when 
things are supposed to be taken care of (but of course this comes at an 
extra burden for the developer).

When I initially looked at your usage of the API, it seemed like an 
implicit scope was what you needed - but the more I kept looking at it, 
the less I'm convinced that having a single scope with cleaner is a 
workable solution in your case. In your case, the "observable" approach 
(e.g. don't register the scope with the cleaner, register some other 
object which, when unreachable, will cause its cleanup action to close() 
its scope field), seems a more robust approach.

In a way, your "observer" is a "witness" for the scope object; in 
principle the API could feature an overloaded implicit factory, like this:

ResourceScope.newImplicitScope();
ResourceScope.newImplicitScope(Object witness);

But I'm not sure if the benefit it's worth the API surface (after all, 
this setup is possible with the existing API, at the cost of managing 
the cleaner object/close calls explicitly).

What I think is required, is better documentation on the implications of 
using a scope associated with a cleaner (especially in the context of 
upcalls).

Maurizio

>
> Jorn
>


More information about the panama-dev mailing list