如何使JNI RegisterNatives callack Java函数具有C ++实例范围?

时间:2014-02-25 17:39:31

标签: java c++ boost java-native-interface boost-thread

我有一个C ++应用程序触发基于Akka Actor的MapReduce系统。这是我通过C ++ JNI包装器类MapReduceBridge做的,考虑到Akka Actor和C ++应用程序线程需要同步(如下所示),它可以正常工作。问题是,通过这种设计,我不得不让我的C ++ MapReduceBridge类成为单例并使用静态变量,因为通过RegisterNatives注册的回调方法是全局的。我的问题是什么是进入类实例范围的最佳方式,因此这个C ++ MapReduceBridge的多个实例可以共存?

两个Java到C ++的回调实现了以下功能:

  • C ++线程必须等待第一次回调,直到Akka-Actor在包含MapExecutor和ReduceExecutor的jar的所有机器中部署/安装。
  • C ++线程必须等待第二次回调,直到Akka-Actor完成运行MapReduce,以便它可以访问结果。

我在这里使用RegisterNatives两个回调(从Java到C ++的函数回调),这是C ++方面:

// allow the C++ MapReduceBridge's application thread to wait for the MapReduce system
static boost::mutex mutex_;
static boost::mutex::scoped_lock lock_(mutex_);
static boost::condition_variable callback_condition_;

// callback notification that installation has completed    
JNIEXPORT jobject JNICALL com_sfoam_hpcmom_mapreduce_bridge_BridgeClient_startupCompletedCallback(JNIEnv* env, jobject obj) {
    log_info << "callback 'startup completed' received";
    callback_condition_.notify_one();
}

// callback notification that MapReduce run has completed   
JNIEXPORT jobject JNICALL com_sfoam_hpcmom_mapreduce_bridge_BridgeClient_runCompletedCallback(JNIEnv* env, jobject obj) {
    log_info << "callback 'run completed' received";
    callback_condition_.notify_one();
}

static const JNINativeMethod kCallbackMethods[] = {
      { "startupCompletedCallback", "()V", (void*)&com_sfoam_hpcmom_mapreduce_bridge_BridgeClient_startupCompletedCallback },
      { "runCompletedCallback"    , "()V", (void*)&com_sfoam_hpcmom_mapreduce_bridge_BridgeClient_runCompletedCallback     }
};

MapReduceBridge::MapReduceBridge(std::string env_jar_path) {
    // ... 
    // snippet where the callbacks are registered
    const int methods_size = sizeof(kCallbackMethods) / sizeof(kCallbackMethods[0]);
    env_->RegisterNatives(bridge_class_, kCallbackMethods, methods_size);
    // ... 
}

void MapReduceBridge::run() {
    // get BridgeClient's method run and invoke it
    env_->CallObjectMethod(bridge_instance_, bridge_run_);
    log_info << "run method launched";

    // wait for the callback that 'run' has completed
    callback_condition_.wait(lock_);
}

在Java方面:

//---------------------------------------------------------------
/**
 * {@inheritDoc}
 */
@Override
public void onReceiveResult(double[] results) {
    this.results = results;
    runCompletedCallback();
}

遵循此示例,我的目标是让同步变量mutex_lock_callback_condition_不是静态的,而是我的MapReduceBridge C ++类的成员,因此能够有多个MapReduceBridge实例,即回调应该不是静态执行,而是在我当前MapReduceBridge实例的范围内执行。

e.g。一种可能的解决方案是在Java中保存指向C ++ this实例的指针,并在回调期间传递该指针。我怎么能在JNI中做到这一点?

1 个答案:

答案 0 :(得分:1)

您可以将Java类中的c / c ++对象的地址(即指针)存储为long。当您将long返回到JNI本机层(作为jlong​​)时,只需将其强制转换回对象指针。

C ++

// callback notification that MapReduce run has completed   
JNIEXPORT jobject JNICALL com_sfoam_hpcmom_mapreduce_bridge_BridgeClient_runCompletedCallback(JNIEnv* env, jobject obj, jlong ptr) {
    log_info << "callback 'run completed' received";
    MapReduceBridge * ptr = (MapReduceBridge *)ptr;
    //Use ptr now to reference your instance variables
    //callback_condition_.notify_one();
}
void MapReduceBridge::run() {
    // get BridgeClient's method run and invoke it
    env_->CallObjectMethod(bridge_instance_, bridge_run_, (long)this );
    log_info << "run method launched";

    // wait for the callback that 'run' has completed
    callback_condition_.wait(lock_);
}

爪哇

public void run(long ptr)
{
    this.ptrToMapReduceBridge = ptr;
    //do stuff
}
@Override
public void onReceiveResult(double[] results) {
   this.results = results;
   runCompletedCallback(this.ptrToMapReduceBridge);

}