📄 gthread-jni.c
字号:
tracing ("cond_broadcast_jni_impl(condObj=%p)", condObj); e.jni_env = &env; (*the_vm)->GetEnv (the_vm, e.void_env, JNI_VERSION_1_1); if (setup_cache (env) < 0) goto done; HIDE_OLD_TROUBLE (env); assert (condObj); /* Must have locked an object to call notifyAll */ if (ENTER_MONITOR (env, condObj)) goto done; (*env)->CallVoidMethod (env, condObj, obj_notifyall_mth); if (MAYBE_BROKEN (env, "cannot broadcast to mutex with Object.notify()")) { EXIT_MONITOR (env, condObj); goto done; } EXIT_MONITOR (env, condObj); SHOW_OLD_TROUBLE ();done: if (TRACE_API_CALLS) tracing (" ==> VOID\n");}/* Wait on a condition variable. For us, this simply means calling * Object.wait. * * Throws a Java exception on trouble; may leave the mutexes set arbitrarily. * XXX TODO: Further improve error recovery. */static voidcond_wait_jni_impl (GCond * gcond, GMutex * gmutex){ struct mutexObj_cache cache; jobject condObj = (jobject) gcond; jobject mutexObj = (jobject) gmutex; JNIEnv *env; union env_union e; if (TRACE_API_CALLS) tracing ("cond_wait_jni_impl(condObj=%p, mutexObj=%p)", condObj, mutexObj); e.jni_env = &env; (*the_vm)->GetEnv (the_vm, e.void_env, JNI_VERSION_1_1); if (setup_cache (env) < 0) goto done; HIDE_OLD_TROUBLE (env); assert (condObj); assert (mutexObj); /* Must have locked a Java object to call wait on it */ if (ENTER_MONITOR (env, condObj) < 0) goto done; /* Our atomicity is now guaranteed; we're protected by the Java monitor on condObj. Unlock the GMutex. */ if (mutexObj_unlock (env, mutexObj, &cache)) goto done; (*env)->CallVoidMethod (env, condObj, obj_wait_mth); if (MAYBE_BROKEN (env, "cannot wait on condObj")) { EXIT_MONITOR (env, condObj); /* ignore err checking */ goto done; } /* Re-acquire the lock on the GMutex. Do this while we're protected by the Java monitor on condObj. */ if (mutexObj_lock (env, mutexObj, &cache)) goto done; EXIT_MONITOR (env, condObj); SHOW_OLD_TROUBLE ();done: if (TRACE_API_CALLS) tracing (" ==> VOID\n");}/** Wait on a condition variable until a timeout. This is a little tricky * for us. We first call Object.wait(J) giving it the appropriate timeout * value. On return, we check whether an InterruptedException happened. If * so, that is Java-speak for wait timing out. * * We return FALSE if we timed out. Return TRUE if the condition was * signalled first, before we timed out. * * In case of trouble we throw a Java exception. Whether we return FALSE or * TRUE depends upon whether the condition was raised before the trouble * happened. * * I believe that this function goes to the proper lengths to try to unlock * all of the locked mutexes and monitors, as appropriate, and that it further * tries to make sure that the thrown exception is the current one, not any * future cascaded one from something like a failure to unlock the monitors. */static gbooleancond_timed_wait_jni_impl (GCond * gcond, GMutex * gmutex, GTimeVal * end_time){ JNIEnv *env; union env_union e; jlong time_millisec; jint time_nanosec; jthrowable cause; jobject condObj = (jobject) gcond; jobject mutexObj = (jobject) gmutex; gboolean condRaised = FALSE; /* Condition has not been raised yet. */ struct mutexObj_cache cache; gboolean interrupted; if (TRACE_API_CALLS) { tracing ("cond_timed_wait_jni_impl(cond=%p, mutex=%p," " end_time=< sec=%lu, usec=%lu >)", condObj, mutexObj, (unsigned long) end_time->tv_sec, (unsigned long) end_time->tv_usec); } e.jni_env = &env; (*the_vm)->GetEnv (the_vm, e.void_env, JNI_VERSION_1_1); if (setup_cache (env) < 0) goto done; HIDE_OLD_TROUBLE (env); time_millisec = end_time->tv_sec * 1000 + end_time->tv_usec / 1000; time_nanosec = 1000 * (end_time->tv_usec % 1000); /* Must have locked an object to call wait */ if (ENTER_MONITOR (env, condObj) < 0) goto done; if (mutexObj_unlock (env, mutexObj, &cache) < 0) { if (EXIT_MONITOR (env, condObj) < 0) criticalMsg ("Unable to unlock an existing lock on a condition; your proram may deadlock"); goto done; } (*env)->CallVoidMethod (env, condObj, obj_wait_nanotime_mth, time_millisec, time_nanosec); /* If there was trouble, save that fact, and the reason for the trouble. We want to respond to this condition as fast as possible. */ cause = (*env)->ExceptionOccurred (env); if ( ! cause ) { condRaised = TRUE; /* condition was signalled */ } else if ((*env)->IsInstanceOf (env, cause, interrupted_exception_class)) { condRaised = FALSE; /* Condition was not raised before timeout. (This is redundant with the initialization of condRaised above) */ (*env)->ExceptionClear (env); /* Clear the InterruptedException. */ cause = NULL; /* no pending cause now. */ } else { interrupted = FALSE; /* Trouble, but not because of InterruptedException. Assume the condition was not raised. */ /* Leave condRaised set to FALSE */ } /* Irrespective of whether there is a pending problem to report, go ahead and try to clean up. This may end up throwing an exception that is different from the one that was thrown by the call to Object.wait(). So we will override it with the first exception (don't want to have cascading problems). */ if (mutexObj_lock (env, mutexObj, &cache) && !cause) { cause = (*env)->ExceptionOccurred (env); assert (cause); } if (EXIT_MONITOR (env, condObj) && !cause) { cause = (*env)->ExceptionOccurred (env); assert (cause); } if (cause) /* Raise the first cause. */ { BROKEN_CAUSE (env, cause, "error in timed wait or during its cleanup"); goto done; } SHOW_OLD_TROUBLE ();done: if (TRACE_API_CALLS) tracing (" ==> condRaised = %s\n", condRaised ? "TRUE" : "FALSE"); return condRaised;}/* Free a condition variable. (isn't C fun?). Can not fail. */static voidcond_free_jni_impl (GCond * cond){ jobject condObj = (jobject) cond; JNIEnv *env; union env_union e; if (TRACE_API_CALLS) tracing ("cond_free_jni_impl(condObj = %p)", condObj); e.jni_env = &env; (*the_vm)->GetEnv (the_vm, e.void_env, JNI_VERSION_1_1); freeObject (env, condObj); if (TRACE_API_CALLS) tracing (" ==> VOID\n");}/************************************************************************//* Thread-local data code *//************************************************************************//* Create a new thread-local key. We use java.lang.ThreadLocal objects * for this. This returns the pointer representation of a Java global * reference. * * We will throw a Java exception and return NULL in case of failure. */static GPrivate *private_new_jni_impl (GDestroyNotify notify __attribute__ ((unused))){ JNIEnv *env; union env_union e; jobject lcl_key; jobject global_key; GPrivate *gkey = NULL; /* Error return code */ if (TRACE_API_CALLS) tracing ("private_new_jni_impl()"); e.jni_env = &env; (*the_vm)->GetEnv (the_vm, e.void_env, JNI_VERSION_1_1); if (setup_cache (env) < 0) goto done; HIDE_OLD_TROUBLE (env); lcl_key = (*env)->NewObject (env, threadlocal_class, threadlocal_ctor); if ( ! lcl_key ) { BROKEN (env, "cannot allocate a ThreadLocal"); goto done; } global_key = ((*env)->NewGlobalRef (env, lcl_key)); DELETE_LOCAL_REF (env, lcl_key); if ( ! global_key) { NEW_BROKEN (env, "cannot create a GlobalRef to a new ThreadLocal"); goto done; } gkey = (GPrivate *) global_key; SHOW_OLD_TROUBLE ();done: if (TRACE_API_CALLS) tracing (" ==> %p\n", (void *) gkey); return gkey;}/* Get this thread's value for a thread-local key. This is simply * ThreadLocal.get for us. Return NULL if no value. (I can't think of * anything else to do.) */static gpointerprivate_get_jni_impl (GPrivate * gkey){ JNIEnv *env; union env_union e; jobject val_wrapper; jobject keyObj = (jobject) gkey; gpointer thread_specific_data = NULL; /* Init to the error-return value */ jlong val; if (TRACE_API_CALLS) tracing ("private_get_jni_impl(keyObj=%p)", keyObj); e.jni_env = &env; (*the_vm)->GetEnv (the_vm, e.void_env, JNI_VERSION_1_1); if (setup_cache (env) < 0) goto done; HIDE_OLD_TROUBLE (env); val_wrapper = (*env)->CallObjectMethod (env, keyObj, threadlocal_get_mth); if (MAYBE_BROKEN (env, "cannot find thread-local object")) goto done; if (! val_wrapper ) { /* It's Java's "null" object. No ref found. This is OK; we must never have set a value in this thread. Note that this next statement is not necessary, strictly speaking, since we're already initialized to NULL. A good optimizing C compiler will detect that and optimize out this statement. */ thread_specific_data = NULL; goto done; } val = (*env)->CallLongMethod (env, val_wrapper, long_longValue_mth); if (MAYBE_BROKEN (env, "cannot get thread local value")) goto done; thread_specific_data = (gpointer) (intptr_t) val; /* Only re-raise the old pending exception if a new one hasn't come along to supersede it. */ SHOW_OLD_TROUBLE ();done: if (TRACE_API_CALLS) tracing (" ==> %p\n", thread_specific_data); return thread_specific_data;}/* Set this thread's value for a thread-local key. This is simply * ThreadLocal.set() for us. */static voidprivate_set_jni_impl (GPrivate * gkey, gpointer thread_specific_data){ JNIEnv *env; union env_union e; jobject val_wrapper; jobject keyObj = (jobject) gkey; if (TRACE_API_CALLS) tracing ("private_set_jni_impl(keyObj=%p, thread_specific_data=%p)", keyObj, thread_specific_data); e.jni_env = &env; (*the_vm)->GetEnv (the_vm, e.void_env, JNI_VERSION_1_1); if (setup_cache (env) < 0) goto done; HIDE_OLD_TROUBLE (env); /* We are just going to always use a Java long to represent a C pointer. Otherwise all of the code would end up being conditionalized for various pointer sizes, and that seems like too much of a hassle, in order to save a paltry few bytes, especially given the horrendous overhead of JNI in any case. */ val_wrapper = (*env)->NewObject (env, long_class, long_ctor, (jlong) (intptr_t) thread_specific_data); if ( ! val_wrapper ) { BROKEN (env, "cannot create a java.lang.Long"); goto done; } /* At this point, we now have set lcl_obj as a numeric class that wraps around the thread-specific data we were given. */ (*env)->CallVoidMethod (env, keyObj, threadlocal_set_mth, val_wrapper); if (MAYBE_BROKEN (env, "cannot set thread local value")) goto done; SHOW_OLD_TROUBLE ();done: if (TRACE_API_CALLS) tracing (" ==> VOID\n");}/** Create an object of type gnu.java.awt.peer.gtk.GThreadNativeMethodRunner. Run it. We need to create joinable threads. We handle the notion of a joinable thread by determining whether or not we are going to maintain a permanent hard reference to it until it croaks. Posix does not appear to have a Java-like concept of daemon threads, where the JVM will exit when there are only daemon threads running. Error handling: To quote from the glib guide: "GError should only be used to report recoverable runtime errors, never to report programming errors." So how do we consider the failure to create a thread? Well, each of the failure cases in this function are discussed, and none of them are really recoverable. The glib library is really designed so that you should fail catastrophically in case of "programming errors". The only error defined for the GThread functions is G_THREAD_ERROR_AGAIN, and that for thread_create. Most of these GThread functions could fail if we run out of memory, for example, but the only one capable of reporting that fact is thread_create. */static voidthread_create_jni_impl (GThreadFunc func, gpointer data, gulong stack_size __attribute__((unused)), gboolean joinable, gboolean bound __attribute__((unused)), GThreadPriority gpriority, /* This prototype is horrible. threadIDp is actually a gpointer to the thread's thread-ID. Which is, of course, itself a gpointer-typed value. Ouch. */ gpointer
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -