All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.lwjgl.system.Callback Maven / Gradle / Ivy

The newest version!
/*
 * Copyright LWJGL. All rights reserved.
 * License terms: https://www.lwjgl.org/license
 */
package org.lwjgl.system;

import org.jspecify.annotations.*;
import org.lwjgl.*;
import org.lwjgl.system.libffi.*;

import java.lang.reflect.*;
import java.util.concurrent.*;

import static org.lwjgl.system.APIUtil.*;
import static org.lwjgl.system.Checks.*;
import static org.lwjgl.system.MemoryStack.*;
import static org.lwjgl.system.MemoryUtil.*;
import static org.lwjgl.system.jni.JNINativeInterface.*;
import static org.lwjgl.system.libffi.LibFFI.*;

/**
 * Base class for dynamically created native functions that call into Java code.
 *
 * 

Callback instances use native resources and must be explicitly freed when no longer used by calling the {@link #free} method.

*/ public abstract class Callback implements Pointer, NativeResource { private static final boolean DEBUG_ALLOCATOR = Configuration.DEBUG_MEMORY_ALLOCATOR.get(false); private static final ClosureRegistry CLOSURE_REGISTRY; private interface ClosureRegistry { void put(long executableAddress, FFIClosure closure); FFIClosure get(long executableAddress); FFIClosure remove(long executableAddress); } static { // Detect whether we need to maintain a mapping from executable addresses to FFIClosure structs. try (MemoryStack stack = stackPush()) { PointerBuffer code = stack.mallocPointer(1); FFIClosure closure = ffi_closure_alloc(FFIClosure.SIZEOF, code); if (closure == null) { throw new OutOfMemoryError(); } if (code.get(0) == closure.address()) { apiLog("Closure Registry: simple"); // When the closure address matches the executable address, we don't have to maintain any mappings. // We can simply cast the executable address to ffi_closure. This is true on many platforms. CLOSURE_REGISTRY = new ClosureRegistry() { @Override public void put(long executableAddress, FFIClosure closure) { // no-op } @Override public FFIClosure get(long executableAddress) { return FFIClosure.create(executableAddress); } @Override public FFIClosure remove(long executableAddress) { return get(executableAddress); } }; } else { apiLog("Closure Registry: ConcurrentHashMap"); CLOSURE_REGISTRY = new ClosureRegistry() { private final ConcurrentHashMap map = new ConcurrentHashMap<>(); @Override public void put(long executableAddress, FFIClosure closure) { map.put(executableAddress, closure); } @Override public FFIClosure get(long executableAddress) { return map.get(executableAddress); } @Override public FFIClosure remove(long executableAddress) { return map.remove(executableAddress); } }; } ffi_closure_free(closure); } } /** Address of the native callback handler that will call the Java method when the native callback function is invoked. */ private static final long CALLBACK_HANDLER; static { // Setup the native callback handler. try { CALLBACK_HANDLER = getCallbackHandler(CallbackI.class.getDeclaredMethod("callback", long.class, long.class)); } catch (Exception e) { throw new IllegalStateException("Failed to initialize the native callback handler.", e); } MemoryUtil.getAllocator(); } private long address; /** * Creates a callback instance using the specified libffi CIF. * * @param cif the libffi CIF */ protected Callback(FFICIF cif) { this.address = create(cif, this); } /** * Creates a callback instance using the specified function address * * @param address the function address */ protected Callback(long address) { if (CHECKS) { check(address); } this.address = address; } @Override public long address() { return address; } @Override public void free() { free(address()); } private static native long getCallbackHandler(Method callback); /** * Creates a native function that delegates to the specified instance when called. * *

The native function uses the default calling convention.

* * @param cif the {@code libffi} CIF * @param instance the callback instance * * @return the dynamically generated native function */ static long create(FFICIF cif, Object instance) { FFIClosure closure; long executableAddress; try (MemoryStack stack = stackPush()) { PointerBuffer code = stack.mallocPointer(1); closure = ffi_closure_alloc(FFIClosure.SIZEOF, code); if (closure == null) { throw new OutOfMemoryError(); } executableAddress = code.get(0); if (DEBUG_ALLOCATOR) { MemoryManage.DebugAllocator.track(executableAddress, FFIClosure.SIZEOF); } } long user_data = NewGlobalRef(instance); int errcode = ffi_prep_closure_loc(closure, cif, CALLBACK_HANDLER, user_data, executableAddress); if (errcode != FFI_OK) { DeleteGlobalRef(user_data); ffi_closure_free(closure); throw new RuntimeException("Failed to prepare the libffi closure"); } CLOSURE_REGISTRY.put(executableAddress, closure); return executableAddress; } /** * Converts the specified function pointer to the {@code CallbackI} instance associated with it. * * @param functionPointer a function pointer * @param the {@code CallbackI} instance type * * @return the {@code CallbackI} instance */ public static T get(long functionPointer) { return memGlobalRefToObject(CLOSURE_REGISTRY.get(functionPointer).user_data()); } /** Like {@link #get}, but returns {@code null} if {@code functionPointer} is {@code NULL}. */ public static @Nullable T getSafe(long functionPointer) { return functionPointer == NULL ? null : get(functionPointer); } /** * Frees any resources held by the specified function pointer. * * @param functionPointer the function pointer */ public static void free(long functionPointer) { if (DEBUG_ALLOCATOR) { MemoryManage.DebugAllocator.untrack(functionPointer); } FFIClosure closure = CLOSURE_REGISTRY.remove(functionPointer); DeleteGlobalRef(closure.user_data()); ffi_closure_free(closure); } public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof Callback)) { return false; } Callback that = (Callback)o; return address == that.address(); } public int hashCode() { return (int)(address ^ (address >>> 32)); } @Override public String toString() { return String.format("%s pointer [0x%X]", getClass().getSimpleName(), address); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy