[Android] [PATCH 1/2] libvlcjni: simplify/improve the attachment of a thread to the Java VM

Thomas Guillem thomas at gllm.fr
Fri Apr 3 09:45:42 CEST 2015


There is now only one way to get a JNIEnv: jni_get_env. This function use the
thread-specific data (TSD) areas to save a JNIEnv into a TSD key. The thread
will be automatically detached to the Java VM when it's canceled or exited.
---
 libvlc/jni/java_event_thread.c    |   9 +---
 libvlc/jni/libvlcjni-util.c       |   5 --
 libvlc/jni/libvlcjni.c            | 104 +++++++++++++++++++++++---------------
 libvlc/jni/native_crash_handler.c |  15 ++----
 libvlc/jni/vout.c                 |  54 +++++---------------
 5 files changed, 80 insertions(+), 107 deletions(-)

diff --git a/libvlc/jni/java_event_thread.c b/libvlc/jni/java_event_thread.c
index 06e4e21..30ed4ac 100644
--- a/libvlc/jni/java_event_thread.c
+++ b/libvlc/jni/java_event_thread.c
@@ -29,9 +29,7 @@
 #include "log.h"
 
 #define THREAD_NAME "JavaEventThread"
-extern int jni_attach_thread(JNIEnv **env, const char *thread_name);
-extern void jni_detach_thread();
-extern int jni_get_env(JNIEnv **env);
+extern JNIEnv *jni_get_env(const char *name);
 
 typedef struct event_queue_elm event_queue_elm;
 struct event_queue_elm
@@ -60,7 +58,7 @@ JavaEventThread_thread(void *data)
     java_event_thread *p_java_event_thread = data;
 
 
-    if (jni_attach_thread(&env, THREAD_NAME) < 0)
+    if (!(env = jni_get_env(THREAD_NAME)))
     {
         pthread_mutex_lock(&p_java_event_thread->lock);
         goto end;
@@ -112,9 +110,6 @@ end:
     }
     pthread_mutex_unlock(&p_java_event_thread->lock);
 
-    if (env)
-        jni_detach_thread();
-
     return NULL;
 }
 
diff --git a/libvlc/jni/libvlcjni-util.c b/libvlc/jni/libvlcjni-util.c
index 8e50962..9ca4e2b 100644
--- a/libvlc/jni/libvlcjni-util.c
+++ b/libvlc/jni/libvlcjni-util.c
@@ -31,11 +31,6 @@
 #define LOG_TAG "VLC/JNI/Util"
 #include "log.h"
 
-#define THREAD_NAME "libvlcjni-util"
-extern int jni_attach_thread(JNIEnv **env, const char *thread_name);
-extern void jni_detach_thread();
-extern int jni_get_env(JNIEnv **env);
-
 jint getInt(JNIEnv *env, jobject thiz, const char* field) {
     jclass clazz = (*env)->GetObjectClass(env, thiz);
     jfieldID fieldMP = (*env)->GetFieldID(env, clazz,
diff --git a/libvlc/jni/libvlcjni.c b/libvlc/jni/libvlcjni.c
index 5e41658..0cea0f5 100644
--- a/libvlc/jni/libvlcjni.c
+++ b/libvlc/jni/libvlcjni.c
@@ -55,9 +55,7 @@ struct fields fields;
 #define VLC_JNI_VERSION JNI_VERSION_1_2
 
 #define THREAD_NAME "libvlcjni"
-int jni_attach_thread(JNIEnv **env, const char *thread_name);
-void jni_detach_thread();
-int jni_get_env(JNIEnv **env);
+JNIEnv *jni_get_env(const char *name);
 
 static void add_media_options(libvlc_media_t *p_md, JNIEnv *env, jobjectArray mediaOptions)
 {
@@ -117,28 +115,17 @@ static void releaseMediaPlayer(JNIEnv *env, jobject thiz)
     }
 }
 
-/* Pointer to the Java virtual machine
- * Note: It's okay to use a static variable for the VM pointer since there
- * can only be one instance of this shared library in a single VM
- */
-static JavaVM *myVm;
-
 static jobject eventHandlerInstance = NULL;
 
 static void vlc_event_callback(const libvlc_event_t *ev, void *data)
 {
     JNIEnv *env;
 
-    bool isAttached = false;
-
     if (eventHandlerInstance == NULL)
         return;
 
-    if (jni_get_env(&env) < 0) {
-        if (jni_attach_thread(&env, THREAD_NAME) < 0)
-            return;
-        isAttached = true;
-    }
+    if (!(env = jni_get_env(THREAD_NAME)))
+        return;
 
     /* Creating the bundle in C allows us to subscribe to more events
      * and get better flexibility for each event. For example, we can
@@ -216,8 +203,55 @@ static void vlc_event_callback(const libvlc_event_t *ev, void *data)
 
 end:
     (*env)->DeleteLocalRef(env, bundle);
-    if (isAttached)
-        jni_detach_thread();
+}
+
+/* Pointer to the Java virtual machine
+ * Note: It's okay to use a static variable for the VM pointer since there
+ * can only be one instance of this shared library in a single VM
+ */
+static JavaVM *myVm;
+static pthread_key_t jni_env_key;
+
+/* This function is called when a thread attached to the Java VM is canceled or
+ * exited */
+static void jni_detach_thread(void *data)
+{
+    //JNIEnv *env = data;
+    (*myVm)->DetachCurrentThread(myVm);
+}
+
+JNIEnv *jni_get_env(const char *name)
+{
+    JNIEnv *env;
+
+    env = pthread_getspecific(jni_env_key);
+    if (env == NULL) {
+        /* if GetEnv returns JNI_OK, the thread is already attached to the
+         * JavaVM, so we are already in a java thread, and we don't have to
+         * setup any destroy callbacks */
+        if ((*myVm)->GetEnv(myVm, (void **)&env, VLC_JNI_VERSION) != JNI_OK)
+        {
+            /* attach the thread to the Java VM */
+            JavaVMAttachArgs args;
+            jint result;
+
+            args.version = VLC_JNI_VERSION;
+            args.name = name;
+            args.group = NULL;
+
+            if ((*myVm)->AttachCurrentThread(myVm, &env, &args) != JNI_OK)
+                return NULL;
+
+            /* Set the attached env to the thread-specific data area (TSD) */
+            if (pthread_setspecific(jni_env_key, env) != 0)
+            {
+                (*myVm)->DetachCurrentThread(myVm);
+                return NULL;
+            }
+        }
+    }
+
+    return env;
 }
 
 jint JNI_OnLoad(JavaVM *vm, void *reserved)
@@ -228,9 +262,16 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved)
 
     if ((*vm)->GetEnv(vm, (void**) &env, VLC_JNI_VERSION) != JNI_OK)
         return -1;
+
+    /* Create a TSD area and setup a destroy callback when a thread that
+     * previously set the jni_env_key is canceled or exited */
+    if (pthread_key_create(&jni_env_key, jni_detach_thread) != 0)
+        return -1;
+
     pthread_mutex_init(&vout_android_lock, NULL);
     pthread_cond_init(&vout_android_surf_attached, NULL);
 
+
 #define GET_CLASS(clazz, str, b_globlal) do { \
     (clazz) = (*env)->FindClass(env, (str)); \
     if (!(clazz)) { \
@@ -356,29 +397,8 @@ void JNI_OnUnload(JavaVM* vm, void* reserved)
     (*env)->DeleteGlobalRef(env, fields.String.clazz);
     (*env)->DeleteGlobalRef(env, fields.VLCObject.clazz);
     (*env)->DeleteGlobalRef(env, fields.Media.clazz);
-}
 
-int jni_attach_thread(JNIEnv **env, const char *thread_name)
-{
-    JavaVMAttachArgs args;
-    jint result;
-
-    args.version = VLC_JNI_VERSION;
-    args.name = thread_name;
-    args.group = NULL;
-
-    result = (*myVm)->AttachCurrentThread(myVm, env, &args);
-    return result == JNI_OK ? 0 : -1;
-}
-
-void jni_detach_thread()
-{
-    (*myVm)->DetachCurrentThread(myVm);
-}
-
-int jni_get_env(JNIEnv **env)
-{
-    return (*myVm)->GetEnv(myVm, (void **)env, VLC_JNI_VERSION) == JNI_OK ? 0 : -1;
+    pthread_key_delete(jni_env_key);
 }
 
 void Java_org_videolan_libvlc_LibVLC_nativeInit(JNIEnv *env, jobject thiz)
@@ -908,10 +928,10 @@ int jni_GetWindowSize(int *width, int *height)
 int aout_get_native_sample_rate(void)
 {
     JNIEnv *p_env;
-    jni_attach_thread (&p_env, THREAD_NAME);
+    if (!(p_env = jni_get_env(THREAD_NAME)))
+        return -1;
     jclass cls = (*p_env)->FindClass (p_env, "android/media/AudioTrack");
     jmethodID method = (*p_env)->GetStaticMethodID (p_env, cls, "getNativeOutputSampleRate", "(I)I");
     int sample_rate = (*p_env)->CallStaticIntMethod (p_env, cls, method, 3); // AudioManager.STREAM_MUSIC
-    jni_detach_thread ();
     return sample_rate;
 }
diff --git a/libvlc/jni/native_crash_handler.c b/libvlc/jni/native_crash_handler.c
index d05557b..31a19e7 100644
--- a/libvlc/jni/native_crash_handler.c
+++ b/libvlc/jni/native_crash_handler.c
@@ -27,9 +27,7 @@
 static struct sigaction old_actions[NSIG];
 
 #define THREAD_NAME "native_crash_handler"
-extern int jni_attach_thread(JNIEnv **env, const char *thread_name);
-extern void jni_detach_thread();
-extern int jni_get_env(JNIEnv **env);
+extern JNIEnv *jni_get_env(const char *name);
 
 // Monitored signals.
 static const int monitored_signals[] = {
@@ -53,13 +51,8 @@ static const int monitored_signals[] = {
 void sigaction_callback(int signal, siginfo_t *info, void *reserved)
 {
     JNIEnv *env;
-    bool b_attached = false;
-
-    if (jni_get_env(&env) < 0) {
-        if (jni_attach_thread(&env, THREAD_NAME) < 0)
-            return;
-        b_attached = true;
-    }
+    if (!(env = jni_get_env(THREAD_NAME)))
+        return;
 
     // Call the Java LibVLC method that handle the crash.
     (*env)->CallStaticVoidMethod(env, fields.LibVLC.clazz,
@@ -67,8 +60,6 @@ void sigaction_callback(int signal, siginfo_t *info, void *reserved)
 
     // Call the old signal handler.
     old_actions[signal].sa_handler(signal);
-    if (b_attached)
-        jni_detach_thread();
 }
 
 
diff --git a/libvlc/jni/vout.c b/libvlc/jni/vout.c
index 5bace67..a6928fd 100644
--- a/libvlc/jni/vout.c
+++ b/libvlc/jni/vout.c
@@ -24,9 +24,7 @@
 #include <jni.h>
 
 #define THREAD_NAME "jni_vout"
-extern int jni_attach_thread(JNIEnv **env, const char *thread_name);
-extern void jni_detach_thread();
-extern int jni_get_env(JNIEnv **env);
+extern JNIEnv *jni_get_env(const char *name);
 
 pthread_mutex_t vout_android_lock;
 pthread_cond_t vout_android_surf_attached;
@@ -56,7 +54,9 @@ void jni_UnlockAndroidSurface() {
 void jni_EventHardwareAccelerationError()
 {
     JNIEnv *env;
-    bool isAttached = false;
+
+    if (!(env = jni_get_env(THREAD_NAME)))
+        return;
 
     pthread_mutex_lock(&vout_android_lock);
     if (vout_android_gui == NULL) {
@@ -64,21 +64,11 @@ void jni_EventHardwareAccelerationError()
         return;
     }
 
-    if (jni_get_env(&env) < 0) {
-        if (jni_attach_thread(&env, THREAD_NAME) < 0) {
-            pthread_mutex_unlock(&vout_android_lock);
-            return;
-        }
-        isAttached = true;
-    }
-
     jclass cls = (*env)->GetObjectClass(env, vout_android_gui);
     jmethodID methodId = (*env)->GetMethodID(env, cls, "eventHardwareAccelerationError", "()V");
     (*env)->CallVoidMethod(env, vout_android_gui, methodId);
 
     (*env)->DeleteLocalRef(env, cls);
-    if (isAttached)
-        jni_detach_thread();
     pthread_mutex_unlock(&vout_android_lock);
 }
 
@@ -102,17 +92,11 @@ static void jni_SetSurfaceLayoutEnv(JNIEnv *p_env, int width, int height, int vi
 void jni_SetSurfaceLayout(int width, int height, int visible_width, int visible_height, int sar_num, int sar_den)
 {
     JNIEnv *p_env;
-    bool isAttached = false;
 
-    if (jni_get_env(&p_env) < 0) {
-        if (jni_attach_thread(&p_env, THREAD_NAME) < 0)
-            return;
-        isAttached = true;
-    }
-    jni_SetSurfaceLayoutEnv(p_env, width, height, visible_width, visible_height, sar_num, sar_den);
+    if (!(p_env = jni_get_env(THREAD_NAME)))
+        return;
 
-    if (isAttached)
-        jni_detach_thread();
+    jni_SetSurfaceLayoutEnv(p_env, width, height, visible_width, visible_height, sar_num, sar_den);
 }
 
 void *jni_AndroidJavaSurfaceToNativeSurface(jobject surf)
@@ -121,13 +105,10 @@ void *jni_AndroidJavaSurfaceToNativeSurface(jobject surf)
     jclass clz;
     jfieldID fid;
     void *native_surface = NULL;
-    bool isAttached = false;
 
-    if (jni_get_env(&p_env) < 0) {
-        if (jni_attach_thread(&p_env, THREAD_NAME) < 0)
-            return NULL;
-        isAttached = true;
-    }
+    if (!(p_env = jni_get_env(THREAD_NAME)))
+        return NULL;
+
     clz = (*p_env)->GetObjectClass(p_env, surf);
     fid = (*p_env)->GetFieldID(p_env, clz, "mSurface", "I");
     if (fid == NULL) {
@@ -149,30 +130,23 @@ void *jni_AndroidJavaSurfaceToNativeSurface(jobject surf)
         native_surface = (void*)(*p_env)->GetIntField(p_env, surf, fid);
     (*p_env)->DeleteLocalRef(p_env, clz);
 
-    if (isAttached)
-        jni_detach_thread();
     return native_surface;
 }
 
 int jni_ConfigureSurface(jobject jsurf, int width, int height, int hal, bool *configured)
 {
     JNIEnv *p_env;
-    bool isAttached = false;
     int ret;
 
+    if (!(p_env = jni_get_env(THREAD_NAME)))
+        return -1;
+
     pthread_mutex_lock(&vout_android_lock);
     if (vout_android_gui == NULL) {
         pthread_mutex_unlock(&vout_android_lock);
         return -1;
     }
 
-    if (jni_get_env(&p_env) < 0) {
-        if (jni_attach_thread(&p_env, THREAD_NAME) < 0) {
-            pthread_mutex_unlock(&vout_android_lock);
-            return -1;
-        }
-        isAttached = true;
-    }
     jclass clz = (*p_env)->GetObjectClass (p_env, vout_android_gui);
     jmethodID methodId = (*p_env)->GetMethodID (p_env, clz, "configureSurface", "(Landroid/view/Surface;III)I");
     ret = (*p_env)->CallIntMethod (p_env, vout_android_gui, methodId, jsurf, width, height, hal);
@@ -181,8 +155,6 @@ int jni_ConfigureSurface(jobject jsurf, int width, int height, int hal, bool *co
 
     (*p_env)->DeleteLocalRef(p_env, clz);
 
-    if (isAttached)
-        jni_detach_thread();
     pthread_mutex_unlock(&vout_android_lock);
     return ret == -1 ? -1 : 0;
 }
-- 
2.1.3



More information about the Android mailing list