Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions src/native/aws_signing.c
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,8 @@ static void s_aws_request_signing_complete(struct aws_signing_result *result, in
struct s_aws_sign_request_callback_data *callback_data = userdata;

/********** JNI ENV ACQUIRE **********/
JNIEnv *env = aws_jni_acquire_thread_env(callback_data->jvm);
struct aws_jvm_env_context jvm_env_context = aws_jni_acquire_thread_env(callback_data->jvm);
JNIEnv *env = jvm_env_context.env;
if (env == NULL) {
/* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */
return;
Expand Down Expand Up @@ -235,7 +236,7 @@ done:;
JavaVM *jvm = callback_data->jvm;
s_cleanup_callback_data(callback_data, env);

aws_jni_release_thread_env(jvm, env);
aws_jni_release_thread_env(jvm, &jvm_env_context);
/********** JNI ENV RELEASE **********/
}

Expand All @@ -244,7 +245,8 @@ static void s_aws_chunk_like_signing_complete(struct aws_signing_result *result,
struct s_aws_sign_request_callback_data *callback_data = userdata;

/********** JNI ENV ACQUIRE **********/
JNIEnv *env = aws_jni_acquire_thread_env(callback_data->jvm);
struct aws_jvm_env_context jvm_env_context = aws_jni_acquire_thread_env(callback_data->jvm);
JNIEnv *env = jvm_env_context.env;
if (env == NULL) {
/* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */
return;
Expand All @@ -262,7 +264,7 @@ done:;
JavaVM *jvm = callback_data->jvm;
s_cleanup_callback_data(callback_data, env);

aws_jni_release_thread_env(jvm, env);
aws_jni_release_thread_env(jvm, &jvm_env_context);
/********** JNI ENV RELEASE **********/
}

Expand All @@ -278,7 +280,8 @@ static bool s_should_sign_header(const struct aws_byte_cursor *name, void *user_
struct aws_signing_config_data *callback_data = user_data;

/********** JNI ENV ACQUIRE **********/
JNIEnv *env = aws_jni_acquire_thread_env(callback_data->jvm);
struct aws_jvm_env_context jvm_env_context = aws_jni_acquire_thread_env(callback_data->jvm);
JNIEnv *env = jvm_env_context.env;
if (env == NULL) {
/* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */
return false;
Expand All @@ -292,7 +295,7 @@ static bool s_should_sign_header(const struct aws_byte_cursor *name, void *user_

(*env)->DeleteLocalRef(env, header_name);

aws_jni_release_thread_env(callback_data->jvm, env);
aws_jni_release_thread_env(callback_data->jvm, &jvm_env_context);
/********** JNI ENV RELEASE **********/

return result;
Expand Down
5 changes: 3 additions & 2 deletions src/native/client_bootstrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ static void s_client_bootstrap_shutdown_complete(void *user_data) {
struct shutdown_callback_data *callback_data = user_data;

/********** JNI ENV ACQUIRE **********/
JNIEnv *env = aws_jni_acquire_thread_env(callback_data->jvm);
struct aws_jvm_env_context jvm_env_context = aws_jni_acquire_thread_env(callback_data->jvm);
JNIEnv *env = jvm_env_context.env;
if (env == NULL) {
/* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */
return;
Expand All @@ -62,7 +63,7 @@ static void s_client_bootstrap_shutdown_complete(void *user_data) {

JavaVM *jvm = callback_data->jvm;
s_shutdown_callback_data_destroy(env, callback_data);
aws_jni_release_thread_env(jvm, env);
aws_jni_release_thread_env(jvm, &jvm_env_context);
/********** JNI ENV RELEASE **********/
}

Expand Down
28 changes: 15 additions & 13 deletions src/native/credentials_provider.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ static void s_on_shutdown_complete(void *user_data) {

// Tell the Java credentials providers that shutdown is done. This lets it release its references.
/********** JNI ENV ACQUIRE **********/
JNIEnv *env = aws_jni_acquire_thread_env(callback_data->jvm);
struct aws_jvm_env_context jvm_env_context = aws_jni_acquire_thread_env(callback_data->jvm);
JNIEnv *env = jvm_env_context.env;
if (env == NULL) {
/* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */
return;
Expand All @@ -89,8 +90,7 @@ static void s_on_shutdown_complete(void *user_data) {

JavaVM *jvm = callback_data->jvm;
s_callback_data_clean_up(env, allocator, callback_data);

aws_jni_release_thread_env(jvm, env);
aws_jni_release_thread_env(jvm, &jvm_env_context);
/********** JNI ENV RELEASE **********/
}

Expand Down Expand Up @@ -560,7 +560,8 @@ static int s_credentials_provider_delegate_get_credentials(
int return_value = AWS_OP_ERR;

/********** JNI ENV ACQUIRE **********/
JNIEnv *env = aws_jni_acquire_thread_env(callback_data->jvm);
struct aws_jvm_env_context jvm_env_context = aws_jni_acquire_thread_env(callback_data->jvm);
JNIEnv *env = jvm_env_context.env;
if (env == NULL) {
/* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */
return AWS_OP_ERR;
Expand Down Expand Up @@ -590,7 +591,7 @@ static int s_credentials_provider_delegate_get_credentials(
done:
(*env)->DeleteLocalRef(env, java_credentials);

aws_jni_release_thread_env(callback_data->jvm, env);
aws_jni_release_thread_env(callback_data->jvm, &jvm_env_context);
/********** JNI ENV RELEASE **********/

return return_value;
Expand Down Expand Up @@ -688,14 +689,14 @@ static void s_aws_login_token_source_data_on_zero_ref(void *user_data) {
JavaVM *jvm = login_token_source_data->jvm;

/********** JNI ENV ACQUIRE **********/
JNIEnv *env = aws_jni_acquire_thread_env(jvm);
struct aws_jvm_env_context jvm_env_context = aws_jni_acquire_thread_env(jvm);
JNIEnv *env = jvm_env_context.env;
if (env != NULL) {
if (login_token_source_data->login_token_source != NULL) {
(*env)->DeleteGlobalRef(env, login_token_source_data->login_token_source);
}
}

aws_jni_release_thread_env(jvm, env);
aws_jni_release_thread_env(jvm, &jvm_env_context);
/********** JNI ENV RELEASE **********/

aws_mem_release(login_token_source_data->allocator, login_token_source_data);
Expand Down Expand Up @@ -855,7 +856,8 @@ static int s_cognito_get_token_pairs(
JavaVM *jvm = login_token_source_data->jvm;

/********** JNI ENV ACQUIRE **********/
JNIEnv *env = aws_jni_acquire_thread_env(jvm);
struct aws_jvm_env_context jvm_env_context = aws_jni_acquire_thread_env(jvm);
JNIEnv *env = jvm_env_context.env;
if (env == NULL) {
return aws_raise_error(AWS_ERROR_JAVA_CRT_JVM_DESTROYED);
}
Expand Down Expand Up @@ -911,7 +913,7 @@ static int s_cognito_get_token_pairs(
s_aws_login_token_source_invocation_destroy(invocation, env);
}

aws_jni_release_thread_env(jvm, env);
aws_jni_release_thread_env(jvm, &jvm_env_context);
/********** JNI ENV RELEASE **********/

return result;
Expand Down Expand Up @@ -1103,7 +1105,8 @@ static void s_on_get_credentials_callback(struct aws_credentials *credentials, i
struct aws_credentials_provider_get_credentials_callback_data *callback_data = user_data;

/********** JNI ENV ACQUIRE **********/
JNIEnv *env = aws_jni_acquire_thread_env(callback_data->jvm);
struct aws_jvm_env_context jvm_env_context = aws_jni_acquire_thread_env(callback_data->jvm);
JNIEnv *env = jvm_env_context.env;
if (env == NULL) {
/* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */
return;
Expand Down Expand Up @@ -1131,8 +1134,7 @@ static void s_on_get_credentials_callback(struct aws_credentials *credentials, i

JavaVM *jvm = callback_data->jvm;
s_cp_callback_data_clean_up(callback_data, env);

aws_jni_release_thread_env(jvm, env);
aws_jni_release_thread_env(jvm, &jvm_env_context);
/********** JNI ENV RELEASE **********/
}

Expand Down
56 changes: 30 additions & 26 deletions src/native/crt.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,21 +69,18 @@ static void s_detach_jvm_from_thread(void *user_data) {

/* we don't need this JNIEnv, but this is an easy way to verify the JVM is still valid to use */
/********** JNI ENV ACQUIRE **********/
JNIEnv *env = aws_jni_acquire_thread_env(jvm);
if (env != NULL) {
struct aws_jvm_env_context jvm_env_context = aws_jni_acquire_thread_env(jvm);
if (jvm_env_context.env != NULL) {
(*jvm)->DetachCurrentThread(jvm);
aws_jni_release_thread_env(jvm, env);
aws_jni_release_thread_env(jvm, &jvm_env_context);
/********** JNI ENV RELEASE **********/
}
}

static JNIEnv *s_aws_jni_get_thread_env(JavaVM *jvm) {
#ifdef ANDROID
JNIEnv *env = NULL;
#else
void *env = NULL;
#endif
if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_6) == JNI_EDETACHED) {
static struct aws_jvm_env_context s_aws_jni_get_thread_env(JavaVM *jvm) {
struct aws_jvm_env_context jvm_env_context = {.env = NULL, .should_detach = false};

if ((*jvm)->GetEnv(jvm, (void **)&jvm_env_context.env, JNI_VERSION_1_6) == JNI_EDETACHED) {
if (!s_dispatch_queue_threads) {
AWS_LOGF_DEBUG(AWS_LS_COMMON_GENERAL, "s_aws_jni_get_thread_env returned detached, attaching");
}
Expand All @@ -106,9 +103,9 @@ static JNIEnv *s_aws_jni_get_thread_env(JavaVM *jvm) {
}

#ifdef ANDROID
jint result = (*jvm)->AttachCurrentThreadAsDaemon(jvm, &env, &attach_args);
jint result = (*jvm)->AttachCurrentThreadAsDaemon(jvm, &jvm_env_context.env, &attach_args);
#else
jint result = (*jvm)->AttachCurrentThreadAsDaemon(jvm, (void **)&env, &attach_args);
jint result = (*jvm)->AttachCurrentThreadAsDaemon(jvm, (void **)&jvm_env_context.env, &attach_args);
#endif

aws_string_destroy(thread_name);
Expand All @@ -117,9 +114,12 @@ static JNIEnv *s_aws_jni_get_thread_env(JavaVM *jvm) {
AWS_FATAL_ASSERT(result != JNI_ENOMEM);
if (result != JNI_OK) {
fprintf(stderr, "Unrecoverable AttachCurrentThreadAsDaemon failed, JNI error code is %d\n", (int)result);
return NULL;
jvm_env_context.env = NULL;
return jvm_env_context;
}

jvm_env_context.should_detach = true;

/*
Dispatch Queue threads are managed by Apple's GCD and thus can't have an at_exit callback assigned. We manually
detatch dispatch queue threads from the JVM during `aws_jni_release_thread_env()` to insure cleanup.
Expand All @@ -131,7 +131,7 @@ static JNIEnv *s_aws_jni_get_thread_env(JavaVM *jvm) {
}
}

return env;
return jvm_env_context;
}

/*
Expand Down Expand Up @@ -210,7 +210,12 @@ static void s_jvm_table_remove_jvm_for_env(JNIEnv *env) {
aws_rw_lock_wunlock(&s_jvm_table_lock);
}

JNIEnv *aws_jni_acquire_thread_env(JavaVM *jvm) {
struct aws_jvm_env_context aws_jni_acquire_thread_env(JavaVM *jvm) {
struct aws_jvm_env_context jvm_env_context = {
.env = NULL,
.should_detach = false,
};

/*
* We use try-lock here in order to avoid the re-entrant deadlock case that could happen if we have a read
* lock already, the JVM shutdown hooks causes another thread to block on taking the write lock, and then
Expand All @@ -221,7 +226,7 @@ JNIEnv *aws_jni_acquire_thread_env(JavaVM *jvm) {
if (aws_last_error() != AWS_ERROR_UNSUPPORTED_OPERATION) {
aws_raise_error(AWS_ERROR_JAVA_CRT_JVM_DESTROYED);
}
return NULL;
return jvm_env_context;
}

if (s_jvms == NULL) {
Expand All @@ -236,32 +241,31 @@ JNIEnv *aws_jni_acquire_thread_env(JavaVM *jvm) {
goto error;
}

JNIEnv *env = s_aws_jni_get_thread_env(jvm);
if (env == NULL) {
jvm_env_context = s_aws_jni_get_thread_env(jvm);
if (jvm_env_context.env == NULL) {
aws_raise_error(AWS_ERROR_JAVA_CRT_JVM_DESTROYED);
goto error;
}

return env;
return jvm_env_context;

error:

aws_rw_lock_runlock(&s_jvm_table_lock);

return NULL;
return jvm_env_context;
}

void aws_jni_release_thread_env(JavaVM *jvm, JNIEnv *env) {
(void)jvm;
(void)env;

if (env != NULL) {
void aws_jni_release_thread_env(JavaVM *jvm, struct aws_jvm_env_context *jvm_env_context) {
if (jvm_env_context->env != NULL) {
/*
Dispatch Queue threads must be manually detached after they're used instead of depending
on a thread exit callback due to the threads not being managed by the CRT and thus, their
lifetimes not trackable outside the context of their immediate use.
An additional needs_detach variable is needed to avoid detaching JVM threads. It happens
when a callback is executed on a JVM thread.
*/
if (s_dispatch_queue_threads) {
if (s_dispatch_queue_threads && jvm_env_context->should_detach) {
(*jvm)->DetachCurrentThread(jvm);
}

Expand Down
23 changes: 17 additions & 6 deletions src/native/crt.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ enum aws_java_crt_error {

struct aws_allocator *aws_jni_get_allocator(void);

/* This struct holds everything needed to interact with the JVM from native functions. */
struct aws_jvm_env_context {
JNIEnv *env;
/* Determines whether to detach non-CRT threads at the end of a callback. */
bool should_detach;
};

/*******************************************************************************
* aws_jni_throw_runtime_exception - throws a crt.CrtRuntimeException with the
* supplied message, sprintf formatted. Control WILL return from this function,
Expand Down Expand Up @@ -227,17 +234,21 @@ struct aws_string *aws_jni_new_string_from_jstring(JNIEnv *env, jstring str);

/*******************************************************************************
* aws_jni_acquire_thread_env - Acquires the JNIEnv for the current thread from the VM,
* attaching the env if necessary. aws_jni_release_thread_env() must be called once
* attaching the env if necessary. aws_jni_release_thread_env() must be called once
* the caller is through with the environment.
* The needs_detach field in the returned context will be set to true if the thread
* was not attached to JVM when this function was called, and false otherwise.
******************************************************************************/
JNIEnv *aws_jni_acquire_thread_env(JavaVM *jvm);
struct aws_jvm_env_context aws_jni_acquire_thread_env(JavaVM *jvm);

/*******************************************************************************
* aws_jni_release_thread_env - Releases an acquired JNIEnv for the current thread. Every successfully
* acquired JNIEnv must be released exactly once. Internally, all this does is release the reader
* lock on the set of valid JVMs.
* aws_jni_release_thread_env - Releases an acquired JNIEnv for the current thread. Every
* successfully acquired JNIEnv must be released exactly once.
* Internally, it
* - releases the reader lock on the set of valid JVMs
* - detaches the dispatch queue threads (on Apple platforms only) from JVM.
******************************************************************************/
void aws_jni_release_thread_env(JavaVM *jvm, JNIEnv *env);
void aws_jni_release_thread_env(JavaVM *jvm, struct aws_jvm_env_context *jvm_env_context);

/*******************************************************************************
* aws_jni_set_dispatch_queue_threads - Sets whether the current event loop group uses dispatch queue threads
Expand Down
10 changes: 6 additions & 4 deletions src/native/custom_key_op_handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ static void s_aws_custom_key_op_handler_perform_operation(
AWS_ASSERT(operation != NULL);

/* Get the Java ENV */
JNIEnv *env = aws_jni_acquire_thread_env(op_handler->jvm);
struct aws_jvm_env_context jvm_env_context = aws_jni_acquire_thread_env(op_handler->jvm);
JNIEnv *env = jvm_env_context.env;
if (env == NULL) {
/* JVM is likely shutting down. Do not crash but log error. */
AWS_LOGF_ERROR(
Expand Down Expand Up @@ -105,15 +106,16 @@ static void s_aws_custom_key_op_handler_perform_operation(
}

/* Release the Java ENV */
aws_jni_release_thread_env(op_handler->jvm, env);
aws_jni_release_thread_env(op_handler->jvm, &jvm_env_context);
}

static void s_aws_custom_key_op_handler_destroy(struct aws_custom_key_op_handler *key_op_handler) {

struct aws_jni_custom_key_op_handler *op_handler = (struct aws_jni_custom_key_op_handler *)key_op_handler->impl;

/* Get the Java ENV */
JNIEnv *env = aws_jni_acquire_thread_env(op_handler->jvm);
struct aws_jvm_env_context jvm_env_context = aws_jni_acquire_thread_env(op_handler->jvm);
JNIEnv *env = jvm_env_context.env;
if (env == NULL) {
/* JVM is likely shutting down. Do not crash but log error. */
AWS_LOGF_ERROR(
Expand All @@ -127,7 +129,7 @@ static void s_aws_custom_key_op_handler_destroy(struct aws_custom_key_op_handler
}

/* Release the Java ENV */
aws_jni_release_thread_env(op_handler->jvm, env);
aws_jni_release_thread_env(op_handler->jvm, &jvm_env_context);

/* Release the Java struct */
aws_mem_release(op_handler->allocator, op_handler);
Expand Down
Loading
Loading