Как я могу сделать так, чтобы функции JNI RegisterNatives callack Java имели область действия экземпляра C ++?

0

У меня есть приложение C++, которое запускает систему MapReduce, основанную на Акке. Это я делаю с помощью класса оболочки CNN JNI MapReduceBridge и он отлично работает, учитывая, что актеры Akka Actor и C++ необходимо синхронизировать (показано ниже). Проблема в том, что с этим дизайном я вынужден иметь класс C++ MapReduceBridge как одноэлементный и использовать статические переменные, потому что методы обратного вызова, зарегистрированные через RegisterNatives являются глобальными. Мой вопрос - лучший способ переместиться в область экземпляров класса, чтобы сосуществовать несколько экземпляров этого C++ MapReduceBridge?

Два обратных вызова Java на C++ выполняют следующие действия:

  • поток C++ должен ждать первого обратного вызова, пока Акка-Актер не развернет/не установит на всех машинах банку, содержащую MapExecutor и ReduceExecutor.
  • поток C++ должен ждать второго обратного вызова, пока Акка-Актер не завершит работу с MapReduce, чтобы он мог получить доступ к результатам.

Я использую здесь два обратных вызова (обратный вызов функции от 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?

Теги:
boost
jni
boost-thread

1 ответ

1
Лучший ответ

Вы можете сохранить 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);

}

  • 0
    Можете ли вы уточнить свой ответ, как это сделать? Вы можете включить простые фрагменты?
  • 0
    @GiovanniAzua добавил пример. Я предположил, что ваша подпись метода bridge_run_ была public void run() Примечание. Это предполагает, что созданный вами экземпляр класса MapReduceBridge остается в области действия в течение всего жизненного цикла вызова Java-> run.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню