HDFS-799. libhdfs must call DetachCurrentThread when a thread is destroyed. Contributed by Colin Patrick McCabe
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/branches/branch-2@1361009 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
c1c388bbb6
commit
1a76841335
|
@ -131,6 +131,9 @@ Release 2.0.1-alpha - UNRELEASED
|
||||||
HDFS-3633. libhdfs: hdfsDelete should pass JNI_FALSE or JNI_TRUE.
|
HDFS-3633. libhdfs: hdfsDelete should pass JNI_FALSE or JNI_TRUE.
|
||||||
(Colin Patrick McCabe via eli)
|
(Colin Patrick McCabe via eli)
|
||||||
|
|
||||||
|
HDFS-799. libhdfs must call DetachCurrentThread when a thread is destroyed.
|
||||||
|
(Colin Patrick McCabe via eli)
|
||||||
|
|
||||||
OPTIMIZATIONS
|
OPTIMIZATIONS
|
||||||
|
|
||||||
HDFS-2982. Startup performance suffers when there are many edit log
|
HDFS-2982. Startup performance suffers when there are many edit log
|
||||||
|
|
|
@ -67,6 +67,12 @@ function(FLATTEN_LIST INPUT SEPARATOR OUTPUT)
|
||||||
set (${OUTPUT} "${_TMPS}" PARENT_SCOPE)
|
set (${OUTPUT} "${_TMPS}" PARENT_SCOPE)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
# Check to see if our compiler and linker support the __thread attribute.
|
||||||
|
# On Linux and some other operating systems, this is a more efficient
|
||||||
|
# alternative to POSIX thread local storage.
|
||||||
|
INCLUDE(CheckCSourceCompiles)
|
||||||
|
CHECK_C_SOURCE_COMPILES("int main(void) { static __thread int i = 0; return 0; }" HAVE_BETTER_TLS)
|
||||||
|
|
||||||
find_package(JNI REQUIRED)
|
find_package(JNI REQUIRED)
|
||||||
if (NOT GENERATED_JAVAH)
|
if (NOT GENERATED_JAVAH)
|
||||||
# Must identify where the generated headers have been placed
|
# Must identify where the generated headers have been placed
|
||||||
|
|
|
@ -3,4 +3,6 @@
|
||||||
|
|
||||||
#cmakedefine _FUSE_DFS_VERSION "@_FUSE_DFS_VERSION@"
|
#cmakedefine _FUSE_DFS_VERSION "@_FUSE_DFS_VERSION@"
|
||||||
|
|
||||||
|
#cmakedefine HAVE_BETTER_TLS
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -15,17 +15,19 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
#include <string.h>
|
|
||||||
|
#include "config.h"
|
||||||
#include "hdfsJniHelper.h"
|
#include "hdfsJniHelper.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
static pthread_mutex_t hdfsHashMutex = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t hdfsHashMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
static pthread_mutex_t jvmMutex = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t jvmMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
static volatile int hashTableInited = 0;
|
static volatile int hashTableInited = 0;
|
||||||
|
|
||||||
#define LOCK_HASH_TABLE() pthread_mutex_lock(&hdfsHashMutex)
|
#define LOCK_HASH_TABLE() pthread_mutex_lock(&hdfsHashMutex)
|
||||||
#define UNLOCK_HASH_TABLE() pthread_mutex_unlock(&hdfsHashMutex)
|
#define UNLOCK_HASH_TABLE() pthread_mutex_unlock(&hdfsHashMutex)
|
||||||
#define LOCK_JVM_MUTEX() pthread_mutex_lock(&jvmMutex)
|
|
||||||
#define UNLOCK_JVM_MUTEX() pthread_mutex_unlock(&jvmMutex)
|
|
||||||
|
|
||||||
|
|
||||||
/** The Native return types that methods could return */
|
/** The Native return types that methods could return */
|
||||||
|
@ -48,6 +50,43 @@ static volatile int hashTableInited = 0;
|
||||||
*/
|
*/
|
||||||
#define MAX_HASH_TABLE_ELEM 4096
|
#define MAX_HASH_TABLE_ELEM 4096
|
||||||
|
|
||||||
|
/** Key that allows us to retrieve thread-local storage */
|
||||||
|
static pthread_key_t gTlsKey;
|
||||||
|
|
||||||
|
/** nonzero if we succeeded in initializing gTlsKey. Protected by the jvmMutex */
|
||||||
|
static int gTlsKeyInitialized = 0;
|
||||||
|
|
||||||
|
/** Pthreads thread-local storage for each library thread. */
|
||||||
|
struct hdfsTls {
|
||||||
|
JNIEnv *env;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The function that is called whenever a thread with libhdfs thread local data
|
||||||
|
* is destroyed.
|
||||||
|
*
|
||||||
|
* @param v The thread-local data
|
||||||
|
*/
|
||||||
|
static void hdfsThreadDestructor(void *v)
|
||||||
|
{
|
||||||
|
struct hdfsTls *tls = v;
|
||||||
|
JavaVM *vm;
|
||||||
|
JNIEnv *env = tls->env;
|
||||||
|
jint ret;
|
||||||
|
|
||||||
|
if (!tls)
|
||||||
|
return;
|
||||||
|
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(tls);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int validateMethodType(MethType methType)
|
static int validateMethodType(MethType methType)
|
||||||
{
|
{
|
||||||
|
@ -375,34 +414,26 @@ char *classNameOfObject(jobject jobj, JNIEnv *env) {
|
||||||
return newstr;
|
return newstr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* getJNIEnv: A helper function to get the JNIEnv* for the given thread.
|
* Get the global JNI environemnt.
|
||||||
* If no JVM exists, then one will be created. JVM command line arguments
|
|
||||||
* are obtained from the LIBHDFS_OPTS environment variable.
|
|
||||||
*
|
*
|
||||||
* @param: None.
|
* We only have to create the JVM once. After that, we can use it in
|
||||||
* @return The JNIEnv* corresponding to the thread.
|
* every thread. You must be holding the jvmMutex when you call this
|
||||||
|
* function.
|
||||||
|
*
|
||||||
|
* @return The JNIEnv on success; error code otherwise
|
||||||
*/
|
*/
|
||||||
JNIEnv* getJNIEnv(void)
|
static JNIEnv* getGlobalJNIEnv(void)
|
||||||
{
|
{
|
||||||
|
|
||||||
const jsize vmBufLength = 1;
|
const jsize vmBufLength = 1;
|
||||||
JavaVM* vmBuf[vmBufLength];
|
JavaVM* vmBuf[vmBufLength];
|
||||||
JNIEnv *env;
|
JNIEnv *env;
|
||||||
jint rv = 0;
|
jint rv = 0;
|
||||||
jint noVMs = 0;
|
jint noVMs = 0;
|
||||||
|
|
||||||
// Only the first thread should create the JVM. The other trheads should
|
|
||||||
// just use the JVM created by the first thread.
|
|
||||||
LOCK_JVM_MUTEX();
|
|
||||||
|
|
||||||
rv = JNI_GetCreatedJavaVMs(&(vmBuf[0]), vmBufLength, &noVMs);
|
rv = JNI_GetCreatedJavaVMs(&(vmBuf[0]), vmBufLength, &noVMs);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
fprintf(stderr, "JNI_GetCreatedJavaVMs failed with error: %d\n", rv);
|
fprintf(stderr, "JNI_GetCreatedJavaVMs failed with error: %d\n", rv);
|
||||||
UNLOCK_JVM_MUTEX();
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,7 +442,6 @@ JNIEnv* getJNIEnv(void)
|
||||||
char *hadoopClassPath = getenv("CLASSPATH");
|
char *hadoopClassPath = getenv("CLASSPATH");
|
||||||
if (hadoopClassPath == NULL) {
|
if (hadoopClassPath == NULL) {
|
||||||
fprintf(stderr, "Environment variable CLASSPATH not set!\n");
|
fprintf(stderr, "Environment variable CLASSPATH not set!\n");
|
||||||
UNLOCK_JVM_MUTEX();
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
char *hadoopClassPathVMArg = "-Djava.class.path=";
|
char *hadoopClassPathVMArg = "-Djava.class.path=";
|
||||||
|
@ -470,7 +500,6 @@ JNIEnv* getJNIEnv(void)
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
fprintf(stderr, "Call to JNI_CreateJavaVM failed "
|
fprintf(stderr, "Call to JNI_CreateJavaVM failed "
|
||||||
"with error: %d\n", rv);
|
"with error: %d\n", rv);
|
||||||
UNLOCK_JVM_MUTEX();
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -481,11 +510,82 @@ JNIEnv* getJNIEnv(void)
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
fprintf(stderr, "Call to AttachCurrentThread "
|
fprintf(stderr, "Call to AttachCurrentThread "
|
||||||
"failed with error: %d\n", rv);
|
"failed with error: %d\n", rv);
|
||||||
UNLOCK_JVM_MUTEX();
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UNLOCK_JVM_MUTEX();
|
|
||||||
|
|
||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getJNIEnv: A helper function to get the JNIEnv* for the given thread.
|
||||||
|
* If no JVM exists, then one will be created. JVM command line arguments
|
||||||
|
* are obtained from the LIBHDFS_OPTS environment variable.
|
||||||
|
*
|
||||||
|
* Implementation note: we rely on POSIX thread-local storage (tls).
|
||||||
|
* This allows us to associate a destructor function with each thread, that
|
||||||
|
* will detach the thread from the Java VM when the thread terminates. If we
|
||||||
|
* failt to do this, it will cause a memory leak.
|
||||||
|
*
|
||||||
|
* However, POSIX TLS is not the most efficient way to do things. It requires a
|
||||||
|
* key to be initialized before it can be used. Since we don't know if this key
|
||||||
|
* is initialized at the start of this function, we have to lock a mutex first
|
||||||
|
* and check. Luckily, most operating systems support the more efficient
|
||||||
|
* __thread construct, which is initialized by the linker.
|
||||||
|
*
|
||||||
|
* @param: None.
|
||||||
|
* @return The JNIEnv* corresponding to the thread.
|
||||||
|
*/
|
||||||
|
JNIEnv* getJNIEnv(void)
|
||||||
|
{
|
||||||
|
JNIEnv *env;
|
||||||
|
struct hdfsTls *tls;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
#ifdef HAVE_BETTER_TLS
|
||||||
|
static __thread struct hdfsTls *quickTls = NULL;
|
||||||
|
if (quickTls)
|
||||||
|
return quickTls->env;
|
||||||
|
#endif
|
||||||
|
pthread_mutex_lock(&jvmMutex);
|
||||||
|
if (!gTlsKeyInitialized) {
|
||||||
|
ret = pthread_key_create(&gTlsKey, hdfsThreadDestructor);
|
||||||
|
if (ret) {
|
||||||
|
pthread_mutex_unlock(&jvmMutex);
|
||||||
|
fprintf("pthread_key_create failed with error %d\n", ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
gTlsKeyInitialized = 1;
|
||||||
|
}
|
||||||
|
tls = pthread_getspecific(gTlsKey);
|
||||||
|
if (tls) {
|
||||||
|
pthread_mutex_unlock(&jvmMutex);
|
||||||
|
return tls->env;
|
||||||
|
}
|
||||||
|
|
||||||
|
env = getGlobalJNIEnv();
|
||||||
|
pthread_mutex_unlock(&jvmMutex);
|
||||||
|
if (!env) {
|
||||||
|
fprintf(stderr, "getJNIEnv: getGlobalJNIEnv failed\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
tls = calloc(1, sizeof(struct hdfsTls));
|
||||||
|
if (!tls) {
|
||||||
|
fprintf(stderr, "getJNIEnv: OOM allocating %d bytes\n",
|
||||||
|
sizeof(struct hdfsTls));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
tls->env = env;
|
||||||
|
ret = pthread_setspecific(gTlsKey, tls);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "getJNIEnv: pthread_setspecific failed with "
|
||||||
|
"error code %d\n", ret);
|
||||||
|
hdfsThreadDestructor(tls);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#ifdef HAVE_BETTER_TLS
|
||||||
|
quickTls = tls;
|
||||||
|
#endif
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue