+static JNIEnv *cjni_thread_attach (void) /* {{{ */
+{
+ cjni_jvm_env_t *cjni_env;
+ JNIEnv *jvm_env;
+
+ cjni_env = pthread_getspecific (jvm_env_key);
+ if (cjni_env == NULL)
+ {
+ /* This pointer is free'd in `cjni_jvm_env_destroy'. */
+ cjni_env = (cjni_jvm_env_t *) malloc (sizeof (*cjni_env));
+ if (cjni_env == NULL)
+ {
+ ERROR ("java plugin: cjni_thread_attach: malloc failed.");
+ return (NULL);
+ }
+ memset (cjni_env, 0, sizeof (*cjni_env));
+ cjni_env->reference_counter = 0;
+ cjni_env->jvm_env = NULL;
+
+ pthread_setspecific (jvm_env_key, cjni_env);
+ }
+
+ if (cjni_env->reference_counter > 0)
+ {
+ cjni_env->reference_counter++;
+ jvm_env = cjni_env->jvm_env;
+ }
+ else
+ {
+ int status;
+ JavaVMAttachArgs args;
+
+ assert (cjni_env->jvm_env == NULL);
+
+ memset (&args, 0, sizeof (args));
+ args.version = JNI_VERSION_1_2;
+
+ status = (*jvm)->AttachCurrentThread (jvm, (void *) &jvm_env, (void *) &args);
+ if (status != 0)
+ {
+ ERROR ("java plugin: cjni_thread_attach: AttachCurrentThread failed "
+ "with status %i.", status);
+ return (NULL);
+ }
+
+ cjni_env->reference_counter = 1;
+ cjni_env->jvm_env = jvm_env;
+ }
+
+ DEBUG ("java plugin: cjni_thread_attach: cjni_env->reference_counter = %i",
+ cjni_env->reference_counter);
+ assert (jvm_env != NULL);
+ return (jvm_env);
+} /* }}} JNIEnv *cjni_thread_attach */
+
+static int cjni_thread_detach (void) /* {{{ */
+{
+ cjni_jvm_env_t *cjni_env;
+ int status;
+
+ cjni_env = pthread_getspecific (jvm_env_key);
+ if (cjni_env == NULL)
+ {
+ ERROR ("java plugin: cjni_thread_detach: pthread_getspecific failed.");
+ return (-1);
+ }
+
+ assert (cjni_env->reference_counter > 0);
+ assert (cjni_env->jvm_env != NULL);
+
+ cjni_env->reference_counter--;
+ DEBUG ("java plugin: cjni_thread_detach: cjni_env->reference_counter = %i",
+ cjni_env->reference_counter);
+
+ if (cjni_env->reference_counter > 0)
+ return (0);
+
+ status = (*jvm)->DetachCurrentThread (jvm);
+ if (status != 0)
+ {
+ ERROR ("java plugin: cjni_thread_detach: DetachCurrentThread failed "
+ "with status %i.", status);
+ }
+
+ cjni_env->reference_counter = 0;
+ cjni_env->jvm_env = NULL;
+
+ return (0);
+} /* }}} JNIEnv *cjni_thread_attach */
+
+static void cjni_jvm_env_destroy (void *args) /* {{{ */
+{
+ cjni_jvm_env_t *cjni_env;
+
+ if (args == NULL)
+ return;
+
+ cjni_env = (cjni_jvm_env_t *) args;
+
+ if (cjni_env->reference_counter > 0)
+ {
+ ERROR ("java plugin: cjni_jvm_env_destroy: "
+ "cjni_env->reference_counter = %i;", cjni_env->reference_counter);
+ }
+
+ if (cjni_env->jvm_env != NULL)
+ {
+ ERROR ("java plugin: cjni_jvm_env_destroy: cjni_env->jvm_env = %p;",
+ (void *) cjni_env->jvm_env);
+ }
+
+ /* The pointer is allocated in `cjni_thread_attach' */
+ free (cjni_env);
+} /* }}} void cjni_jvm_env_destroy */
+
+/*
+ * Delete a global reference, set by the various `Register*' functions.
+ */
+static void cjni_callback_info_destroy (void *arg) /* {{{ */
+{
+ JNIEnv *jni_env;
+ cjni_callback_info_t *cbi;
+
+ DEBUG ("java plugin: cjni_callback_info_destroy (arg = %p);", arg);
+
+ if (arg == NULL)
+ return;
+
+ jni_env = cjni_thread_attach ();
+ if (jni_env == NULL)
+ {
+ ERROR ("java plugin: cjni_callback_info_destroy: cjni_thread_attach failed.");
+ return;
+ }
+
+ cbi = (cjni_callback_info_t *) arg;
+
+ (*jni_env)->DeleteGlobalRef (jni_env, cbi->object);
+
+ cbi->method = NULL;
+ cbi->object = NULL;
+ cbi->class = NULL;
+ free (cbi);
+
+ cjni_thread_detach ();
+} /* }}} void cjni_callback_info_destroy */
+