HDFS-11529. Add libHDFS API to return last exception. Contributed by Sailesh Mukil.
This commit is contained in:
parent
20e3ae260b
commit
fda86ef2a3
@ -163,6 +163,24 @@ struct hdfsFile_internal;
|
||||
ret = -errno; \
|
||||
} while (ret == -EINTR);
|
||||
|
||||
#define EXPECT_STR_CONTAINS(str, substr) \
|
||||
do { \
|
||||
char *_my_ret_ = (str); \
|
||||
int _my_errno_ = errno; \
|
||||
if ((str) == NULL) { \
|
||||
fprintf(stderr, "TEST_ERROR: failed on %s:%d with NULL return " \
|
||||
"return value (errno: %d): expected substring: %s\n", \
|
||||
__FILE__, __LINE__, _my_errno_, (substr)); \
|
||||
return -1; \
|
||||
} \
|
||||
if (strstr((str), (substr)) == NULL) { \
|
||||
fprintf(stderr, "TEST_ERROR: failed on %s:%d with return " \
|
||||
"value %s (errno: %d): expected substring: %s\n", \
|
||||
__FILE__, __LINE__, _my_ret_, _my_errno_, (substr)); \
|
||||
return -1; \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
/**
|
||||
* Test that an HDFS file has the given statistics.
|
||||
*
|
||||
|
@ -165,6 +165,12 @@ static int doTestHdfsOperations(struct tlhThreadInfo *ti, hdfsFS fs,
|
||||
/* There should not be any file to open for reading. */
|
||||
EXPECT_NULL(hdfsOpenFile(fs, paths->file1, O_RDONLY, 0, 0, 0));
|
||||
|
||||
/* Check if the exceptions are stored in the TLS */
|
||||
EXPECT_STR_CONTAINS(hdfsGetLastExceptionRootCause(),
|
||||
"File does not exist");
|
||||
EXPECT_STR_CONTAINS(hdfsGetLastExceptionStackTrace(),
|
||||
"java.io.FileNotFoundException");
|
||||
|
||||
/* hdfsOpenFile should not accept mode = 3 */
|
||||
EXPECT_NULL(hdfsOpenFile(fs, paths->file1, 3, 0, 0, 0));
|
||||
|
||||
|
@ -110,15 +110,50 @@ void getExceptionInfo(const char *excName, int noPrintFlags,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* getExceptionUtilString: A helper function that calls 'methodName' in
|
||||
* ExceptionUtils. The function 'methodName' should have a return type of a
|
||||
* java String.
|
||||
*
|
||||
* @param env The JNI environment.
|
||||
* @param exc The exception to get information for.
|
||||
* @param methodName The method of ExceptionUtils to call that has a String
|
||||
* return type.
|
||||
*
|
||||
* @return A C-type string containing the string returned by
|
||||
* ExceptionUtils.'methodName', or NULL on failure.
|
||||
*/
|
||||
static char* getExceptionUtilString(JNIEnv *env, jthrowable exc, char *methodName)
|
||||
{
|
||||
jthrowable jthr;
|
||||
jvalue jVal;
|
||||
jstring jStr = NULL;
|
||||
char *excString = NULL;
|
||||
jthr = invokeMethod(env, &jVal, STATIC, NULL,
|
||||
"org/apache/commons/lang/exception/ExceptionUtils",
|
||||
methodName, "(Ljava/lang/Throwable;)Ljava/lang/String;", exc);
|
||||
if (jthr) {
|
||||
destroyLocalReference(env, jthr);
|
||||
return NULL;
|
||||
}
|
||||
jStr = jVal.l;
|
||||
jthr = newCStr(env, jStr, &excString);
|
||||
if (jthr) {
|
||||
destroyLocalReference(env, jthr);
|
||||
return NULL;
|
||||
}
|
||||
destroyLocalReference(env, jStr);
|
||||
return excString;
|
||||
}
|
||||
|
||||
int printExceptionAndFreeV(JNIEnv *env, jthrowable exc, int noPrintFlags,
|
||||
const char *fmt, va_list ap)
|
||||
{
|
||||
int i, noPrint, excErrno;
|
||||
char *className = NULL;
|
||||
jstring jStr = NULL;
|
||||
jvalue jVal;
|
||||
jthrowable jthr;
|
||||
const char *stackTrace;
|
||||
const char *rootCause;
|
||||
|
||||
jthr = classNameOfObject(exc, env, &className);
|
||||
if (jthr) {
|
||||
@ -139,32 +174,30 @@ int printExceptionAndFreeV(JNIEnv *env, jthrowable exc, int noPrintFlags,
|
||||
noPrint = 0;
|
||||
excErrno = EINTERNAL;
|
||||
}
|
||||
|
||||
// We don't want to use ExceptionDescribe here, because that requires a
|
||||
// pending exception. Instead, use ExceptionUtils.
|
||||
rootCause = getExceptionUtilString(env, exc, "getRootCauseMessage");
|
||||
stackTrace = getExceptionUtilString(env, exc, "getStackTrace");
|
||||
// Save the exception details in the thread-local state.
|
||||
setTLSExceptionStrings(rootCause, stackTrace);
|
||||
|
||||
if (!noPrint) {
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, " error:\n");
|
||||
|
||||
// We don't want to use ExceptionDescribe here, because that requires a
|
||||
// pending exception. Instead, use ExceptionUtils.
|
||||
jthr = invokeMethod(env, &jVal, STATIC, NULL,
|
||||
"org/apache/commons/lang/exception/ExceptionUtils",
|
||||
"getStackTrace", "(Ljava/lang/Throwable;)Ljava/lang/String;", exc);
|
||||
if (jthr) {
|
||||
fprintf(stderr, "(unable to get stack trace for %s exception: "
|
||||
"ExceptionUtils::getStackTrace error.)\n", className);
|
||||
destroyLocalReference(env, jthr);
|
||||
if (!rootCause) {
|
||||
fprintf(stderr, "(unable to get root cause for %s)\n", className);
|
||||
} else {
|
||||
jStr = jVal.l;
|
||||
stackTrace = (*env)->GetStringUTFChars(env, jStr, NULL);
|
||||
if (!stackTrace) {
|
||||
fprintf(stderr, "(unable to get stack trace for %s exception: "
|
||||
"GetStringUTFChars error.)\n", className);
|
||||
} else {
|
||||
fprintf(stderr, "%s", stackTrace);
|
||||
(*env)->ReleaseStringUTFChars(env, jStr, stackTrace);
|
||||
}
|
||||
fprintf(stderr, "%s", rootCause);
|
||||
}
|
||||
if (!stackTrace) {
|
||||
fprintf(stderr, "(unable to get stack trace for %s)\n", className);
|
||||
} else {
|
||||
fprintf(stderr, "%s", stackTrace);
|
||||
}
|
||||
}
|
||||
destroyLocalReference(env, jStr);
|
||||
|
||||
destroyLocalReference(env, exc);
|
||||
free(className);
|
||||
return excErrno;
|
||||
|
@ -29,9 +29,14 @@
|
||||
*
|
||||
* If you encounter an exception, return a local reference to it. The caller is
|
||||
* responsible for freeing the local reference, by calling a function like
|
||||
* PrintExceptionAndFree. (You can also free exceptions directly by calling
|
||||
* printExceptionAndFree. (You can also free exceptions directly by calling
|
||||
* DeleteLocalRef. However, that would not produce an error message, so it's
|
||||
* usually not what you want.)
|
||||
*
|
||||
* The root cause and stack trace exception strings retrieved from the last
|
||||
* exception that happened on a thread are stored in the corresponding
|
||||
* thread local state and are accessed by hdfsGetLastExceptionRootCause and
|
||||
* hdfsGetLastExceptionStackTrace respectively.
|
||||
*/
|
||||
|
||||
#include "platform.h"
|
||||
@ -81,7 +86,8 @@ void getExceptionInfo(const char *excName, int noPrintFlags,
|
||||
int *excErrno, int *shouldPrint);
|
||||
|
||||
/**
|
||||
* Print out information about an exception and free it.
|
||||
* Store the information about an exception in the thread-local state and print
|
||||
* it and free the jthrowable object.
|
||||
*
|
||||
* @param env The JNI environment
|
||||
* @param exc The exception to print and free
|
||||
@ -97,7 +103,8 @@ int printExceptionAndFreeV(JNIEnv *env, jthrowable exc, int noPrintFlags,
|
||||
const char *fmt, va_list ap);
|
||||
|
||||
/**
|
||||
* Print out information about an exception and free it.
|
||||
* Store the information about an exception in the thread-local state and print
|
||||
* it and free the jthrowable object.
|
||||
*
|
||||
* @param env The JNI environment
|
||||
* @param exc The exception to print and free
|
||||
@ -113,7 +120,8 @@ int printExceptionAndFree(JNIEnv *env, jthrowable exc, int noPrintFlags,
|
||||
const char *fmt, ...) TYPE_CHECKED_PRINTF_FORMAT(4, 5);
|
||||
|
||||
/**
|
||||
* Print out information about the pending exception and free it.
|
||||
* Store the information about the pending exception in the thread-local state
|
||||
* and print it and free the jthrowable object.
|
||||
*
|
||||
* @param env The JNI environment
|
||||
* @param noPrintFlags Flags which determine which exceptions we should NOT
|
||||
|
@ -2956,6 +2956,7 @@ done:
|
||||
destroyLocalReference(env, jFileBlockHosts);
|
||||
destroyLocalReference(env, jHost);
|
||||
if (ret) {
|
||||
errno = ret;
|
||||
if (blockHosts) {
|
||||
hdfsFreeHosts(blockHosts);
|
||||
}
|
||||
@ -3512,7 +3513,15 @@ int hdfsFileIsEncrypted(hdfsFileInfo *fileInfo)
|
||||
return !!(extInfo->flags & HDFS_EXTENDED_FILE_INFO_ENCRYPTED);
|
||||
}
|
||||
|
||||
char* hdfsGetLastExceptionRootCause()
|
||||
{
|
||||
return getLastTLSExceptionRootCause();
|
||||
}
|
||||
|
||||
char* hdfsGetLastExceptionStackTrace()
|
||||
{
|
||||
return getLastTLSExceptionStackTrace();
|
||||
}
|
||||
|
||||
/**
|
||||
* vim: ts=4: sw=4: et:
|
||||
|
@ -1042,6 +1042,38 @@ extern "C" {
|
||||
LIBHDFS_EXTERNAL
|
||||
void hadoopRzBufferFree(hdfsFile file, struct hadoopRzBuffer *buffer);
|
||||
|
||||
/**
|
||||
* Get the last exception root cause that happened in the context of the
|
||||
* current thread, i.e. the thread that called into libHDFS.
|
||||
*
|
||||
* The pointer returned by this function is guaranteed to be valid until
|
||||
* the next call into libHDFS by the current thread.
|
||||
* Users of this function should not free the pointer.
|
||||
*
|
||||
* A NULL will be returned if no exception information could be retrieved
|
||||
* for the previous call.
|
||||
*
|
||||
* @return The root cause as a C-string.
|
||||
*/
|
||||
LIBHDFS_EXTERNAL
|
||||
char* hdfsGetLastExceptionRootCause();
|
||||
|
||||
/**
|
||||
* Get the last exception stack trace that happened in the context of the
|
||||
* current thread, i.e. the thread that called into libHDFS.
|
||||
*
|
||||
* The pointer returned by this function is guaranteed to be valid until
|
||||
* the next call into libHDFS by the current thread.
|
||||
* Users of this function should not free the pointer.
|
||||
*
|
||||
* A NULL will be returned if no exception information could be retrieved
|
||||
* for the previous call.
|
||||
*
|
||||
* @return The stack trace as a C-string.
|
||||
*/
|
||||
LIBHDFS_EXTERNAL
|
||||
char* hdfsGetLastExceptionStackTrace();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -498,29 +498,98 @@ static JNIEnv* getGlobalJNIEnv(void)
|
||||
*/
|
||||
JNIEnv* getJNIEnv(void)
|
||||
{
|
||||
JNIEnv *env;
|
||||
THREAD_LOCAL_STORAGE_GET_QUICK();
|
||||
struct ThreadLocalState *state = NULL;
|
||||
THREAD_LOCAL_STORAGE_GET_QUICK(&state);
|
||||
if (state) return state->env;
|
||||
|
||||
mutexLock(&jvmMutex);
|
||||
if (threadLocalStorageGet(&env)) {
|
||||
if (threadLocalStorageGet(&state)) {
|
||||
mutexUnlock(&jvmMutex);
|
||||
return NULL;
|
||||
}
|
||||
if (env) {
|
||||
if (state) {
|
||||
mutexUnlock(&jvmMutex);
|
||||
return env;
|
||||
|
||||
// Free any stale exception strings.
|
||||
free(state->lastExceptionRootCause);
|
||||
free(state->lastExceptionStackTrace);
|
||||
state->lastExceptionRootCause = NULL;
|
||||
state->lastExceptionStackTrace = NULL;
|
||||
|
||||
return state->env;
|
||||
}
|
||||
|
||||
env = getGlobalJNIEnv();
|
||||
/* Create a ThreadLocalState for this thread */
|
||||
state = threadLocalStorageCreate();
|
||||
if (!state) {
|
||||
fprintf(stderr, "getJNIEnv: Unable to create ThreadLocalState\n");
|
||||
return NULL;
|
||||
}
|
||||
state->env = getGlobalJNIEnv();
|
||||
mutexUnlock(&jvmMutex);
|
||||
if (!env) {
|
||||
fprintf(stderr, "getJNIEnv: getGlobalJNIEnv failed\n");
|
||||
return NULL;
|
||||
if (!state->env) {
|
||||
goto fail;
|
||||
}
|
||||
if (threadLocalStorageSet(env)) {
|
||||
return NULL;
|
||||
if (threadLocalStorageSet(state)) {
|
||||
goto fail;
|
||||
}
|
||||
THREAD_LOCAL_STORAGE_SET_QUICK(env);
|
||||
return env;
|
||||
THREAD_LOCAL_STORAGE_SET_QUICK(state);
|
||||
|
||||
return state->env;
|
||||
|
||||
fail:
|
||||
fprintf(stderr, "getJNIEnv: getGlobalJNIEnv failed\n");
|
||||
hdfsThreadDestructor(state);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* getLastTLSExceptionRootCause()
|
||||
{
|
||||
struct ThreadLocalState *state = NULL;
|
||||
THREAD_LOCAL_STORAGE_GET_QUICK(&state);
|
||||
if (!state) {
|
||||
mutexLock(&jvmMutex);
|
||||
if (threadLocalStorageGet(&state)) {
|
||||
mutexUnlock(&jvmMutex);
|
||||
return NULL;
|
||||
}
|
||||
mutexUnlock(&jvmMutex);
|
||||
}
|
||||
return state->lastExceptionRootCause;
|
||||
}
|
||||
|
||||
char* getLastTLSExceptionStackTrace()
|
||||
{
|
||||
struct ThreadLocalState *state = NULL;
|
||||
THREAD_LOCAL_STORAGE_GET_QUICK(&state);
|
||||
if (!state) {
|
||||
mutexLock(&jvmMutex);
|
||||
if (threadLocalStorageGet(&state)) {
|
||||
mutexUnlock(&jvmMutex);
|
||||
return NULL;
|
||||
}
|
||||
mutexUnlock(&jvmMutex);
|
||||
}
|
||||
return state->lastExceptionStackTrace;
|
||||
}
|
||||
|
||||
void setTLSExceptionStrings(const char *rootCause, const char *stackTrace)
|
||||
{
|
||||
struct ThreadLocalState *state = NULL;
|
||||
THREAD_LOCAL_STORAGE_GET_QUICK(&state);
|
||||
if (!state) {
|
||||
mutexLock(&jvmMutex);
|
||||
if (threadLocalStorageGet(&state)) {
|
||||
mutexUnlock(&jvmMutex);
|
||||
return;
|
||||
}
|
||||
mutexUnlock(&jvmMutex);
|
||||
}
|
||||
|
||||
free(state->lastExceptionRootCause);
|
||||
free(state->lastExceptionStackTrace);
|
||||
state->lastExceptionRootCause = (char*)rootCause;
|
||||
state->lastExceptionStackTrace = (char*)stackTrace;
|
||||
}
|
||||
|
||||
int javaObjectIsOfClass(JNIEnv *env, jobject obj, const char *name)
|
||||
|
@ -105,6 +105,8 @@ jthrowable globalClassReference(const char *className, JNIEnv *env, jclass *out)
|
||||
jthrowable classNameOfObject(jobject jobj, JNIEnv *env, char **name);
|
||||
|
||||
/** getJNIEnv: A helper function to get the JNIEnv* for the given thread.
|
||||
* It gets this from the ThreadLocalState if it exists. If a ThreadLocalState
|
||||
* does not exist, one will be created.
|
||||
* If no JVM exists, then one will be created. JVM command line arguments
|
||||
* are obtained from the LIBHDFS_OPTS environment variable.
|
||||
* @param: None.
|
||||
@ -112,6 +114,39 @@ jthrowable classNameOfObject(jobject jobj, JNIEnv *env, char **name);
|
||||
* */
|
||||
JNIEnv* getJNIEnv(void);
|
||||
|
||||
/**
|
||||
* Get the last exception root cause that happened in the context of the
|
||||
* current thread.
|
||||
*
|
||||
* The pointer returned by this function is guaranteed to be valid until
|
||||
* the next call to invokeMethod() by the current thread.
|
||||
* Users of this function should not free the pointer.
|
||||
*
|
||||
* @return The root cause as a C-string.
|
||||
*/
|
||||
char* getLastTLSExceptionRootCause();
|
||||
|
||||
/**
|
||||
* Get the last exception stack trace that happened in the context of the
|
||||
* current thread.
|
||||
*
|
||||
* The pointer returned by this function is guaranteed to be valid until
|
||||
* the next call to invokeMethod() by the current thread.
|
||||
* Users of this function should not free the pointer.
|
||||
*
|
||||
* @return The stack trace as a C-string.
|
||||
*/
|
||||
char* getLastTLSExceptionStackTrace();
|
||||
|
||||
/** setTLSExceptionStrings: Sets the 'rootCause' and 'stackTrace' in the
|
||||
* ThreadLocalState if one exists for the current thread.
|
||||
*
|
||||
* @param rootCause A string containing the root cause of an exception.
|
||||
* @param stackTrace A string containing the stack trace of an exception.
|
||||
* @return None.
|
||||
*/
|
||||
void setTLSExceptionStrings(const char *rootCause, const char *stackTrace);
|
||||
|
||||
/**
|
||||
* Figure out if a Java object is an instance of a particular class.
|
||||
*
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "os/thread_local_storage.h"
|
||||
|
||||
#include <jni.h>
|
||||
#include <malloc.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
|
||||
@ -34,23 +35,48 @@ static int gTlsKeyInitialized = 0;
|
||||
*
|
||||
* @param v The thread-local data
|
||||
*/
|
||||
static void hdfsThreadDestructor(void *v)
|
||||
void hdfsThreadDestructor(void *v)
|
||||
{
|
||||
JavaVM *vm;
|
||||
JNIEnv *env = v;
|
||||
struct ThreadLocalState *state = (struct ThreadLocalState*)v;
|
||||
JNIEnv *env = state->env;;
|
||||
jint ret;
|
||||
|
||||
ret = (*env)->GetJavaVM(env, &vm);
|
||||
if (ret) {
|
||||
fprintf(stderr, "hdfsThreadDestructor: GetJavaVM failed with error %d\n",
|
||||
ret);
|
||||
(*env)->ExceptionDescribe(env);
|
||||
} else {
|
||||
(*vm)->DetachCurrentThread(vm);
|
||||
/* Detach the current thread from the JVM */
|
||||
if (env) {
|
||||
ret = (*env)->GetJavaVM(env, &vm);
|
||||
if (ret) {
|
||||
fprintf(stderr, "hdfsThreadDestructor: GetJavaVM failed with error %d\n",
|
||||
ret);
|
||||
(*env)->ExceptionDescribe(env);
|
||||
} else {
|
||||
(*vm)->DetachCurrentThread(vm);
|
||||
}
|
||||
}
|
||||
|
||||
/* Free exception strings */
|
||||
if (state->lastExceptionStackTrace) free(state->lastExceptionStackTrace);
|
||||
if (state->lastExceptionRootCause) free(state->lastExceptionRootCause);
|
||||
|
||||
/* Free the state itself */
|
||||
free(state);
|
||||
}
|
||||
|
||||
int threadLocalStorageGet(JNIEnv **env)
|
||||
struct ThreadLocalState* threadLocalStorageCreate()
|
||||
{
|
||||
struct ThreadLocalState *state;
|
||||
state = (struct ThreadLocalState*)malloc(sizeof(struct ThreadLocalState));
|
||||
if (state == NULL) {
|
||||
fprintf(stderr,
|
||||
"threadLocalStorageSet: OOM - Unable to allocate thread local state\n");
|
||||
return NULL;
|
||||
}
|
||||
state->lastExceptionStackTrace = NULL;
|
||||
state->lastExceptionRootCause = NULL;
|
||||
return state;
|
||||
}
|
||||
|
||||
int threadLocalStorageGet(struct ThreadLocalState **state)
|
||||
{
|
||||
int ret = 0;
|
||||
if (!gTlsKeyInitialized) {
|
||||
@ -63,18 +89,18 @@ int threadLocalStorageGet(JNIEnv **env)
|
||||
}
|
||||
gTlsKeyInitialized = 1;
|
||||
}
|
||||
*env = pthread_getspecific(gTlsKey);
|
||||
*state = pthread_getspecific(gTlsKey);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int threadLocalStorageSet(JNIEnv *env)
|
||||
int threadLocalStorageSet(struct ThreadLocalState *state)
|
||||
{
|
||||
int ret = pthread_setspecific(gTlsKey, env);
|
||||
int ret = pthread_setspecific(gTlsKey, state);
|
||||
if (ret) {
|
||||
fprintf(stderr,
|
||||
"threadLocalStorageSet: pthread_setspecific failed with error %d\n",
|
||||
ret);
|
||||
hdfsThreadDestructor(env);
|
||||
hdfsThreadDestructor(state);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -34,42 +34,67 @@
|
||||
* operating systems that support it.
|
||||
*/
|
||||
#ifdef HAVE_BETTER_TLS
|
||||
#define THREAD_LOCAL_STORAGE_GET_QUICK() \
|
||||
static __thread JNIEnv *quickTlsEnv = NULL; \
|
||||
#define THREAD_LOCAL_STORAGE_GET_QUICK(state) \
|
||||
static __thread struct ThreadLocalState *quickTlsEnv = NULL; \
|
||||
{ \
|
||||
if (quickTlsEnv) { \
|
||||
return quickTlsEnv; \
|
||||
*state = quickTlsEnv; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define THREAD_LOCAL_STORAGE_SET_QUICK(env) \
|
||||
#define THREAD_LOCAL_STORAGE_SET_QUICK(state) \
|
||||
{ \
|
||||
quickTlsEnv = (env); \
|
||||
quickTlsEnv = (state); \
|
||||
}
|
||||
#else
|
||||
#define THREAD_LOCAL_STORAGE_GET_QUICK()
|
||||
#define THREAD_LOCAL_STORAGE_SET_QUICK(env)
|
||||
#define THREAD_LOCAL_STORAGE_GET_QUICK(state)
|
||||
#define THREAD_LOCAL_STORAGE_SET_QUICK(state)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Gets the JNIEnv in thread-local storage for the current thread. If the call
|
||||
* succeeds, and there is a JNIEnv associated with this thread, then returns 0
|
||||
* and populates env. If the call succeeds, but there is no JNIEnv associated
|
||||
* with this thread, then returns 0 and sets JNIEnv to NULL. If the call fails,
|
||||
* then returns non-zero. Only one thread at a time may execute this function.
|
||||
* The caller is responsible for enforcing mutual exclusion.
|
||||
*
|
||||
* @param env JNIEnv out parameter
|
||||
* @return 0 if successful, non-zero otherwise
|
||||
*/
|
||||
int threadLocalStorageGet(JNIEnv **env);
|
||||
struct ThreadLocalState {
|
||||
/* The JNIEnv associated with the current thread */
|
||||
JNIEnv *env;
|
||||
/* The last exception stack trace that occured on this thread */
|
||||
char *lastExceptionStackTrace;
|
||||
/* The last exception root cause that occured on this thread */
|
||||
char *lastExceptionRootCause;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the JNIEnv in thread-local storage for the current thread.
|
||||
* The function that is called whenever a thread with libhdfs thread local data
|
||||
* is destroyed.
|
||||
*
|
||||
* @param env JNIEnv to set
|
||||
* @param v The thread-local data
|
||||
*/
|
||||
void hdfsThreadDestructor(void *v);
|
||||
|
||||
/**
|
||||
* Creates an object of ThreadLocalState.
|
||||
*
|
||||
* @return The newly created object if successful, NULL otherwise.
|
||||
*/
|
||||
struct ThreadLocalState* threadLocalStorageCreate();
|
||||
|
||||
/**
|
||||
* Gets the ThreadLocalState in thread-local storage for the current thread.
|
||||
* If the call succeeds, and there is a ThreadLocalState associated with this
|
||||
* thread, then returns 0 and populates 'state'. If the call succeeds, but
|
||||
* there is no ThreadLocalState associated with this thread, then returns 0
|
||||
* and sets ThreadLocalState to NULL. If the call fails, then returns non-zero.
|
||||
* Only one thread at a time may execute this function. The caller is
|
||||
* responsible for enforcing mutual exclusion.
|
||||
*
|
||||
* @param env ThreadLocalState out parameter
|
||||
* @return 0 if successful, non-zero otherwise
|
||||
*/
|
||||
int threadLocalStorageSet(JNIEnv *env);
|
||||
int threadLocalStorageGet(struct ThreadLocalState **state);
|
||||
|
||||
/**
|
||||
* Sets the ThreadLocalState in thread-local storage for the current thread.
|
||||
*
|
||||
* @param env ThreadLocalState to set
|
||||
* @return 0 if successful, non-zero otherwise
|
||||
*/
|
||||
int threadLocalStorageSet(struct ThreadLocalState *state);
|
||||
|
||||
#endif
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "os/thread_local_storage.h"
|
||||
|
||||
#include <jni.h>
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
|
||||
@ -27,16 +28,21 @@ static DWORD gTlsIndex = TLS_OUT_OF_INDEXES;
|
||||
|
||||
/**
|
||||
* If the current thread has a JNIEnv in thread-local storage, then detaches the
|
||||
* current thread from the JVM.
|
||||
* current thread from the JVM and also frees up the ThreadLocalState object.
|
||||
*/
|
||||
static void detachCurrentThreadFromJvm()
|
||||
{
|
||||
struct ThreadLocalState *state = NULL;
|
||||
JNIEnv *env = NULL;
|
||||
JavaVM *vm;
|
||||
jint ret;
|
||||
if (threadLocalStorageGet(&env) || !env) {
|
||||
if (threadLocalStorageGet(&state) || !state) {
|
||||
return;
|
||||
}
|
||||
if (!state->env) {
|
||||
return;
|
||||
}
|
||||
env = state->env;
|
||||
ret = (*env)->GetJavaVM(env, &vm);
|
||||
if (ret) {
|
||||
fprintf(stderr,
|
||||
@ -46,6 +52,13 @@ static void detachCurrentThreadFromJvm()
|
||||
} else {
|
||||
(*vm)->DetachCurrentThread(vm);
|
||||
}
|
||||
|
||||
/* Free exception strings */
|
||||
if (state->lastExceptionStackTrace) free(state->lastExceptionStackTrace);
|
||||
if (state->lastExceptionRootCause) free(state->lastExceptionRootCause);
|
||||
|
||||
/* Free the state itself */
|
||||
free(state);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -122,7 +135,21 @@ extern const PIMAGE_TLS_CALLBACK pTlsCallback;
|
||||
const PIMAGE_TLS_CALLBACK pTlsCallback = tlsCallback;
|
||||
#pragma const_seg()
|
||||
|
||||
int threadLocalStorageGet(JNIEnv **env)
|
||||
struct ThreadLocalState* threadLocalStorageCreate()
|
||||
{
|
||||
struct ThreadLocalState *state;
|
||||
state = (struct ThreadLocalState*)malloc(sizeof(struct ThreadLocalState));
|
||||
if (state == NULL) {
|
||||
fprintf(stderr,
|
||||
"threadLocalStorageSet: OOM - Unable to allocate thread local state\n");
|
||||
return NULL;
|
||||
}
|
||||
state->lastExceptionStackTrace = NULL;
|
||||
state->lastExceptionRootCause = NULL;
|
||||
return state;
|
||||
}
|
||||
|
||||
int threadLocalStorageGet(struct ThreadLocalState **state)
|
||||
{
|
||||
LPVOID tls;
|
||||
DWORD ret;
|
||||
@ -137,13 +164,13 @@ int threadLocalStorageGet(JNIEnv **env)
|
||||
}
|
||||
tls = TlsGetValue(gTlsIndex);
|
||||
if (tls) {
|
||||
*env = tls;
|
||||
*state = tls;
|
||||
return 0;
|
||||
} else {
|
||||
ret = GetLastError();
|
||||
if (ERROR_SUCCESS == ret) {
|
||||
/* Thread-local storage contains NULL, because we haven't set it yet. */
|
||||
*env = NULL;
|
||||
*state = NULL;
|
||||
return 0;
|
||||
} else {
|
||||
/*
|
||||
@ -158,15 +185,15 @@ int threadLocalStorageGet(JNIEnv **env)
|
||||
}
|
||||
}
|
||||
|
||||
int threadLocalStorageSet(JNIEnv *env)
|
||||
int threadLocalStorageSet(struct ThreadLocalState *state)
|
||||
{
|
||||
DWORD ret = 0;
|
||||
if (!TlsSetValue(gTlsIndex, (LPVOID)env)) {
|
||||
if (!TlsSetValue(gTlsIndex, (LPVOID)state)) {
|
||||
ret = GetLastError();
|
||||
fprintf(stderr,
|
||||
"threadLocalStorageSet: TlsSetValue failed with error %d\n",
|
||||
ret);
|
||||
detachCurrentThreadFromJvm(env);
|
||||
detachCurrentThreadFromJvm(state);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user