GetDirectBufferAddress & GetPrimitiveArrayCritical order

1,098 views
Skip to first unread message

Mario Zechner

unread,
Sep 25, 2010, 11:26:25 PM9/25/10
to android-ndk
Hi,

i just had a nice adventure with GetDirectBufferAddress &
GetPrimitiveArrayCritical. Here's a code snippet

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;
}

This crashes on the emulator with a sigseg (0xdeadd00d). It works on
all the devices i have (HTC Hero (1.5), Samsung Galaxy S (2.1),
Motorola Droid (2.1) and Nexus 1 (2.2). The code is compiled with the
toolchain from the latest NDK release, no special flags except -O2.

If i swap the first to statements to

float* pSrc = (float*)env->GetPrimitiveArrayCritical(src, 0);
float* pDst = (float*)env->GetDirectBufferAddress( dst );

everything works as expected.

The documenation of GetPrimitiveArrayCritical at
http://download-llnw.oracle.com/javase/1.5.0/docs/guide/jni/spec/functions.html
says that "Inside a critical region, native code must not call other
JNI functions, or any system call that may cause the current thread to
block and wait for another Java thread." Now i can interpret this in
two ways: no JNI calls what so ever, and additionally no blocking
system calls. Or i could interpret it as no JNI calls that block and
no system calls that block. Give that it works on all the devices as
well as on the latest Sun JRE (yes, it's still Sun in my head...) i
assume the second interpretation should be the correct one.

Should i file a bug or is the sigseg of the first code example in
compliance with the specification?

Ciao,
Mario

fadden

unread,
Sep 26, 2010, 5:00:36 PM9/26/10
to android-ndk
On Sep 25, 8:26 pm, Mario Zechner <badlogicga...@gmail.com> wrote:
> This crashes on the emulator with a sigseg (0xdeadd00d).

It's a deliberate VM abort, because the emulator has CheckJNI
enabled. Production devices don't, so it doesn't cause a failure
there. This is spelled out more explicitly in the "logcat" output,
which you can view with DDMS or "adb logcat".

> The documenation of GetPrimitiveArrayCritical athttp://download-llnw.oracle.com/javase/1.5.0/docs/guide/jni/spec/func...
> says that "Inside a critical region, native code must not call other
> JNI functions, or any system call that may cause the current thread to
> block and wait for another Java thread." Now i can interpret this in
> two ways: no JNI calls what so ever, and additionally no blocking
> system calls. Or i could interpret it as no JNI calls that block and
> no system calls that block.

A very limited set of JNI calls is allowed, chosen somewhat
arbitrarily. If you need to use other JNI calls while you have the
region, use the non-Critical variant of the Get call.

These are currently on the "okay" list:

GetStringLength
GetStringChars
GetStringUTFLength
GetStringUTFChars
GetStringRegion
GetStringUTFRegion
GetArrayLength
ExceptionCheck

and of course:

ReleasePrimitiveArrayCritical
ReleaseStringCritical

(There's a reason why all the String stuff is in there, but it eludes
me at the moment.)

The bottom line is that you shouldn't be doing much of anything while
holding a "critical" region. Querying for a direct buffer address
could involve execution of interpreted code (and, in fact, did, in the
original implementation inherited from Harmony).

Mario Zechner

unread,
Sep 27, 2010, 7:15:04 PM9/27/10
to android-ndk
Thanks a bunch for the great answer. I knew i'm doing something
stupid :) The more you learn...
Reply all
Reply to author
Forward
0 new messages