Funneling Objects through void*

Jorn Vernee jorn.vernee at oracle.com
Thu Jun 15 19:04:20 UTC 2023


Hey Johannes,

Some thoughts:

 > Of course it is trivial to write such functionally using a counter 
and a ConcurrentHashMap, but that is not the only possible implementation.

 > In fact the JNI functions `newGlobalRef` and `deleteGlobalRef` would 
provide an adequate implementation.

I'll note that newGlobalRef/deleteGlobalRef is implemented with a global 
store (look e.g. for OopStorage::allocate [1]), and the object handle is 
a pointer into that store. I.e. it is not too different from having a 
global Map<Long, Object> somewhere, then passing the `long` (analogous 
to the JNI handle) around through native code, and 'resolving' it 
through Map::get when getting back into Java. Global refs are also a 
generically applicable mechanism, and I suspect that most use cases 
would benefit from a more custom tailored solution for that particular 
use case. Though, I agree that the problem exists (and it's something 
we've run into as well [2])

I think we mostly discussed adding direct support for Object -> JNI 
handle conversions in order to interoperate with native functions that 
depend on JNI (such as the native AWT API from that SO question you linked).

 > Currently I have to create a new upcall stub for every invocation

What is the problem with doing this? Is it cumbersome? Or does it seem 
wasteful? WRT the latter, we still have some options for making upcall 
stub allocation with the same FunctionDescriptor share the same VM stub 
(mostly). Though, we already share the Java wrapper class at the moment.

In both of these cases, I wonder if the best solution we can provide is 
a more intuitive/convenient way of addressing these use cases, rather 
than something which necessarily has a better implementation than what a 
user can do today.

Jorn

[1]: 
https://github.com/openjdk/jdk/blob/3e0bbd290c534b0f9729c54cd45308d505907797/src/hotspot/share/gc/shared/oopStorage.cpp#L436
[2]: 
https://github.com/openjdk/jextract/blob/cf3afe9ca71592c8ebb32f219707285dd1d5b28a/src/main/java/org/openjdk/jextract/clang/Cursor.java#L194

On 15/06/2023 18:05, Johannes Kuhn wrote:
> When trying to help this StackOverflow question[1], I tried to use 
> `EnumWindows`[2]. EnumWindows takes a callback and a void* parameter 
> and invokes the callback using the toplevel HWND & the passed void* 
> parameter.
>
> Currently I have to create a new upcall stub for every invocation, but 
> if I could funnel some java.lang.Object through the void* parameter, I 
> would only need to create a single upcall, where I retrieve the 
> object, cast it and call some method on it.
>
> Example (pseudo) code:
>
>     public interface EnumWindowsCallback {
>         int callback(long hwnd);
>     }
>     private static int enumWindowsUpcall(long hwnd, MemoryAddress obj) {
>         return (int) ((EnumWindowsCallback) 
> toObject(obj)).callback(hwnd);
>     }
>
>     public static int enumWindows(EnumWindowsCallback cb) throws 
> Throwable {
>         try (Arena arena = Arena.ofConfined()) {
>             return (int) 
> ENUM_WINDOWS_MH.invokeExact(ENUM_WINDOWS_UPCALL, toPointer(arena, cb));
>         }
>     }
>
> One problem is that the arena is not accessible within the callback.
>
>
> Of course it is trivial to write such functionally using a counter and 
> a ConcurrentHashMap, but that is not the only possible implementation.
>
> In fact the JNI functions `newGlobalRef` and `deleteGlobalRef` would 
> provide an adequate implementation.
>
> - Johannes
>
> [1]: 
> https://stackoverflow.com/questions/75620948/how-do-i-get-the-hwnd-of-a-canvas-using-panama/
> [2]: 
> https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumwindows


More information about the panama-dev mailing list