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



我有一个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_不是静态的,而是我的MapReduceBridgeC++类的成员,因此能够具有MapReduceBridge的多个实例,即回调不应静态执行,而是在我当前的MapReduceBridge实例的范围内执行。

例如,一种可能的解决方案是在Java中保存指向C++CCD_ 12实例的指针,并在回调期间传递该指针。我如何在JNI中做到这一点?

您可以将c/c++对象的地址(即指针)作为long存储在Java类中。当您将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_);
}

Java

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

}

最新更新