In my android application I've got a callback from a native thread into Java code which needs to be synchronized with the main UI thread. The intention is that the UI thread display a list of options based on information returned from the native thread. Until the user selects an option the native thread needs to block. After the user selects an option the native thread reads the value and continues running.
I've tried to implement this solution using a ConditionVariable however I get a VM error with the comment indicating "Fatal spin-on-suspend, dumping threads".
It looks as if it's not possible to use a Java based synchronization object to synchronize these threads. The code works perfectly in the case where I've got two Java threads.
In general is there any way to use a Java based synchronization object to synchronize a Java and native thread, or does this need to be implemented using the NDK with a call from the Java thread into an NDK function that implements the synchronization?
The way to do this is not to use a Java based sync object but rather an NDK based sync object as follows:
static pthread_cond_t uiConditionVariable = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t uiConditionMutex = PTHREAD_MUTEX_INITIALIZER;
/**
* This function opens the condition variable which releases waiting threads.
*/
JNIEXPORT void JNICALL
Java_com_Xxxx_openConditionVariable(JNIEnv *env,jobject o)
{
pthread_mutex_lock(&uiConditionMutex);
pthread_cond_signal(&uiConditionVariable);
pthread_mutex_unlock(&uiConditionMutex);
}
/**
* This function blocks on the condition variable associated with the
*/
JNIEXPORT void JNICALL
Java_com_Xxxx_blockConditionVariable(JNIEnv *env,jobject o)
{
pthread_mutex_lock(&uiConditionMutex);
pthread_cond_wait(&uiConditionVariable,&uiConditionMutex);
pthread_mutex_unlock(&uiConditionMutex);
}
Related
I'm pretty new to jni.
I need to invoke a native method from java code, which should perform a time-consuming operation asynchronously (in another thread), and at the end it should invoke a java callback. However I need this callback to be invoked in the same java thread that originated the operation. That is, the java application should be single-threaded, and all callbacks should be invoked in the context of this thread.
I've read tutorials on callback invocations, the stuff about keeping global refs to the object, getting the appropriate methodid and invoking it.
I've also found stuff about how to call a java method from another thread (AttachCurrentThread), however this is not what I need. I need to make the java call within the originating thread, so the question is how do I switch to that thread?
For instance, in Win32 I'd use something like PostMessage/PostThreadMessage to invoke a code in the specified thread, assuming it runs the message-loop. There're also alternatives like QueueUserAPC which is applicable if the thread waits for events in an alertable state.
So, what are the options at my disposal? Is there a jni method for posting execution of a java method in the specified thread? Or perhaps a linux equivalent of PostThreadMessage? I can also think about workarounds within the java code, but prefer to solve this on the native side.
Thanks in advance.
Please see a relevant recent discussion: How do I post code to be run on the Android main thread from a separate thread in C++?. The most interesting part is this thread in android-ndk group from 2012.
TL;NR: from JNI_OnLoad() or other call that happens early enough, and comes from the UI thread, you call
pipe2(messagePipe, O_NONBLOCK | O_CLOEXEC);
ALooper_addFd(ALooper_forThread(), messagePipe[0], 0, ALOOPER_EVENT_INPUT, handler, data);
Elsewhere you define a simple handler function:
int handler(int fd, int, void* pHandler) {
int what = 0;
read(fd, &what, sizeof(what));
static_cast<MyHandler *>(pHandler)->handle(what));
return 1;
}
Now the native version of Handler.sendEmptyMessage(), and a close analog of CWinThread::PostThreadMessage() (without wParam and lParam), could then be as simple as
MyHandler::sendEmptyMessage(int what) {
write(messagePipe[1], &what, sizeof(what));
}
I am trying to track down a problem in my Android application. This problem causes a crash during garbage collection in the native layer of the code. I'll explain myself.
My application consists merely in java source controlling GUI elements and such and a native library that manages rendering and other performance-critical parts of the application. It features a GLSurfaceView for rendering.
The java layer needs to be able to access and instantiate native objects, so these are exposed to Java through Swig. What Swig does is that in each wrapper class (in Java) it generates a finalize method that will call the native delete operator.
Consider the following pseudo-code:
In C++:
class MyClass {
public:
MyClass () {}
~MyClass ()
{
print("deleting MyClass instance");
print("doing 1");
do1();
print("doing 2");
do2();
print("doing 3");
do3();
print("instance deleted");
}
};
This is pseudo code meaning that for example "print" stands for print in android logcat I was just Keeping It Simply Stupid ;) .
What puzzles me is that when the garbage collector runs, and calls the destructors in some cases I can find the output interleaved between multiple destructors (as if they were being deleted concurrently). In this example case it could be :
deleting MyClass instance
doing 1
deleting MyClass instance
doing2
doing1
doing2
deleting MyClass instance
doing3
doing3
instance deleted
doing1
doing2
instance deleted
doing3
instance deleted
While at the same time logcat clearly states that these messages originate from the same thread (the finalizer thread).
Hence my question: "How can a single thread be executing multiple native calls at the same time ?"
Does the native destructor call back into the VM?
Say that I have two native threads, A and B. In thread A I use JNI methods to get a Java object by invoking a java method (constructor, static method, etc) and get a global reference to it.
java_vm->AttachCurrentThread(&env,NULL);
j_ref_inputStream = env->CallObjectMethod(g_j_ref_btSocket,j_mid_getInputStream);
g_j_ref_inputStream = env->NewGlobalRef(j_ref_inputStream);
Now I pass my global reference to Thread B where I call a method on it:
java_vm->AttachCurrentThread(&env2,NULL);
jint b = env2->CallIntMethod(g_j_ref_inputStream,j_mid_read);
Calling read() on an InputStream will block Thread B (right?). To forcefully unblock Thread B I can try generating an IOException. So in Thread A I do:
env->callVoidMethod(g_j_ref_btSocket,j_mid_close);
But where do I catch this exception? I'm not too clear on JNI and native threads. Do I call the following in Thread A:
j_exception = env->ExceptionOccurred();
if(j_exception) // handle exception
Or in Thread B:
j_exception = env2->ExceptionOccurred();
if(j_exception) // handle exception
Or does it not matter? (please elaborate on why in either case)
Forget the JNI aspect of it. Just consider what you're trying to do. think of it in pure Java if it helps. You are trying to unblock thread B by closing a stream it is blocked reading. So B will get an exception.
In reality both threads should call 'ExceptionOccurred()' after every JNI call, not just the ones you're 'expecting' exceptions on. But thread B will get this exception.
I have a C thread that is making requests and receiving updates from a server. The updates are sent to Java through JNI calls. My problem happens when I'm receiving a player's inventory which can contain up to 100 items (100 responses from the server, I cannot modify this part). Sometimes the problem happens, sometimes not but the bigger the inventory is, the more often I have this issue.
I don't get any exception in the logcat except the following message :
06-10 10:09:46.085: I/Choreographer(23815): Skipped 87 frames! The application may be doing too much work on its main thread.
And then my app closes. I also need to say that even when I comment the lines where I update the UI with runOnUiThread the app crash.
I tried to check if I'm on the UI Thread when I return to Java with JNI but Looper.myLooper() == Looper.getMainLooper() return false.
Anyone experienced the same problem ? Are the C threads related to the main thread somehow ? Thanks
EDIT
When I receive an update from the server the following calls are made :
From a Java Thread (not the UI thread) : call a C function named notifyAll
From notifyAll call a C function named update which will call its equivalent in Java (see code below)
void UpdateListenerWrapper::update(Update& u) {
// Retrieve the current JNIEnv* with the cached JVM
int status;
JNIEnv* env;
bool isAttached = false;
status = gCachedJVM->GetEnv((void **) &env, JNI_VERSION_1_2);
if(status < 0) {
__android_log_print(ANDROID_LOG_ERROR, "UpdateListenerWrapper", "Failed to get JNI environment");
status = gCachedJVM->AttachCurrentThread(&env, NULL);
if(status < 0) {
__android_log_print(ANDROID_LOG_ERROR, "UpdateListenerWrapper", "Failed to attach current thread");
return;
}
isAttached = true;
}
jmethodID update = env->GetMethodID(gClazzUpdateListenerWrapper, "update", "(J)V"); // J stands for Java long type
// Call Java method update from jUpdateListener object
env->CallVoidMethod(jUpdateListener, update, (jlong)(intptr_t)&u); // Pointer as agument, we'll build the Update object in Java
if (isAttached) {
gCachedJVM->DetachCurrentThread();
}
}
I think the problem is at this line gCachedJVM->GetEnv((void **) &env, JNI_VERSION_1_2); and maybe GetEnv return a pointer for the UI thread. Could that be the problem ? How can I fix this ?
The app crash is unrelated to the Choreographer complaints. Those are just a warning that indicates the animation is being starved.
You really want to be viewing the logcat output in a mode that shows thread IDs. I recommend adb logcat -v threadtime from the command line. If you put a log message at the start of your server interaction you can easily see if it's running on the UI thread (thread ID and process ID are the same -- not guaranteed by the system, but reliably true in apps).
Never do network or database I/O on the main thread. If it takes too long, the system gets bored and decides that your app is not responding.
Calling into native code through JNI does not switch you to a different thread. There are not C threads and Java threads, just threads, which can call in and out of code written in C and Java.
Re: question updates...
GetEnv always returns a pointer to data for the current thread. Also, CallVoidMethod always happens in the current thread; even if you passed in the wrong JNIEnv it wouldn't "jump" threads.
The GetMethodID call can be expensive in a class with a lot of methods, so you should try to cache that at the same point where gClassUpdateListenerWrapper is set up. Attaching and detaching the thread from the VM can also be expensive and is something best avoided -- and if you're calling here from a Java method, then by definition it's attached already. I would guess that isAttached is never being set.
That doesn't really explain why the Choreographer is starving though. I think you still need to add a log message to C update() and use logcat -v threadtime to get a sense for what is happening on which threads, and use traceview to see where the time is going.
I found the solution to my problem but it's kind of specific to my app. In the update function (Java side), I had a case which had no break and each updates were triggering new network calls (not in the UI thread). Nasty to find but thanks for your time and your answers, you helped me to solve this :)
My application is an Android app with some native code.
The Java code starts a thread via new Thread(new Runnable), in the native code, I attach that thread to the JVM via AttachCurrentThread.
The native function which is called in that thread is an endless loop which will listen for simple events(boolean variables) and call another native function which by using the proper JNIEnv pointer and jobject will fill 2 java variables on the Java side and call a void method to display the data. The reason for that loop is that the overhead to calling the function normally(has a lot of allocation and destruction to do) is most likely huge.
The only problem is that while I can renew a JNIEnv pointer, I have no idea how to do it for a jobject.
And by jobject I mean the jobject which is passed to the native code via the function call like so
jint Java_com_example_example_MainActivity_NativeFunc( JNIEnv* env,
jobject obj)
{
return;
}
The Java code starts a thread via new Thread(new Runnable), in the native code, I attach that thread to the JVM via AttachCurrentThread.
You don't have to attach the JVM to a thread that was created with Java code, it is already attached.
If I understood your question correctly you get a jobject once and want to access that object later in other native methods. To accomplish that, you can use NewGlobalRef:
jobject myGlobalReference = env->NewGlobalRef(obj);
You can store that jobject in a global C/C++ variable and use it later. It will stay valid until the JVM dies or you delete the reference with DeleteGlobalRef. Keep in mind that the garbage collection won't collect the referenced object if all Java references to it are gone as long as you don't delete your global reference!