Here is a quick gotcha for using pthreads that make JNI calls on Android: the JNIEnv is not shared between threads.
This simple implementation of threads will crash on any JNI call.
struct ThreadArgsYou need to call AttachCurrentThread and DetachCurrentThread to allow JNI access.
{
GHRunnable* mRunnable;
};
void* threadLaunch(void* arg)
{
ThreadArgs* threadArgs = (ThreadArgs*)arg;
threadArgs->mRunnable->run();
delete threadArgs;
}
void GHAndroidThread::runThread(GHRunnable& runnable)
{
ThreadArgs* threadArgs = new ThreadArgs;
threadArgs->mRunnable = &runnable;
pthread_t myPThread;
if (pthread_create(&myPThread, NULL, threadLaunch, (void*)(threadArgs)))
{
GHDebugMessage::outputString("Error creating pthread");
return;
}
}
struct ThreadArgsLastly any JNI calls should probably include the following wrapper to get the correct env for the current thread. I tried it without this code and didn't have problems but the docs do say it's a different JNIEnv per thread. Maybe it would crash without this on a different Android version.
{
GHRunnable* mRunnable;
JavaVM* mJVM;
};
void* threadLaunch(void* arg)
{
ThreadArgs* threadArgs = (ThreadArgs*)arg;
JNIEnv* jniEnv;
bool attachNeeded = true;
int status = threadArgs->mJVM->GetEnv((void**)&jniEnv, JNI_VERSION_1_6);
if (status == JNI_OK)
{
attachNeeded = false;
}
if (attachNeeded)
{
threadArgs->mJVM->AttachCurrentThread(&jniEnv, NULL);
}
threadArgs->mRunnable->run();
if (attachNeeded)
{
threadArgs->mJVM->DetachCurrentThread();
}
delete threadArgs;
}
void GHAndroidThread::runThread(GHRunnable& runnable)
{
ThreadArgs* threadArgs = new ThreadArgs;
threadArgs->mRunnable = &runnable;
threadArgs->mJVM = mJNIMgr.getJVM();
pthread_t myPThread;
if (pthread_create(&myPThread, NULL, threadLaunch, (void*)(threadArgs)))
{
GHDebugMessage::outputString("Error creating pthread");
return;
}
}
JNIEnv& GHJNIMgr::getJNIEnv(void)
{
JNIEnv* env;
int status = mJVM->GetEnv((void**)&env, JNI_VERSION_1_6);
if (status < 0) {
GHDebugMessage::outputString("Failed to get JNIEnv");
}
return *env;
}