从本机pthread调用Java方法时SIGSEGV

从本机pthread调用Java方法时SIGSEGV,java,c,multithreading,java-native-interface,pthreads,Java,C,Multithreading,Java Native Interface,Pthreads,在通过JNI使用C代码的Java项目中,我有一段本机C代码,该代码获取对对象及其方法之一的引用,然后启动本机线程,在结构中将这些引用传递给它。当线程尝试调用该方法时,代码会因SIGSEGV而崩溃。从主线程调用相同的方法是可行的 通过一些研究,我了解到env引用仅在线程内有效,并且必须先附加任何其他本机线程。我这样做了,但是代码在第一次调用该方法时仍然崩溃 奇怪的是,当我在创建另一个线程之前从主线程调用相同的方法时(只需取消主代码中的行注释),事情会运行一段时间。输出线程在崩溃之前循环了大约100

在通过JNI使用C代码的Java项目中,我有一段本机C代码,该代码获取对对象及其方法之一的引用,然后启动本机线程,在结构中将这些引用传递给它。当线程尝试调用该方法时,代码会因SIGSEGV而崩溃。从主线程调用相同的方法是可行的

通过一些研究,我了解到
env
引用仅在线程内有效,并且必须先附加任何其他本机线程。我这样做了,但是代码在第一次调用该方法时仍然崩溃

奇怪的是,当我在创建另一个线程之前从主线程调用相同的方法时(只需取消主代码中的行注释),事情会运行一段时间。输出线程在崩溃之前循环了大约10000次

方法调用为
DataOutputStream.writeShort()
。该线程是唯一一个写入
DataOutputStream
的线程。但是,
DataOutputStream
连接到
DataInputStream

简化本机代码:

static void write_output(struct output_state *s) {
    int i;
    jint sample;
    for (i = 0; i < 2 * s->result_len; i += 2) {
        sample = (s->result[i] << 8) + s->result[i+1];
        (*(s->env))->CallVoidMethod(s->env, s->tunerOut, s->writeShort, sample);
    }
}

static void *output_thread_fn(void *arg)
{
    struct output_state *s = arg;
    (*(s->jvm))->AttachCurrentThread(s->jvm, &(s->env), NULL);
    while (!do_exit) {
        // use timedwait and pad out under runs
        safe_cond_wait(&s->ready, &s->ready_m);
        pthread_rwlock_rdlock(&s->rw); //sync access to s with producer thread
        write_output(s);
        pthread_rwlock_unlock(&s->rw);
    }
    (*(s->jvm))->DetachCurrentThread(s->jvm);
    return 0;
}

JNIEXPORT jboolean JNICALL Java_eu_jacquet80_rds_input_SdrGroupReader_open
  (JNIEnv *env, jobject self) {
    jclass clsSelf = (*env)->GetObjectClass(env, self);
    jfieldID fTunerOut = (*env)->GetFieldID(env, clsSelf, "tunerOut", "Ljava/io/DataOutputStream;");
    jobject tunerOut = (*env)->GetObjectField(env, self, fTunerOut);
    jclass cls = (*env)->GetObjectClass(env, tunerOut);
    jmethodID writeShortID = (*env)->GetMethodID(env, cls, "writeShort", "(I)V");

    if (!writeShortID || !cls)
        return 0;

    (*env)->GetJavaVM(env, &(output.jvm));
    output.tunerOut = tunerOut;
    output.writeShort = writeShortID;

    // (*env)->CallVoidMethod(env, tunerOut, writeShortID, 0); // just for testing

    pthread_create(&controller.thread, NULL, controller_thread_fn, (void *)(&controller));
    usleep(100000);
    pthread_create(&output.thread, NULL, output_thread_fn, (void *)(&output));
    pthread_create(&demod.thread, NULL, demod_thread_fn, (void *)(&demod));
    pthread_create(&dongle.thread, NULL, dongle_thread_fn, (void *)(&dongle));

    return 1;
}
静态无效写入输出(结构输出状态*s){
int i;
金特样品;
对于(i=0;i<2*s->result\u len;i+=2){
样本=(s->result[i]result[i+1];
(*(s->env))->CallVoidMethod(s->env,s->tunerOut,s->writeShort,sample);
}
}
静态void*输出线程fn(void*arg)
{
结构输出_state*s=arg;
(*(s->jvm))->AttachCurrentThread(s->jvm,&(s->env),NULL);
当(!do_退出){
//在运行下使用timedwait和pad out
安全条件等待(&s->就绪,&s->就绪);
pthread_rwlock_rdlock(&s->rw);//使用生产者线程同步对s的访问
写入输出;
pthread\u rwlock\u unlock(&s->rw);
}
(*(s->jvm))->DetachCurrentThread(s->jvm);
返回0;
}
JNIEXPORT jboolean JNICALL Java\u eu\u jacquet80\u rds\u input\u SdrGroupReader\u open
(JNIEnv*env,jobject-self){
jclass clsSelf=(*env)->GetObjectClass(env,self);
jfieldID ftune路由=(*env)->GetFieldID(env,clsSelf,“tunerOut”,“Ljava/io/DataOutputStream;”);
jobject tunerOut=(*env)->GetObjectField(env、self、ftune路由);
jclass cls=(*env)->GetObjectClass(env,tunerOut);
jmethodID writeShortID=(*env)->GetMethodID(env,cls,“writeShort”,“I)V”);
如果(!writeShortID | |!cls)
返回0;
(*env)->GetJavaVM(env,&(output.jvm));
output.tunerOut=tunerOut;
output.writeShort=writeShortID;
//(*env)->CallVoidMethod(env,tunerOut,writeShortID,0);//仅用于测试
pthread_create(&controller.thread,NULL,controller_thread_fn,(void*)(&controller));
美国LEEP(100000);
pthread_create(&output.thread,NULL,output_thread_fn,(void*)(&output));
pthread_create(&demod.thread,NULL,demod_thread_fn,(void*)(&demod));
pthread_create(&dongle.thread,NULL,dongle_thread_fn,(void*)(&dongle));
返回1;
}
诸如
jclass
jfieldID
jobject
jmethodID
等JNI引用是否受到与
JNIEnv
相同的限制,即仅在同一线程内有效

考虑到这一点,我将JNI引用从
open()
移动到
output\u-thread()
,就在调用
AttachCurrentThread()
之后。但是,我仍然需要跨线程边界传递
jobject
引用(
self
),调用
GetObjectClass()
崩溃


创建线程本机代码并让该线程调用给定类实例的特定方法的正确方法是什么?

事实证明我的猜测是正确的:
jobject
jclass
引用确实是本地的,即仅在同一线程内有效,并且只有在当前本机方法返回之前才有效。请参阅

我将引用相关代码移动到线程函数的方法是正确的,只是我需要首先通过调用
NewGlobalRef()
self
转换为全局引用

更新代码:

static void write_output(struct output_state *s) {
    int i;
    jint sample;
    for (i = 0; i < 2 * s->result_len; i += 2) {
        sample = (s->result[i] << 8) + s->result[i+1];
        (*(s->env))->CallVoidMethod(s->env, s->tunerOut, s->writeShort, sample);
    }
}

static void *output_thread_fn(void *arg)
{
    struct output_state *s = arg;
    (*(s->jvm))->AttachCurrentThread(s->jvm, &(s->env), NULL);
    jclass clsSelf = (*(s->env))->GetObjectClass(s->env, s->self);
    jfieldID fTunerOut = (*(s->env))->GetFieldID(s->env, clsSelf, "tunerOut", "Ljava/io/DataOutputStream;");
    s->tunerOut = (*(s->env))->GetObjectField(s->env, s->self, fTunerOut);
    jclass cls = (*(s->env))->GetObjectClass(s->env, s->tunerOut);
    s->writeShort = (*(s->env))->GetMethodID(s->env, cls, "writeShort", "(I)V");
    while (!do_exit) {
        // use timedwait and pad out under runs
        safe_cond_wait(&s->ready, &s->ready_m);
        pthread_rwlock_rdlock(&s->rw); //sync access to s with producer thread
        write_output(s);
        pthread_rwlock_unlock(&s->rw);
    }
    (*(s->jvm))->DetachCurrentThread(s->jvm);
    return 0;
}

JNIEXPORT jboolean JNICALL Java_eu_jacquet80_rds_input_SdrGroupReader_open
  (JNIEnv *env, jobject self) {
    jclass clsSelf = (*env)->GetObjectClass(env, self);

    if (!writeShortID || !cls)
        return 0;

    output.self = (*env)->NewGlobalRef(env, self);
    (*env)->GetJavaVM(env, &(output.jvm));

    (*env)->CallVoidMethod(env, tunerOut, writeShortID, 0); // just for testing

    pthread_create(&controller.thread, NULL, controller_thread_fn, (void *)(&controller));
    usleep(100000);
    pthread_create(&output.thread, NULL, output_thread_fn, (void *)(&output));
    pthread_create(&demod.thread, NULL, demod_thread_fn, (void *)(&demod));
    pthread_create(&dongle.thread, NULL, dongle_thread_fn, (void *)(&dongle));

    return 1;
}
静态无效写入输出(结构输出状态*s){
int i;
金特样品;
对于(i=0;i<2*s->result\u len;i+=2){
样本=(s->result[i]result[i+1];
(*(s->env))->CallVoidMethod(s->env,s->tunerOut,s->writeShort,sample);
}
}
静态void*输出线程fn(void*arg)
{
结构输出_state*s=arg;
(*(s->jvm))->AttachCurrentThread(s->jvm,&(s->env),NULL);
jclass clsSelf=(*(s->env))->GetObjectClass(s->env,s->self);
jfieldID ftune路由=(*(s->env))->GetFieldID(s->env,clsSelf,“tunerOut”,“Ljava/io/DataOutputStream;”);
s->tunerOut=(*(s->env))->GetObjectField(s->env,s->self,ftune路由);
jclass cls=(*(s->env))->GetObjectClass(s->env,s->tunerOut);
s->writeShort=(*(s->env))->GetMethodID(s->env,cls,“writeShort”,“I)V”);
当(!do_退出){
//在运行下使用timedwait和pad out
安全条件等待(&s->就绪,&s->就绪);
pthread_rwlock_rdlock(&s->rw);//使用生产者线程同步对s的访问
写入输出;
pthread\u rwlock\u unlock(&s->rw);
}
(*(s->jvm))->DetachCurrentThread(s->jvm);
返回0;
}
JNIEXPORT jboolean JNICALL Java\u eu\u jacquet80\u rds\u input\u SdrGroupReader\u open
(JNIEnv*env,jobject-self){
jclass clsSelf=(*env)->GetObjectClass(env,self);
如果(!writeShortID | |!cls)
返回0;
output.self=(*env)->NewGlobalRef(env,self);
(*env)->GetJavaVM(env,&(output.jvm));
(*env)->CallVoidMethod(env,tunerOut,writeShortID,0);//仅用于测试
pthread_create(&controller.thread,NULL,controller_thread_fn,(void*)(&controller));
美国LEEP(100000);
pthread_create(&output.thread,NULL,output_thread_fn,(void*)(&output));
pthread_create(&demod.thread,NULL,demod_thread_fn,(void*)(&demod));
pthread_create(&dongle.thread,NULL,dongle_thread_fn,(void*)(&dongle));
返回1;
}
一个