JNI Env in different threads and exceptions - android

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.

Related

jni: calling a java method in its appartment thread

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

Updates from C thread leads to Choreographer skipping frames

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 :)

Android Python Multiple Threads

Hello I am having issues in regards to running threads in Android with Python SL4A. I am trying to run two threads at the same time, but seem to be having issues
from threading import *
import time
def func1():
while True:
print("func1")
def func2():
while True:
print("func2")
thread = Thread(target = func1)
thread.start()
for i in range(1000):
thread = Thread(target = func2)
thread.start()
time.sleep(2)
time.sleep(2)
the first thread func1 starts fine but then is never run again once func2 takes over.
Would anyone have any advice on how to fix this?
Thank you
I'm not sure what you're expecting here. You first pass func1 into a thread, then start it. You never reference func1 or the thread again after that.
Later, you create an iterator, within which you pass func2 into a new thread and start it. You repeat that process a thousand times, with a two second sleep between each.
Do you want both functions to run a thousand times? If so...
thread1 = Thread(target=func1)
thread2 = Thread(target=func2)
for i in range(1000):
thread1.start()
thread2.start()
time.sleep(2)
Note that in your code the variable thread is assigned Thread(target=func1) before the iterator. Inside the iterator, the same name, thread, is assigned Thread(target=func2), so it no longer points to the same thing.
Also, if you're creating an object to reference it in a loop, you want to try and keep the initialisation outside of the loop, so you don't create the same object over and over. Just create one, then reference it in the loop.

pthread updates a common variable

In my problem, I create a pthread using pthread_create(), say myThread.
They both share a common variable "done" for the main thread to tell the myThread stops reading from the socket.
in my pthread, I have a loop:
// done is a common variable
while (!done && socket->read(&buffer) == OK) {
// do something....
}
Some times, I want to tell myThread to stop reading from socket, so I do:
done = true;
void *test;
pthread_join(myThread, &test);
Will this cause a race condition? i.e. will myThread not see my main thread update the value of 'done' before it blocks on the read call?
Writing a variable in one thread and reading it in another needs synchronization (such as a mutex) to avoid race conditions.
If this is a real socket and not a strange object, consider calling shutdown() to tear down the connection and wake up blocked threads while keeping the file descriptor valid, see Wake up thread blocked on accept() call. Once the read has failed, myThread locks the mutex and checks the done variable.
Other methods include calling poll() on the socket and a pipe for delivering the shutdown message.

Android: Synchronization of native thread with main thread

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

Categories

Resources