In our project, Java implemented callback is passed to native code. The function signature looks like std::function<bool(uint64_t)>
.
infoMap.put(new Info("std::function<bool(uint64_t)>").pointerTypes("FilterFunction")); public static class FilterFunction extends FunctionPointer { static { Loader.load(); } /** Pointer cast constructor. Invokes {@link Pointer#Pointer(Pointer)}. */ public FilterFunction(Pointer p) { super(p); } protected FilterFunction() { allocate(); } private native void allocate(); public native boolean call(@Cast("uint64_t") long key); }
In our testing program, a ThreadPool is created. Each thread runs the same logic, creating one callback and passing the callback to native code. Pseudo code looks like this
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threadCount); List<Future> futures = new ArrayList<>(); NativeClass2 processor = new ...; for (int i = 0; i < threadCount; i++) { futures.add(executor.submit(()->{ NativeClass1 ctx = new ...; ctx.set_filter(new FilterFunction() { @Override public boolean call(@Cast("uint64_t") long key) { return false; } }); for (Query q : queries) { processor.process(ctx, q); } }); }
When there are 29 or less threads created, the testing program runs fine. However creating 30 or more threads will lead to crashing(core dump).
#0 0x00007efef0bb88af in raise () from /lib64/libc.so.6
#1 0x00007efef0bba4aa in abort () from /lib64/libc.so.6
#2 0x00007efef0417ee9 in os::abort(bool) () from /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-1.1.centos7.x86_64/jre/lib/amd64/server/libjvm.so
#3 0x00007efef063f03a in VMError::report_and_die() () from /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-1.1.centos7.x86_64/jre/lib/amd64/server/libjvm.so
#4 0x00007efef0422095 in JVM_handle_linux_signal () from /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-1.1.centos7.x86_64/jre/lib/amd64/server/libjvm.so
#5 0x00007efef04150a8 in signalHandler(int, siginfo_t*, void*) () from /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-1.1.centos7.x86_64/jre/lib/amd64/server/libjvm.so
#6 <signal handler called>
#7 0x00007efef01dc4bf in jni_invoke_nonstatic(JNIEnv_*, JavaValue*, _jobject*, JNICallType, _jmethodID*, JNI_ArgumentPusher*, Thread*) [clone .constprop.235] ()
from /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-1.1.centos7.x86_64/jre/lib/amd64/server/libjvm.so
#8 0x00007efef01e66aa in jni_CallBooleanMethodA () from /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-1.1.centos7.x86_64/jre/lib/amd64/server/libjvm.so
#9 0x00007efde0380d48 in ?? ()
from /home/user/.javacpp/cache/java-sdk-samples-1.0-SNAPSHOT-jar-with-dependencies.jar/com/mycompany/myproject2/linux-x86_64/libjnimyproject.so
#10 0x00007efde03c09bc in ?? ()
#0 0x00007efef0bb88af in raise () from /lib64/libc.so.6
#1 0x00007efef0bba4aa in abort () from /lib64/libc.so.6
#2 0x00007efef0417ee9 in os::abort(bool) () from /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-1.1.centos7.x86_64/jre/lib/amd64/server/libjvm.so
#3 0x00007efef063f03a in VMError::report_and_die() () from /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-1.1.centos7.x86_64/jre/lib/amd64/server/libjvm.so
#4 0x00007efef0422095 in JVM_handle_linux_signal () from /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-1.1.centos7.x86_64/jre/lib/amd64/server/libjvm.so
#5 0x00007efef04150a8 in signalHandler(int, siginfo_t*, void*) () from /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-1.1.centos7.x86_64/jre/lib/amd64/server/libjvm.so
#6 <signal handler called>
#7 0x00007efef01dc4bf in jni_invoke_nonstatic(JNIEnv_*, JavaValue*, _jobject*, JNICallType, _jmethodID*, JNI_ArgumentPusher*, Thread*) [clone .constprop.235] ()
from /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-1.1.centos7.x86_64/jre/lib/amd64/server/libjvm.so
#8 0x00007efef01e66aa in jni_CallBooleanMethodA () from /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-1.1.centos7.x86_64/jre/lib/amd64/server/libjvm.so
#9 0x00007efde0380d48 in ?? ()
from /home/user/.javacpp/cache/java-sdk-samples-1.0-SNAPSHOT-jar-with-dependencies.jar/com/mycompany/myproject2/linux-x86_64/libjnimyproject.so
#10 0x00007efde03c09bc in ?? ()
Stack: [0x00007efdd9ed8000,0x00007efdd9fd9000], sp=0x00007efdd9fd6e00, free space=1019k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V [libjvm.so+0x7034bf] jni_invoke_nonstatic(JNIEnv_*, JavaValue*, _jobject*, JNICallType, _jmethodID*, JNI_ArgumentPusher*, Thread*) [clone .constprop.235]+0x27f
V [libjvm.so+0x70d6aa] jni_CallBooleanMethodA+0xfa
C [libjnimyproject.so+0xafd48]
The generated jni code contains static callback array, which seems not to be protected by lock or synchronizes with proper memory order. I do not know if that's ok. (Besides, I do not see usage of rptr->ptr in operator()(uint64_t arg0)
)
static JavaCPP_com_mycompany_myproject2_presets_myproject_00024FilterFunction JavaCPP_com_mycompany_myproject2_presets_myproject_00024FilterFunction_instances[10];
JNIEXPORT void JNICALL Java_com_mycompany_myproject2_presets_myproject_00024FilterFunction_allocate(JNIEnv* env, jobject obj) {
obj = env->NewWeakGlobalRef(obj);
if (obj == NULL) {
JavaCPP_log("Error creating global reference of com.mycompany.myproject2.presets.myproject.FilterFunction instance for callback.");
return;
}
JavaCPP_com_mycompany_myproject2_presets_myproject_00024FilterFunction* rptr = new (std::nothrow) JavaCPP_com_mycompany_myproject2_presets_myproject_00024FilterFunction;
if (rptr != NULL) {
rptr->obj = obj;
JavaCPP_initPointer(env, obj, rptr, 1, rptr, &JavaCPP_com_mycompany_myproject2_presets_myproject_00024FilterFunction_deallocate);
for (int i = 0; i < 10; i++) {
if (JavaCPP_com_mycompany_myproject2_presets_myproject_00024FilterFunction_instances[i].obj == NULL) {
rptr->ptr = JavaCPP_com_mycompany_myproject2_presets_myproject_00024FilterFunction_allocate_callbacks[i];
JavaCPP_com_mycompany_myproject2_presets_myproject_00024FilterFunction_instances[i] = *rptr;
break;
}
}
}
}
struct JavaCPP_hidden JavaCPP_com_mycompany_myproject2_presets_myproject_00024FilterFunction {
JavaCPP_com_mycompany_myproject2_presets_myproject_00024FilterFunction() : ptr(NULL), obj(NULL) { }
unsigned char operator()(uint64_t arg0);
unsigned char (*ptr)(uint64_t arg0);
jobject obj; static jmethodID mid;
};
unsigned char JavaCPP_com_mycompany_myproject2_presets_myproject_00024FilterFunction::operator()(uint64_t arg0) {
jboolean rarg = 0;
JNIEnv* env;
bool attached = JavaCPP_getEnv(&env);
if (env == NULL) {
goto end;
}
{
jvalue args[1];
args[0].j = (jlong)arg0;
if (obj == NULL) {
obj = JavaCPP_createPointer(env, 120);
obj = obj == NULL ? NULL : env->NewGlobalRef(obj);
if (obj == NULL) {
JavaCPP_log("Error creating global reference of com.mycompany.myproject2.presets.myproject.FilterFunction instance for callback.");
} else {
env->SetLongField(obj, JavaCPP_addressFID, ptr_to_jlong(this));
}
for (int i = 0; i < 10; i++) {
if (this == &JavaCPP_com_mycompany_myproject2_presets_myproject_00024FilterFunction_instances[i]) {
ptr = JavaCPP_com_mycompany_myproject2_presets_myproject_00024FilterFunction_allocate_callbacks[i];
break;
}
}
}
if (mid == NULL) {
mid = JavaCPP_getMethodID(env, 120, "call", "(J)Z");
}
if (obj == NULL) {
JavaCPP_log("Function pointer object is NULL in callback for com.mycompany.myproject2.presets.myproject.FilterFunction.");
} else if (mid == NULL) {
JavaCPP_log("Error getting method ID of function caller \"public native boolean com.mycompany.myproject2.presets.myproject$FilterFunction.call(long)\" for callback.");
} else {
rarg = env->CallBooleanMethodA(obj, mid, args);
}
}
end:
JavaCPP_detach(attached);
return rarg;
}
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4