Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android java+native data visibility

Assume I have two threads in my game app (besides the main thread):

  • GLRenderer thread (offered by Android's GLSurfaceView.Renderer)
  • Another thread (game thread)

Both threads use JNI to call certain C++ (i.e. Android NDK) components of the app.

Assume I have a direct IntBuffer allocated in Java (e.g. from the GLRenderer thread, but don't assume this). Facts:

  • this direct buffer is read by native code from the GLRenderer thread (i.e. by the C++ component called via JNI)
  • this direct buffer is sometimes written from the other thread (game thread)

In the following two scenarios, what is the (best) way for synchronization (actually data visibility ensuring), i.e. to guarantee that the native code in the GLRenderer code sees an up-to-date IntBuffer content?

  • Scenario #1: the Java code of the game thread writes to the IntBuffer (e.g. via IntBuffer.put())
  • Scenario #2: the native code called from the game thread writes to the IntBuffer

I was thinking that standard Java synchronization will work for both cases:

public void onDrawFrame(GL10 gl) { // the GLRenderer thread
    // ...
    synchronized (obj) {
        callNativeCode1(); // a JNI call; this is where the C++ native code reads the IntBuffer
    }

}

public void run() { // the game thread
    // ...

    synchronized (obj) {
        intBuffer.put(...); // writing the buffer from managed code
    }

    // ...
    synchronized (obj) {
        callNativeCode2(); // a JNI call; writing the buffer from C++ native code
    }
}
like image 434
Thomas Calc Avatar asked May 26 '26 19:05

Thomas Calc


2 Answers

No knowledge of the specifics of memory sharing with JNI but I would suggest using an AtomicIntegerArray.

Your options are:

  1. synchronized - You would need to somehow achieve the same in the JNI - not easy.
  2. volatile - making a whole array volatile would be a problem.
  3. atomics - I'd say the best route.

See Package java.util.concurrent.atomic for:

The AtomicIntegerArray, AtomicLongArray, and AtomicReferenceArray classes further extend atomic operation support to arrays of these types. These classes are also notable in providing volatile access semantics for their array elements, which is not supported for ordinary arrays.

This would essentially ensure that so long as the JNI code does not do anything to bypass the cache flush semantics of Java then the JNI package should see a consistent and updated view of the data.

I would recommend some significant research to confirm this but I believe this is the only way you will achieve what you are looking for.

like image 64
OldCurmudgeon Avatar answered May 28 '26 07:05

OldCurmudgeon


You can use JNI equivalent of synchronization to guarantee native data accessed by two threads are synched:

//before read/writing shared data
(*env)->MonitorEnter(obj);

...                      /* synchronized block */

//after read/writing shared data
(*env)->MonitorExit(obj);

You can find more info in this IBM article.

Edit: After a bit more digging, it turns out that how C code should be kept synchronized is VM-implementation dependent (see section 2.1: http://www.hdfgroup.org/hdf-java-html/JNI/). The scary part is this:

...and in most cases there is no way to know what the C code must do.

Unfortunately I can't find info on how Android specifically handles this, or whether it's handled consistently between Android versions.

However, another interesting info is provided by Android dev site (http://developer.android.com/training/articles/smp.html) which indicated that ARM CPUs provides weak memory consistency, so this may be the default behavior for C code.

Basically the issue comes down to: Whether c data would be synchronized when thread access is synchronized in Java. Since link 1 (http://www.hdfgroup.org/hdf-java-html/JNI/) doesn't give us an straight answer on this, and link 2 (http://developer.android.com/training/articles/smp.html) indicate that ARM CPUs provides weak memory consistency, doing a monitor call in C code again seem to be the safer bet.

like image 20
Kai Avatar answered May 28 '26 08:05

Kai



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!