[foreign] RFR 8217420: Have UniversalNativeInvoker and UniversalUpcallHandler create (and close) the scopes used by boxing code

Jorn Vernee jbvernee at xs4all.nl
Tue Jan 22 11:15:19 UTC 2019


Comments inline...

John Rose schreef op 2019-01-22 01:47:
> On Jan 21, 2019, at 10:13 AM, Jorn Vernee <jbvernee at xs4all.nl> wrote:
> 
>> 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 wouldn't call #1 C style, but rather ABI style.  C style allows
> by-value
> passes of structs in all directions, with zero concern for pointer
> scoping,
> because the pointers are kept invisible.
> 
> I think #2 is closer to C style, properly understood, but it's not
> perfect
> either if it requires manual deallocation.
> 
> So, calling #1 ABI style, we then also have:
> 
> 3.) Go with C style.  Struct arguments (as opposed to pointers to
> structs)
> are passed by value and all appearances in Java exhibit value-like
> semantics,
> which includes no lifetime constraints.  The struct is copied onto the
> Java
> heap, defensively, and is preserved there with no writability.  If we
> had
> true Valhalla value types, it could be a value object instance, but it
> should
> be at least a value-based class.

I have not been keeping up with Valhalla lately.

Having value-like semantics in Java would mean embedding the bit-image 
directly into the value-type object right? I thought the tech was not 
there yet for this. I think you'd need some kind of fixed-length long[] 
that is fused into the struct object and gets copied along with it? This 
has the same problem that a user would need to create an off-heap copy 
if they wanted to pass a pointer to it to a C function (that you 
mentioned later). Or could there be support for direct pointers to value 
types, as alternative to the Object + offset scheme in Unsafe right now? 
(since they don't get moved by the GC, right?).

> There's still a trade-off here, as usual.  Putting the struct
> bit-image
> on the Java heap means it is safe to share across threads and across
> scopes, and will be GC-ed when no longer in use.  That's all good.
> The downside is you can only use it for further by-value actions,
> such as passing it as an argument later on to a down-call or storing
> it bitwise into a container.  If you need to pass an address for the
> struct data to a C function, that address needs to point to a scoped
> off-heap copy of the struct value.  Internally, at the ABI level, the
> by-value calling sequence will almost certainly need to recopy the
> struct into registers or into a stack buffer:  But that's what C does
> anyway, so that copy doesn't really bear on the decision to store
> struct values on-heap or off-heap.
> 
> Also, design issues for arguments to up-calls are often paralleled
> with returns from down-calls, and it's probably true in this case.
> Whatever policy we go with for by-value structs should apply
> equally to up-call arguments and down-call returns.

The way I think this would work for down-call returns is that; a 
by-value struct return constitutes a transfer of resources to the 
caller. In native code this is also true, but the returned value 
immediately gets automatic storage duration, because it's a value type. 
But, on the Java side the value resides (currently) off-heap, and 
_someone_ has to manage this resource. So I feel we'd need to teach 
users that they have to keep that in mind (maybe with some 
@ResourceTransfer annotation).

In the patch this is asymmetrical with upcalls, and I didn't find a way 
of doing something automatic for struct by-value returns yet. I 
experimented with tying the returned value's Scope to the lifetime of 
the returned Struct object using a Cleaner. But, I realized this also 
means that whenever you want to pass a pointer to the struct to a C 
function it's up to the caller to keep the object alive, and if the 
passing of this pointer is meant as a resource-transfer then you'd 
really want a way to detach the object from the Cleaner again (which I 
don't think there is?).

Having symmetry for down-/up-calls seems important to me as well for 
simplicity's sake, and I'm starting to see the appeal of the status quo 
more and more (i.e. defensive copy of up-call args). If we go with that 
I think it's important to tell the developer when we're transferring a 
resource to them, and it's up to them to manage it.

Jorn


More information about the panama-dev mailing list