2

I have a JNI call from android, where I send data (bytes, width and height) for image processing. The function works fine when executing from the main android thread. I want to call from another thread, to avoiding blocking UI, but when I do that the JNI throws a memory error. If I call from main thread, works fine.

The error is thrown from env->GetIntArrayRegion(...).

extern "C"
JNIEXPORT jobjectArray JNICALL
Java_topcodes_TopCodesScanner_searchTopCodesNative(JNIEnv *env, jobject thiz, jint image_width,
                                                   jint image_height, jintArray image_data) {
    auto image_data_size = env->GetArrayLength(image_data);
    jint image_data_buf[image_data_size];
    // error in this line
    env->GetIntArrayRegion(image_data, 0, image_data_size, image_data_buf);

The way I'm creating the new threads is this:

private val executorService by lazy { Executors.newFixedThreadPool(4) }

    override fun analyze(image: ImageProxy) {
        
        executorService.submit {  // if comment, works fine
            try {
                val blocks = findBlocks(image)
                handler.post { listener(blocks); }
            } finally {
                image.close()
            }
        }                         // if comment, works fine
    }

Here is the stack trace.

memcpy 0x0000007f8f938d9c
art::JNI::*GetIntArrayRegion(_JNIEnv, _jintArray*, int, int, int*) 0x0000007f8bddcea0
art::CheckJNI::GetPrimitiveArrayRegion(char const*, art::Primitive::Type, _JNIEnv*, _jarray*, int, int, void*) 0x0000007f8bbf07fc
<unknown> 0x0000007f750c0aec
<unknown> 0x0000007f750c0708
topcodes.TopCode[] topcodes.TopCodesScanner.searchTopCodesNative(int, int, int[]) 0x0000007f7844218c
art_quick_invoke_stub 0x0000007f8bbb6ca8
art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*) 0x0000007f8bbc5b80
artInterpreterToCompiledCodeBridge 0x0000007f8bf64574
bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*) 0x0000007f8bd3fc48
art::JValue art::interpreter::ExecuteGotoImpl<false, false>(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue) 0x0000007f8bb76e44
art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::DexFile::CodeItem const*, art::ShadowFrame*) 0x0000007f8bd1d9c4
artQuickToInterpreterBridge 0x0000007f8bfd4944
art_quick_to_interpreter_bridge 0x0000007f8bbc0a28
<unknown> 0x0000007f8bbc0b90

I've searched for similar questions. Thanks in advance.

1 Answers1

0
auto image_data_size = env->GetArrayLength(image_data);
jint image_data_buf[image_data_size];

Don't make a C-style array if you don't know the size at compile time.
See: Why does C++ allow variable length arrays that aren't dynamically allocated?

Use std::vector<jint> image_data_buf(image_data_size) instead.

m88
  • 1,968
  • 6
  • 14
  • Thanks, it worked! The change I've made: ``` std::vector image_data_buf(image_data_size); env->GetIntArrayRegion(image_data, 0, image_data_size, image_data_buf.data()); ``` – Cesar Viana Mar 05 '21 at 19:54