[foreign] RFR 8217420: Have UniversalNativeInvoker and UniversalUpcallHandler create (and close) the scopes used by boxing code
Jorn Vernee
jbvernee at xs4all.nl
Mon Jan 21 21:31:38 UTC 2019
Maurizio Cimadamore schreef op 2019-01-21 22:16:
> Hi Jorn,
> I think I agree with the spirit of the fix. I like that this makes the
> scopes stricter - which ended up uncovering some issues.
>
> Again, I have to point out that this fix only restrict things for
> universal invoker mode - I believe that when using the direct
> invocation scheme, parameters to upcalls will be passed in leaky
> scopes, which will not reveal the clang-related failure.
Sorry about that, I wasn't sure before if I was supposed to make changes
to the direct invoker as well, since I would have no way of testing
those changes locally. (I've put it on my checklist for the future).
> I think I'd like to see a specific test for lifecycle of callback
> args, so that we can easily run it with all possible invoker modes and
> make sure it passes.
I added a test for that (CallScopeTest), is there something else you'd
want to have tested? AFAICT this lifecycle problem currently only exists
with structs.
Jorn
> Maurizio
>
> On 21/01/2019 18:13, Jorn Vernee wrote:
>> Hi, another patch :)
>>
>> From the bug description:
>>
>> Currently, when (un-)boxing code needs to do an allocation a Scope is
>> created for that, and this Scope is then leaked.
>>
>> For an up-/down-call there are 2 kinds of scopes; 1) for values being
>> passed as arguments by value, which are valid for the duration of the
>> call. 2) for values being returned, which are up to the caller to
>> manage.
>>
>> We can do better than the current scheme of always leaking, by having
>> UniversalUpcallHandler and UniversalNativeInvoker create both of these
>> types of scopes, pass them to boxing code, and then close scopes of
>> type 1. when the call is finished.
>>
>> This will reduce the amount of memory being leaked, and could also
>> help identify bugs where references to temporary objects are leaked.
>>
>> ---
>>
>> After implementing this, it turns out that UniversalUpcallHandler only
>> needs a Scope for the arguments being passed to the java method being
>> called. For returns there should be no allocations; either the value
>> is being passed in registers, or written to a pointer passed in by the
>> caller.
>>
>> I did discover a problem with this patch in the TestJextractFFI patch
>> code:
>>
>> public Stream<Cursor> children() {
>> final ArrayList<Cursor> ar = new ArrayList<>();
>> // FIXME: need a way to pass ar down as user data d
>> try (Scope sc = Scope.newNativeScope()) {
>> LibClang.lib.clang_visitChildren(cursor,
>> sc.allocateCallback((c, p, d) -> {
>> ar.add(new Cursor(c));
>> return LibClang.lib.CXChildVisit_Continue();
>> }), Pointer.nullPointer());
>> return ar.stream();
>> }
>> }
>>
>> According to C semantics, the argument to the lambda `c`, which is a
>> CXCursor struct being passed in by-value, is only valid for the
>> duration of the call. The Windows ABI depends on this fact, because it
>> allocates the struct on the stack of the caller, and then passes a
>> pointer to the callee (the lambda in this case). I think on SysV a
>> by-value struct is always passed in register + stack?
>>
>> But, the `c` argument is being leaked from the lambda, by wrapping it
>> in a Cursor and storing it in a list in the enclosing scope. When the
>> call to the lambda ends, the struct is deallocated and the pointer
>> that is stored on the Java side is now dangling. I've initially solved
>> the crash this was causing by defensively copying the struct in the
>> Windows boxing code (instead of just wrapping the passed pointer), but
>> I realized this also means that a user would have to manually release
>> a struct passed into an upcall to avoid a memory leak.
>>
>> We have 2 options here:
>>
>> 1.) Go with C style. Struct arguments are valid only during an upcall,
>> and we have to manually copy them if we want to 'leak' them from the
>> scope of the upcall.
>> 2.) Go with status quo. Struct doesn't need to be copied manually when
>> saving it outside the scope of the upcall. The copy is defensively
>> done in Windows boxing code instead, and sometimes redundant. Only on
>> Windows (for now), struct arguments to an upcall have to be manually
>> released to avoid a memory leak.
>>
>> I think 1 is the better option here since this adheres to the
>> expectations native code has when calling into Java. I've reflected
>> that in the patch as well, but of course I'm interested in hearing
>> other opinions as well :) Note that the crash I was seeing before no
>> longer happens since the Scope used in the boxing code is now closed
>> after the call, and we get an exception instead.
>>
>> Bug: https://bugs.openjdk.java.net/browse/JDK-8217420
>> Webrev:
>> http://cr.openjdk.java.net/~jvernee/panama/webrevs/8217420/webrev.00/
>>
>> Cheers,
>> Jorn
More information about the panama-dev
mailing list