在通過JNI使用C代碼的Java項目中,我有一段本機C代碼,它獲取對一個對象及其方法之一的引用,然後啓動本地線程,傳遞這些引用它在一個結構中。當線程試圖調用該方法時,代碼會與SIGSEGV一起崩潰。從主線程調用相同的方法。當從本地pthread調用Java方法時SIGSEGV
做了一些研究我瞭解到env
引用只在線程內有效,並且必須先附加任何其他本地線程。我這樣做了,但代碼在第一次調用方法時仍然崩潰。
奇怪的是,當我在創建另一個線程(在主代碼中取消註釋行)之前從主線程中調用相同的方法時,事情會運行一段時間。輸出線程在崩潰之前循環約10,000次。
方法調用是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;
}
是JNI引用,如jclass
,jfieldID
,jobject
和jmethodID
受到同樣的限制JNIEnv
,即只能在同一線程是否有效?
懷疑這一點後,我把的JNI參考資料移到output_thread()
之後,立即致電AttachCurrentThread()
。但是,我仍然需要跨越線程邊界傳遞jobject
參考(self
),並致電GetObjectClass()
崩潰。
什麼是創建線程本機代碼並讓該線程調用給定類實例的特定方法的正確方法?