1

在 SystemC 中使用 JNI 时,我遇到了一个非常奇怪的问题,我无法解释自己。

只是关于我使用的环境的一些信息:我目前正在安装 openjdk-6 和 openjdk-7 的 12.04 ubuntu 上进行开发。两个jdks都重现了这个问题!

有这个主要方法:

#include "tlm.h"
#include "SystemCWrapper.h"

int sc_main(int argc, char *argv[]) {
    SystemCWrapper wrapper("test");

    std::cout << "############  Before  ############" << std::endl;
    wrapper.run();

    std::cout << "############ SC-START ############" << std::endl;
    sc_core::sc_start();

    std::cout << "############   After  ############" << std::endl;
    wrapper.run();
    return 0;
}

以及 SystemCWrapper 的以下实现:

#include "SystemCWrapper.h"

#include <jni.h>
#include <iostream>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>

SC_HAS_PROCESS( SystemCWrapper );

using std::cout;
using std::cerr;
using std::endl;

static JavaVM * createJavaVM () {
    JavaVM *jvm;
    JNIEnv *env;
    JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */

    JavaVMOption* options = new JavaVMOption[2];
    options[0].optionString = const_cast<char*>("-Djava.class.path=HelloWorld.jar");
    options[1].optionString = const_cast<char*>("-verbose:jni");
    vm_args.version = JNI_VERSION_1_6;
    vm_args.nOptions = 1; // NOTE: set to 2 for more verbose jni information
    vm_args.options = options;
    vm_args.ignoreUnrecognized = false;
    /* load and initialize a Java VM, return a JNI interface pointer in env */
    JNI_CreateJavaVM(&jvm, (void**) &env, &vm_args);

    return jvm;
}


SystemCWrapper::SystemCWrapper(sc_core::sc_module_name nam) : sc_module(nam), jvm(NULL){
    SC_THREAD(run);       // Thread to run the ISS

    // setup the JNI
    jvm = createJavaVM();
}

SystemCWrapper::~SystemCWrapper() {
}

void SystemCWrapper::run() {
    // Print general information
    cout << "Hello SystemC (PID: " << getpid() << ", PTID: " << pthread_self() << ", LTID: " << syscall(SYS_gettid) << ")" << endl;

    JNIEnv* env;

    int ret = jvm->GetEnv((void**)&env, JNI_VERSION_1_6);

    if(ret == JNI_OK) {
        cout << "Retrieve successful." << endl;
    } else if(ret == JNI_EDETACHED){
        cerr << "Environment is detached." << endl;
    } else {
        cerr << "Retrieving environment failed! (" << ret << ")" << endl;
    }

    jclass clazz = env->FindClass("JavaProgram");
    if(clazz == NULL) {
        cerr << "Class not found" << endl;
        return;
    }
    jmethodID mConstr = env->GetMethodID(clazz, "<init>", "()V");
    if(mConstr == NULL) {
        cerr << "Constructor not found" << endl;
        return;
    }
    jmethodID mRun = env->GetMethodID(clazz, "run", "()I");
    if(mRun == NULL) {
        cerr << "Run method not found" << endl;
        return;
    }
    jobject obj = env->NewObject(clazz, mConstr);
    if(obj == NULL) {
        cerr << "Object was not created!" << endl;
        return;
    }
    int result = env->CallIntMethod(obj, mRun);
    if(result == 0) {
        cerr << "Run should never return 0!" << endl;
        return;
    }
}

结果进入以下控制台输出(Java 程序仅输出“Hello Java!”,如果被调用):

$ ./build/bin/jnitest 

    SystemC 2.3.1-Accellera --- Jun  6 2014 13:36:39
    Copyright (c) 1996-2014 by all Contributors,
    ALL RIGHTS RESERVED
############  Before  ############
Hello SystemC (PID: 5660, PTID: 140661929555776, LTID: 5660)
Retrieve successful.
Hello Java!
############ SC-START ############
Hello SystemC (PID: 5660, PTID: 140661929555776, LTID: 5660)
Retrieve successful.
Object was not created!
############   After  ############
Hello SystemC (PID: 5660, PTID: 140661929555776, LTID: 5660)
Retrieve successful.
Hello Java!

似乎 SystemC 方法sc_core::sc_start();所做的事情是每个 JNI 调用都没有“传递”到 Java。

我已经尝试了一些调整,但即使我将当前线程附加到 jvm 也没有任何变化(因为可以看到线程 ID 甚至保持不变)。

有什么建议么?如果需要更多信息,请告诉我。

4

1 回答 1

0

经过调查和一个大的试错阶段,我得出的结论是,我自己解决这个问题并不是很简单。出于这个原因,我创建了一个 pthread 来处理 SC_THREAD 之外的 JNI 调用。

我认为问题是 systemc 下的 QuickThreads 库不能与 JNI 结合使用。我在这方面没有找到任何东西,但我可能没有很好地搜索。

我也可以使用 pthreads 而不是 QuickThreads 重新编译 systemc,但是由于我的解决方案应该在两种配置中都适用,这对我来说不是一个选择。

最后,这里是帮助我摆脱痛苦的代码。

static JNIEnv* getJniEnv(JavaVM* jvm) {
    JNIEnv* env = NULL;
    int i = jvm->GetEnv((void**) &env, JNI_VERSION_1_6);
    if (i == JNI_EDETACHED) {
        int ret = jvm->AttachCurrentThreadAsDaemon((void **) &env, NULL);
        if ( ret != 0) {
            cerr << "Failed to attach thread to jni (" << ret << ")!\n";
        }
    } else if(i != JNI_OK) {
        cerr << "Failed retrieving jni environment!\n";
    }
    if(env == NULL) {
        cerr << "Pointer to JNIEnv is NULL!\n";
    }
    return env;
}

SystemCWrapper::SystemCWrapper(sc_core::sc_module_name nam) : sc_module(nam), jvm(NULL) {
    SC_THREAD(spawnAndRun); // Thread to run the ISS
    // setup the JNI
    jvm = createJavaVM();
}

SystemCWrapper::~SystemCWrapper() {}

static void * doStart(void * data) {
    SystemCWrapper* wrapper = (SystemCWrapper*)data;
    wrapper->run();
    return NULL;
}

void SystemCWrapper::spawnAndRun() {
    pthread_t t;
    void * ret;
    pthread_create(&t, NULL, doStart, (void*)this);
    pthread_join(t, &ret);
}

void SystemCWrapper::run() {
    // Print general information
    cout << "Hello SystemC (PID: " << getpid() << ", PTID: " << pthread_self() << ", LTID: " << syscall(SYS_gettid) << ")" << endl;
    JNIEnv* env = getJniEnv(jvm);

    if(env == NULL)
        return;

    jclass clazz = env->FindClass("JavaProgram");
    // ... same code as above ... //

}

@Ifor 非常感谢您的反馈!!!

于 2014-06-25T08:20:26.303 回答