]> sigrok.org Git - libsigrok.git/commitdiff
Java: Don't use JNIEnv* captured by lambdas, it may be invalid for the context
authorMarcus Comstedt <redacted>
Sun, 1 Nov 2015 22:27:41 +0000 (23:27 +0100)
committerUwe Hermann <redacted>
Mon, 2 Nov 2015 17:37:10 +0000 (18:37 +0100)
bindings/java/org/sigrok/core/classes/classes.i

index 894c72f75b9d501daa582b75e744451588018df8..681515c45cde02130b546a33112921a61b24d9f4 100644 (file)
@@ -237,6 +237,40 @@ MAP_COMMON(const sigrok::ConfigKey *, std::set<enum sigrok::Capability>,
    $1 = jenv;
 %} 
 
+/* Thread safe JNIEnv handling */
+
+%inline {
+namespace {
+  class ScopedEnv {
+    public:
+      ScopedEnv(JavaVM *jvm);
+      ScopedEnv(const ScopedEnv &ref) = delete;
+      ~ScopedEnv();
+      JNIEnv* operator-> () { return env; }
+      operator bool () const { return (bool)env; }
+    private:
+      JavaVM *jvm;
+      JNIEnv *env;
+      int env_status;
+  };
+  ScopedEnv::ScopedEnv(JavaVM *jvm) : jvm(jvm), env(NULL) {
+    env_status = jvm->GetEnv((void **)&env, JNI_VERSION_1_2);
+    if (env_status == JNI_EDETACHED) {
+%#if defined(__ANDROID__)
+      jvm->AttachCurrentThread(&env, NULL);
+%#else
+      jvm->AttachCurrentThread((void **)&env, NULL);
+%#endif
+    }
+  }
+  ScopedEnv::~ScopedEnv() {
+    if (env_status == JNI_EDETACHED) {
+      jvm->DetachCurrentThread();
+    }
+  }
+}
+}
+
 /* Support Java log callbacks. */
 
 %typemap(javaimports) sigrok::Context
@@ -255,6 +289,8 @@ typedef jobject jlogcallback;
 {
   void add_log_callback(JNIEnv *env, jlogcallback obj)
   {
+    JavaVM *jvm = NULL;
+    env->GetJavaVM(&jvm);
     jclass obj_class = env->GetObjectClass(obj);
     jmethodID method = env->GetMethodID(obj_class, "run",
       "(Lorg/sigrok/core/classes/LogLevel;Ljava/lang/String;)V");
@@ -267,6 +303,9 @@ typedef jobject jlogcallback;
       const sigrok::LogLevel *loglevel,
       std::string message)
     {
+      ScopedEnv env(jvm);
+      if (!env)
+        throw sigrok::Error(SR_ERR);
       jlong loglevel_addr = 0;
       *(const sigrok::LogLevel **) &loglevel_addr = loglevel;
       jobject loglevel_obj = env->NewObject(
@@ -297,6 +336,8 @@ typedef jobject jdatafeedcallback;
 {
   void add_datafeed_callback(JNIEnv *env, jdatafeedcallback obj)
   {
+    JavaVM *jvm = NULL;
+    env->GetJavaVM(&jvm);
     jclass obj_class = env->GetObjectClass(obj);
     jmethodID method = env->GetMethodID(obj_class, "run",
       "(Lorg/sigrok/core/classes/Device;Lorg/sigrok/core/classes/Packet;)V");
@@ -312,6 +353,9 @@ typedef jobject jdatafeedcallback;
       std::shared_ptr<sigrok::Device> device,
       std::shared_ptr<sigrok::Packet> packet)
     {
+      ScopedEnv env(jvm);
+      if (!env)
+        throw sigrok::Error(SR_ERR);
       jlong device_addr = 0;
       jlong packet_addr = 0;
       *(std::shared_ptr<sigrok::Device> **) &device_addr =