📄 gthread-jni.c
字号:
if (cause) { jthrowable currentException = (*env)->ExceptionOccurred (env); if (cause == currentException) { criticalMsg ("Description follows to System.err:"); (*env)->ExceptionDescribe (env); /* ExceptionDescribe has the side-effect of clearing the pending exception; relaunch it. */ describedException = TRUE; if ((*env)->Throw (env, cause)) { BADLY_BROKEN1 ("Relaunching an exception with Throw failed."); return -1; } } else { DELETE_LOCAL_REF (env, currentException); criticalMsg (WHERE "currentException != cause; something else happened" " while handling an exception."); } } } /* if (EXPLAIN_TROUBLE) */ if (isBroken && DIE_IF_BROKEN) fatalMsg ("%s:%d: Aborting execution; BROKEN: %s\n", file, line, message); if ((buf = malloc (len))) { memset (buf, 0, len); g_snprintf (buf, len, fmt, message, file, line); jmessage = (*env)->NewStringUTF (env, buf); free (buf); } else { jmessage = NULL; } /* Create the RuntimeException wrapper object and throw it. It is OK for CAUSE to be NULL. */ wrapper = (jthrowable) (*env)->NewObject (env, runtimeException_class, runtimeException_ctor, jmessage, cause); DELETE_LOCAL_REF (env, jmessage); if (!wrapper) { /* I think this should only happen: - if there are bugs in my JNI code, or - if the VM is broken, or - if we run out of memory. */ if (EXPLAIN_TROUBLE) { criticalMsg (WHERE "GNU Classpath: JNI NewObject() could not create" " a new java.lang.RuntimeException."); criticalMsg ("We were trying to warn about the following" " previous failure:"); criticalMsg ("%s:%d: %s", file, line, message); criticalMsg ("The latest (NewObject()) exception's description" " follows, to System.err:"); (*env)->ExceptionDescribe (env); } BADLY_BROKEN1 ("Failure of JNI NewObject()" " to make a java.lang.RuntimeException"); return -1; } /* throw it */ if ((*env)->Throw (env, wrapper)) { /* Throw() should just never fail, unless we're in such severe trouble that we might as well die. */ BADLY_BROKEN1 ("GNU Classpath: Failure of JNI Throw to report an Exception"); return -1; } DELETE_LOCAL_REF (env, wrapper); return 1;}/* Rethrow an exception we received, wrapping it with a RuntimeException. 1 if we did rethrow, -1 if we had trouble while rethrowing. CAUSE should be identical to the most recent exception that happened, so that ExceptionDescribe will work. (Otherwise nix.) */static intrethrow (JNIEnv * env, jthrowable cause, const char *message, gboolean isBroken, const char *file, int line){ assert (cause); return throw (env, cause, message, isBroken, file, line);}/* This function checks for a pending exception, and rethrows it with * a wrapper RuntimeException to deal with possible type problems (in * case some calling piece of code does not expect the exception being * thrown) and to include the given extra message. * * Returns 0 if no problems found (so no exception thrown), 1 if we rethrew an * exception. Returns -1 on failure. */static intmaybe_rethrow (JNIEnv * env, const char *message, gboolean isBroken, const char *file, int line){ jthrowable cause = (*env)->ExceptionOccurred (env); int ret = 0; /* rethrow if an exception happened */ if (cause) { ret = rethrow (env, cause, message, isBroken, file, line); DELETE_LOCAL_REF (env, cause); } return 0;}/* MAYBE_TROUBLE() is used to include a source location in the exception message. Once we have run maybe_rethrow, if there WAS trouble, return TRUE, else FALSE. MAYBE_TROUBLE() is actually never used; all problems that throw exceptions are BROKEN, at least. Nothing is recoverable :(. See the discussion of possible errors at thread_create_jni_impl(). */#define MAYBE_TROUBLE(_env, _message) \ maybe_rethrow(_env, _message, FALSE, __FILE__, __LINE__)/* MAYBE_TROUBLE(), but something would be BROKEN if it were true. */#define MAYBE_BROKEN(_env, _message) \ maybe_rethrow(_env, _message, TRUE, __FILE__, __LINE__)/* Like MAYBE_TROUBLE(), TROUBLE() is never used. */#define TROUBLE(_env, _message) \ rethrow(_env, (*env)->ExceptionOccurred (env), _message, FALSE, \ __FILE__, __LINE__)#define BROKEN(_env, _message) \ rethrow (_env, (*env)->ExceptionOccurred (env), _message, TRUE, \ __FILE__, __LINE__)/* Like MAYBE_TROUBLE(), NEW_TROUBLE() is never used. */#define NEW_TROUBLE(_env, _message) \ throw (_env, NULL, _message, FALSE, __FILE__, __LINE__)#define NEW_BROKEN(_env, _message) \ throw (_env, NULL, _message, TRUE, __FILE__, __LINE__)/* Like MAYBE_TROUBLE(), RETHROW_CAUSE() is never used. */#define RETHROW_CAUSE(_env, _cause, _message) \ rethrow (_env, _cause, _message, FALSE, __FILE__, __LINE__)#define BROKEN_CAUSE(_env, _cause, _message) \ rethrow (_env, _cause, _message, TRUE, __FILE__, __LINE__)/* Macros to handle the possibility that someone might have called one of the GThreadFunctions API functions with a Java exception pending. It is generally discouraged to continue to use JNI after a Java exception has been raised. Sun's JNI book advises that one trap JNI errors immediately and not continue with an exception pending. These are #if'd out for these reasons: 1) They do not work in the C '89 subset that Classpath is currently (2004 May 10) sticking to; HIDE_OLD_TROUBLE() includes a declaration that should be in scope for the rest of the function, so it needs a language version that lets you mix declarations and statements. (This could be worked around if it were important.) 2) They chew up more time and resources. 3) There does not ever seem to be old trouble -- the assertion in HIDE_OLD_TROUBLE never goes off. You will want to re-enable them if this code needs to be used in a context where old exceptions might be pending when the GThread functions are called. The implementations in this file are responsible for skipping around calls to SHOW_OLD_TROUBLE() if they've raised exceptions during the call. So, if we reach SHOW_OLD_TROUBLE, we are guaranteed that there are no exceptions pending. */#if 1#define HIDE_OLD_TROUBLE(env) \ assert ( NULL == (*env)->ExceptionOccurred (env) )#define SHOW_OLD_TROUBLE() \ assert ( NULL == (*env)->ExceptionOccurred (env) )#else /* 0 */#define HIDE_OLD_TROUBLE(env) \ jthrowable savedTrouble = (*env)->ExceptionOccurred (env); \ (*env)->ExceptionClear (env);#define SHOW_OLD_TROUBLE() do \{ \ assert ( NULL == (*env)->ExceptionOccurred (env) ) \ if (savedTrouble) \ { \ if ((*env)->Throw (env, savedTrouble)) \ BADLY_BROKEN ("ReThrowing the savedTrouble failed"); \ } \ DELETE_LOCAL_REF (env, savedTrouble); \} while(0)#endif /* 0 *//* Set up the cache of jclass and jmethodID primitives we need in order to throw new exceptions and rethrow exceptions. We do this independently of the other caching. We need to have this cache set up first, so that we can then report errors properly. If any errors while setting up the error cache, the world is BADLY_BROKEN. May be called more than once. Returns -1 if the cache was not initialized properly, 1 if it was. */static intsetup_exception_cache (JNIEnv * env){ static int exception_cache_initialized = 0; /* -1 for trouble, 1 for proper init. */ jclass lcl_class; /* a class used for local refs */ if (exception_cache_initialized) return exception_cache_initialized; lcl_class = (*env)->FindClass (env, "java/lang/RuntimeException"); if ( ! lcl_class ) { BADLY_BROKEN1 ("Broken Class library or VM?" " Couldn't find java/lang/RuntimeException"); return exception_cache_initialized = -1; } /* Pin it down. */ runtimeException_class = (jclass) (*env)->NewGlobalRef (env, lcl_class); DELETE_LOCAL_REF (env, lcl_class); if (!runtimeException_class) { BADLY_BROKEN1 ("Serious trouble: could not turn" " java.lang.RuntimeException into a global reference"); return exception_cache_initialized = -1; } runtimeException_ctor = (*env)->GetMethodID (env, runtimeException_class, "<init>", "(Ljava/lang/String;Ljava/lang/Throwable;)V"); if ( ! runtimeException_ctor ) { BADLY_BROKEN1 ("Serious trouble: classpath couldn't find a" " two-arg constructor for java/lang/RuntimeException"); return exception_cache_initialized = -1; } return exception_cache_initialized = 1;}/**********************************************************//***** The main cache *************************************//**********************************************************//** This is a cache of all classes, methods, and field IDs that we use during the run. We maintain a permanent global reference to each of the classes we cache, since otherwise the (local) jclass that refers to that class would go out of scope and possibly be reused in further calls. The permanent global reference also achieves the secondary goal of protecting the validity of the methods and field IDs in case the classes were otherwise unloaded and then later loaded again. Obviously, this will never happen to classes such as java.lang.Thread and java.lang.Object, but the primary reason for maintaining permanent global refs is sitll valid. The code in jnilink.c has a similar objective. TODO: Consider using that code instead. --Steven Augart*//* All of these are cached classes and method IDs: *//* java.lang.Object */static jclass obj_class; /* java.lang.Object */static jmethodID obj_ctor; /* no-arg Constructor for java.lang.Object */static jmethodID obj_notify_mth; /* java.lang.Object.notify() */static jmethodID obj_notifyall_mth; /* java.lang.Object.notifyall() */static jmethodID obj_wait_mth; /* java.lang.Object.wait() */static jmethodID obj_wait_nanotime_mth; /* java.lang.Object.wait(JI) *//* GThreadMutex and its methods */static jclass mutex_class;static jmethodID mutex_ctor;static jfieldID mutex_lockForPotentialLockers_fld;static jfieldID mutex_potentialLockers_fld;/* java.lang.Thread and its methods*/static jclass thread_class; /* java.lang.Thread */static jmethodID thread_current_mth; /* Thread.currentThread() */static jmethodID thread_equals_mth; /* Thread.equals() */static jmethodID thread_join_mth; /* Thread.join() */static jmethodID thread_setPriority_mth; /* Thread.setPriority() */static jmethodID thread_stop_mth; /* Thread.stop() */static jmethodID thread_yield_mth; /* Thread.yield() *//* java.lang.ThreadLocal and its methods */static jclass threadlocal_class; /* java.lang.ThreadLocal */static jmethodID threadlocal_ctor; /* Its constructor */static jmethodID threadlocal_set_mth; /* ThreadLocal.set() */static jmethodID threadlocal_get_mth; /* ThreadLocal.get() *//* java.lang.Long and its methods */static jclass long_class; /* java.lang.Long */static jmethodID long_ctor; /* constructor for it: (J) */static jmethodID long_longValue_mth; /* longValue()J *//* GThreadNativeMethodRunner */static jclass runner_class;static jmethodID runner_ctor;static jmethodID runner_threadToThreadID_mth;static jmethodID runner_threadIDToThread_mth;static jmethodID runner_deRegisterJoinable_mth;static jmethodID runner_start_mth; /* Inherited Thread.start() *//* java.lang.InterruptedException */static jclass interrupted_exception_class;/* Returns a negative value if there was trouble during initialization. Returns a positive value of the cache was initialized correctly. Never returns zero. */static intsetup_cache (JNIEnv * env){ jclass lcl_class; static int initialized = 0; /* 1 means initialized, 0 means uninitialized, -1 means mis-initialized */ if (initialized) return initialized; /* make sure we can report on trouble */ if (setup_exception_cache (env) < 0) return initialized = -1;#ifdef JNI_VERSION_1_2 if (HAVE_JNI_VERSION_1_2) assert ( ! (*env)->ExceptionCheck (env)); else#endif assert ( ! (*env)->ExceptionOccurred (env)); /* java.lang.Object and its methods */ lcl_class = (*env)->FindClass (env, "java/lang/Object"); if (!lcl_class) { BROKEN (env, "cannot find java.lang.Object"); return initialized = -1; } /* Pin it down. */ obj_class = (jclass) (*env)->NewGlobalRef (env, lcl_class); DELETE_LOCAL_REF (env, lcl_class); if (!obj_class) { BROKEN (env, "Cannot get a global reference to java.lang.Object"); return initialized = -1; } obj_ctor = (*env)->GetMethodID (env, obj_class, "<init>", "()V"); if (!obj_ctor) { BROKEN (env, "cannot find constructor for java.lang.Object"); return initialized = -1; } obj_notify_mth = (*env)->GetMethodID (env, obj_class, "notify", "()V"); if ( ! obj_notify_mth ) { BROKEN (env, "cannot find java.lang.Object.notify()V"); return initialized = -1; } obj_notifyall_mth = (*env)->GetMethodID (env, obj_class, "notifyAll", "()V"); if ( ! obj_notifyall_mth) { BROKEN (env, "cannot find java.lang.Object.notifyall()V"); return initialized = -1; } obj_wait_mth = (*env)->GetMethodID (env, obj_class, "wait", "()V"); if ( ! obj_wait_mth ) { BROKEN (env, "cannot find Object.<wait()V>"); return initialized = -1; } obj_wait_nanotime_mth = (*env)->GetMethodID (env, obj_class, "wait", "(JI)V"); if ( ! obj_wait_nanotime_mth ) { BROKEN (env, "cannot find Object.<wait(JI)V>"); return initialized = -1; } /* GThreadMutex and its methods */ lcl_class = (*env)->FindClass (env, "gnu/java/awt/peer/gtk/GThreadMutex"); if ( ! lcl_class) { BROKEN (env, "cannot find gnu.java.awt.peer.gtk.GThreadMutex"); return initialized = -1; } /* Pin it down. */ mutex_class = (jclass) (*env)->NewGlobalRef (env, lcl_class); DELETE_LOCAL_REF (env, lcl_class); if ( ! mutex_class) { BROKEN (env, "Cannot get a global reference to GThreadMutex");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -