У меня есть приложение C++, которое запускает систему MapReduce, основанную на Акке. Это я делаю с помощью класса оболочки CNN JNI MapReduceBridge
и он отлично работает, учитывая, что актеры Akka Actor и C++ необходимо синхронизировать (показано ниже). Проблема в том, что с этим дизайном я вынужден иметь класс C++ MapReduceBridge
как одноэлементный и использовать статические переменные, потому что методы обратного вызова, зарегистрированные через RegisterNatives
являются глобальными. Мой вопрос - лучший способ переместиться в область экземпляров класса, чтобы сосуществовать несколько экземпляров этого C++ MapReduceBridge
?
Два обратных вызова Java на C++ выполняют следующие действия:
Я использую здесь два обратных вызова (обратный вызов функции от Java до C++) с использованием RegisterNatives
, это сторона C++:
// allow the C++ MapReduceBridge 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 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_
не были статическими, а lock_
членом моего класса MapReduceBridge
C++ и, следовательно, могли иметь несколько экземпляров MapReduceBridge
то есть обратные вызовы должны выполняться не статически, а в объеме мой текущий экземпляр MapReduceBridge
.
например, одним из возможных решений было бы сохранить в Java указатель на C++ this
экземпляр и передать этот указатель во время обратных вызовов. Как я могу это сделать в JNI?
Вы можете сохранить ac/c++ адрес объекта (т.е. указатель) в своем Java-классе как длинный. Когда вы вернетесь на свой родной слой 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 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);
}
public void run()
Примечание. Это предполагает, что созданный вами экземпляр класса MapReduceBridge остается в области действия в течение всего жизненного цикла вызова Java-> run.