JNIでC側のポインタを保持する方法について
Cのポインタを保持したい
現在embulk-output-oracleを高速化するために、JNI(Java Native Interface)を使ってプログラミングしている。
その時ちょっと悩んだのが、Cのポインタをどうやって保持するか、だ。
フローとしては、
Java | C | |
---|---|---|
初期化処理 | → | 必要なメモリを確保 |
処理本体 | → | メモリを使っていろいろ処理 |
後処理 | → | メモリを解放 |
のような感じだ。
初期化時に確保したメモリのポインタを、後続の処理に引き継ぐ必要がある。
どうやってポインタを保持するか
まず、C側でグローバル変数で持つ方法。
これだと並列処理に対応できないからだめだ。
Java側にポインタを返し、後続の処理で渡し直してもらう方法がいいだろう。
と思ったが、Javaにはポインタ型というのが無い。intとかlongにキャストすればいいのかな?とも思ったが、処理系依存になるし気持ち悪いなあ。
ちょっと面倒になるが、Java側にはintのIDを返し、C側でIDとポインタとのマップを管理する方法も考えられる。
STL(Standard Template Library)を使えば、mapも簡単。
と思ったが…、どうもスレッドセーフではないっぽい。別のライブラリで排他制御を行わないといけないようだ。
Cで排他制御をおこなうライブラリは…?うーん、めんどくさくなってきた。
バイト配列で保持すればいいんじゃないだろうか
結局、処理系によってポインタのサイズは違うかもしれないが、バイト配列で持っちゃえばいいんじゃないかな?と考えたのだ。
ポインタからバイト配列の変換はこんな感じ。
JNIEXPORT jbyteArray JNICALL initialize(JNIEnv *env, jobject) { void *buffer = ... jbyteArray pointer = env->NewByteArray(sizeof(void*)); env->SetByteArrayRegion(pointer, 0, sizeof(void*), (jbyte*)&buffer); return pointer; }
Java側では byte[] pointer のように持つことができる。
バイト配列をポインタに戻すのはこんな感じ。
JNIEXPORT void JNICALL release(JNIEnv *env, jobject, jbyteArray pointer) { void *buffer; env->GetByteArrayRegion(pointer, 0, sizeof(buffer), (jbyte*)&buffer); ... }
一手間掛かるけど、これなら処理系によらずJava側のコードは統一でよい、はず。たぶん。