GetDirectBufferAddress & GetPrimitiveArrayCritical Order

From Android Game Development Wiki

Jump to: navigation, search

[edit] The Problem

When writting JNI functions that interact with arrays and direct ByteBuffers one needs to use the JNI methods GetPrimitiveArrayCritical and GetDirectBufferAddress to obtain pinned pointers to the respective memory areas of the Java array and direct ByteBuffer.

The documentation of GetPrimitiveArrayCritical states that no other JNI function should be called after a call to GetPrimitiveArrayCritical. However, most Dalvik version used by different devices do not strictly follow this rule and allow for other JNI functions to be called. This deviates from the default behaviour. The Dalvik version used in emulator instances does enfore the restriction. Note that the Sun Java VM also does not complain about an incorrect order of these two JNI calls.

The behaviour of JNI function calls after GetPrimitiveArrayCritical was called is undefined. Testing on a device only might not reveal this bug as Dalvik will happily accept such missplaced calls.

[edit] The Solution

Do not call any other JNI function after you called GetPrimitiveArrayCritical! Test your JNI functions on the emulator as it enforces the standard behaviour!

An example for incorrect code:

JNIEXPORT jint JNICALL 
Java_com_badlogic_gdx_utils_BufferUtils_copyJni___3FLjava_nio_Buffer_2II 
  (JNIEnv *env, jclass, jfloatArray src, jobject dst, jint numFloats, 
jint offset ) 
{ 
   float* pSrc = (float*)env->GetPrimitiveArrayCritical(src, 0); 
   float* pDst = (float*)env->GetDirectBufferAddress( dst ); 
   memcpy( pDst, pSrc + (offset << 2), numFloats << 2 ); 
   env->ReleasePrimitiveArrayCritical(src, pSrc, 0); 
   return (int)pDst; 
}

An example of correct code:

JNIEXPORT jint JNICALL 
Java_com_badlogic_gdx_utils_BufferUtils_copyJni___3FLjava_nio_Buffer_2II 
  (JNIEnv *env, jclass, jfloatArray src, jobject dst, jint numFloats, 
jint offset ) 
{ 
   float* pDst = (float*)env->GetDirectBufferAddress( dst );
   float* pSrc = (float*)env->GetPrimitiveArrayCritical(src, 0);  
   memcpy( pDst, pSrc + (offset << 2), numFloats << 2 ); 
   env->ReleasePrimitiveArrayCritical(src, pSrc, 0); 
   return (int)pDst; 
}

[edit] External Links

Discussion of the problem on the Android NDK mailing list

Personal tools