blob: e9cec38f2ecf57c453778ff239ed7b4812bb7b8d [file] [log] [blame] [edit]
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/auth/auth.h>
#include <aws/common/allocator.h>
#include <aws/common/atomics.h>
#include <aws/common/clock.h>
#include <aws/common/common.h>
#include <aws/common/hash_table.h>
#include <aws/common/logging.h>
#include <aws/common/rw_lock.h>
#include <aws/common/string.h>
#include <aws/common/system_info.h>
#include <aws/common/thread.h>
#include <aws/event-stream/event_stream.h>
#include <aws/http/connection.h>
#include <aws/http/http.h>
#include <aws/io/channel.h>
#include <aws/io/io.h>
#include <aws/io/logging.h>
#include <aws/io/tls_channel_handler.h>
#include <aws/mqtt/mqtt.h>
#include <aws/s3/s3.h>
#include <stdio.h>
#include "crt.h"
#include "java_class_ids.h"
#include "logging.h"
/* 0 = off, 1 = bytes, 2 = stack traces, see aws_mem_trace_level */
int g_memory_tracing = 0;
static struct aws_allocator *s_init_allocator(void) {
if (g_memory_tracing) {
struct aws_allocator *allocator = aws_default_allocator();
allocator = aws_mem_tracer_new(allocator, NULL, (enum aws_mem_trace_level)g_memory_tracing, 8);
return allocator;
}
return aws_default_allocator();
}
static struct aws_allocator *s_allocator = NULL;
struct aws_allocator *aws_jni_get_allocator(void) {
if (AWS_UNLIKELY(s_allocator == NULL)) {
s_allocator = s_init_allocator();
}
return s_allocator;
}
static void s_detach_jvm_from_thread(void *user_data) {
AWS_LOGF_DEBUG(AWS_LS_COMMON_GENERAL, "s_detach_jvm_from_thread invoked");
JavaVM *jvm = 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) {
(*jvm)->DetachCurrentThread(jvm);
aws_jni_release_thread_env(jvm, env);
/********** 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) {
AWS_LOGF_DEBUG(AWS_LS_COMMON_GENERAL, "s_aws_jni_get_thread_env returned detached, attaching");
struct aws_string *thread_name = NULL;
if (aws_thread_current_name(aws_jni_get_allocator(), &thread_name)) {
/* Retrieving thread name can fail for multitude of reasons and is
not fatal. Ignore the error and continue. */
AWS_LOGF_DEBUG(AWS_LS_COMMON_GENERAL, "Failed to retrieve name for the thread.");
}
struct JavaVMAttachArgs attach_args = {
.version = JNI_VERSION_1_6,
.name = NULL,
.group = NULL,
};
if (thread_name != NULL) {
attach_args.name = (char *)aws_string_c_str(thread_name);
}
#ifdef ANDROID
jint result = (*jvm)->AttachCurrentThreadAsDaemon(jvm, &env, &attach_args);
#else
jint result = (*jvm)->AttachCurrentThreadAsDaemon(jvm, (void **)&env, &attach_args);
#endif
aws_string_destroy(thread_name);
/* Ran out of memory, don't log in this case */
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;
}
/* This should only happen in event loop threads, the JVM main thread attachment is
* managed by the JVM, so we only need to clean up event loop thread attachments */
AWS_FATAL_ASSERT(AWS_OP_SUCCESS == aws_thread_current_at_exit(s_detach_jvm_from_thread, (void *)jvm));
}
return env;
}
/*
A simple system to support unpredictable JVM shutdowns. In an ideal world, everyone would correctly use the
CrtResource ref counting and strict (aws_thread_managed_join_all) shutdown, but given the difficulty of using
them correctly, that's not a realistic expectation. So we need to come up with a way for JVM shutdowns to
not trigger crashes from native threads that try and call back to Java (where even the JavaVM pointer cached
on the binding object is now garbage) after the JVM has shutdown (but before the process has killed all of its
threads).
Our system works as follows:
We track the set of all active JVMs (since we don't correctly support multiple JVMs yet, this is always going to be
either one or zero for now). We protect this set with a read-write lock. Adding (CRT init) or removing (JVM
shutdown hook) a JVM from this set will take a write lock. Acquiring a JNIEnv from a tracked JVM, will take a read
lock, and releasing a JNIEnv will release the read lock.
Acquiring a JNIEnv succeeds if the JVM in question is in our set, and fails otherwise. All users of a JNIEnv have
been hardened to check for null and just not call to Java in that case.
Since we don't have RAII in C, bindings must be very careful to release once, and exactly once, every JNIEnv that
they acquire. An alternative approach would be to replace all of the JNIEnv usage with a new API that
takes the lock, calls a supplied callback (which does all the JNIEnv operations), and then releases the lock. This
approach was tried but was so disruptive refactor-wise that I deemed it too dangerous to try and push through. So
instead, we just have to be careful with acquire/release.
In this way, the vast majority of usage is relatively contentionless; it's just a bunch of native threads taking
read locks on a shared rw lock. Only when the JVM shutdown hook calls into native is there read-write contention.
*/
static struct aws_rw_lock s_jvm_table_lock = AWS_RW_LOCK_INIT;
static struct aws_hash_table *s_jvms = NULL;
static void s_jvm_table_add_jvm_for_env(JNIEnv *env) {
aws_rw_lock_wlock(&s_jvm_table_lock);
if (s_jvms == NULL) {
/* use default allocator so that tracing allocator doesn't flag this as a leak during tests */
s_jvms = aws_mem_calloc(aws_default_allocator(), 1, sizeof(struct aws_hash_table));
AWS_FATAL_ASSERT(
AWS_OP_SUCCESS ==
aws_hash_table_init(s_jvms, aws_default_allocator(), 1, aws_hash_ptr, aws_ptr_eq, NULL, NULL));
}
JavaVM *jvm = NULL;
jint jvmresult = (*env)->GetJavaVM(env, &jvm);
AWS_FATAL_ASSERT(jvmresult == 0 && jvm != NULL);
int was_created = 0;
AWS_FATAL_ASSERT(AWS_OP_SUCCESS == aws_hash_table_put(s_jvms, jvm, NULL, &was_created));
AWS_FATAL_ASSERT(was_created == 1);
aws_rw_lock_wunlock(&s_jvm_table_lock);
}
static void s_jvm_table_remove_jvm_for_env(JNIEnv *env) {
aws_rw_lock_wlock(&s_jvm_table_lock);
if (s_jvms == NULL) {
goto done;
}
JavaVM *jvm = NULL;
jint jvmresult = (*env)->GetJavaVM(env, &jvm);
AWS_FATAL_ASSERT(jvmresult == 0 && jvm != NULL);
AWS_FATAL_ASSERT(AWS_OP_SUCCESS == aws_hash_table_remove(s_jvms, jvm, NULL, NULL));
if (aws_hash_table_get_entry_count(s_jvms) == 0) {
aws_hash_table_clean_up(s_jvms);
aws_mem_release(aws_default_allocator(), s_jvms);
s_jvms = NULL;
}
done:
aws_rw_lock_wunlock(&s_jvm_table_lock);
}
JNIEnv *aws_jni_acquire_thread_env(JavaVM *jvm) {
/*
* 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
* we try to reacquire the read-lock recursively due to some synchronous code path. That case can deadlock
* but since the JVM is going away, it's safe to just fail completely from here on out.
*/
if (aws_rw_lock_try_rlock(&s_jvm_table_lock)) {
if (aws_last_error() != AWS_ERROR_UNSUPPORTED_OPERATION) {
aws_raise_error(AWS_ERROR_JAVA_CRT_JVM_DESTROYED);
}
return NULL;
}
if (s_jvms == NULL) {
aws_raise_error(AWS_ERROR_JAVA_CRT_JVM_DESTROYED);
goto error;
}
struct aws_hash_element *element = NULL;
int find_result = aws_hash_table_find(s_jvms, jvm, &element);
if (find_result != AWS_OP_SUCCESS || element == NULL) {
aws_raise_error(AWS_ERROR_JAVA_CRT_JVM_DESTROYED);
goto error;
}
JNIEnv *env = s_aws_jni_get_thread_env(jvm);
if (env == NULL) {
aws_raise_error(AWS_ERROR_JAVA_CRT_JVM_DESTROYED);
goto error;
}
return env;
error:
aws_rw_lock_runlock(&s_jvm_table_lock);
return NULL;
}
void aws_jni_release_thread_env(JavaVM *jvm, JNIEnv *env) {
(void)jvm;
(void)env;
if (env != NULL) {
aws_rw_lock_runlock(&s_jvm_table_lock);
}
}
void aws_jni_throw_runtime_exception(JNIEnv *env, const char *msg, ...) {
va_list args;
va_start(args, msg);
char buf[1024];
vsnprintf(buf, sizeof(buf), msg, args);
va_end(args);
int error = aws_last_error();
char exception[1280];
snprintf(
exception,
sizeof(exception),
"%s (aws_last_error: %s(%d), %s)",
buf,
aws_error_name(error),
error,
aws_error_str(error));
jclass runtime_exception = crt_runtime_exception_properties.crt_runtime_exception_class;
(*env)->ThrowNew(env, runtime_exception, exception);
}
void aws_jni_throw_null_pointer_exception(JNIEnv *env, const char *msg, ...) {
va_list args;
va_start(args, msg);
char buf[1024];
vsnprintf(buf, sizeof(buf), msg, args);
va_end(args);
(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/NullPointerException"), buf);
}
void aws_jni_throw_illegal_argument_exception(JNIEnv *env, const char *msg, ...) {
va_list args;
va_start(args, msg);
char buf[1024];
vsnprintf(buf, sizeof(buf), msg, args);
va_end(args);
(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/IllegalArgumentException"), buf);
}
bool aws_jni_check_and_clear_exception(JNIEnv *env) {
bool exception_pending = (*env)->ExceptionCheck(env);
if (exception_pending) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
}
return exception_pending;
}
bool aws_jni_get_and_clear_exception(JNIEnv *env, jthrowable *out) {
bool exception_pending = (*env)->ExceptionCheck(env);
if (exception_pending) {
(*env)->DeleteGlobalRef(env, *out);
*out = (jthrowable)(*env)->NewGlobalRef(env, (*env)->ExceptionOccurred(env));
(*env)->ExceptionClear(env);
}
return exception_pending;
}
int aws_size_t_from_java(JNIEnv *env, size_t *out_size, jlong java_long, const char *errmsg_prefix) {
if (java_long < 0) {
aws_jni_throw_illegal_argument_exception(env, "%s cannot be negative", errmsg_prefix);
goto error;
}
if ((uint64_t)java_long > (uint64_t)SIZE_MAX) {
aws_jni_throw_illegal_argument_exception(
env, "%s cannot exceed %zu when running 32bit", errmsg_prefix, SIZE_MAX);
goto error;
}
*out_size = (size_t)java_long;
return AWS_OP_SUCCESS;
error:
*out_size = 0;
return AWS_OP_ERR;
}
jbyteArray aws_java_byte_array_new(JNIEnv *env, size_t size) {
jbyteArray jArray = (*env)->NewByteArray(env, (jsize)size);
return jArray;
}
bool aws_copy_native_array_to_java_byte_array(JNIEnv *env, jbyteArray dst, uint8_t *src, size_t amount) {
(*env)->SetByteArrayRegion(env, dst, 0, (jsize)amount, (jbyte *)src);
return aws_jni_check_and_clear_exception(env);
}
/**
* Converts a Native aws_byte_cursor to a Java byte[]
*/
jbyteArray aws_jni_byte_array_from_cursor(JNIEnv *env, const struct aws_byte_cursor *native_data) {
jbyteArray jArray = aws_java_byte_array_new(env, native_data->len);
if (jArray) {
if (!aws_copy_native_array_to_java_byte_array(env, jArray, native_data->ptr, native_data->len)) {
return jArray;
}
}
return NULL;
}
/**
* Get the Buffer Position (the next element to read/write)
*/
int aws_jni_byte_buffer_get_position(JNIEnv *env, jobject java_byte_buffer) {
jint position = (*env)->CallIntMethod(env, java_byte_buffer, byte_buffer_properties.get_position);
return (aws_jni_check_and_clear_exception(env)) ? -1 : (int)position;
}
/**
* Set the Buffer Position (the next element to read/write)
*/
void aws_jni_byte_buffer_set_position(JNIEnv *env, jobject jByteBuf, jint position) {
jobject val = (*env)->CallObjectMethod(env, jByteBuf, byte_buffer_properties.set_position, position);
AWS_FATAL_ASSERT(!aws_jni_check_and_clear_exception(env));
(*env)->DeleteLocalRef(env, val);
}
/**
* Set the Buffer Limit (the max allowed element to read/write)
*/
void aws_jni_byte_buffer_set_limit(JNIEnv *env, jobject jByteBuf, jint limit) {
jobject val = (*env)->CallObjectMethod(env, jByteBuf, byte_buffer_properties.set_limit, limit);
AWS_FATAL_ASSERT(!aws_jni_check_and_clear_exception(env));
(*env)->DeleteLocalRef(env, val);
}
jobject aws_jni_direct_byte_buffer_from_raw_ptr(JNIEnv *env, const void *dst, size_t capacity) {
jobject jByteBuf = (*env)->NewDirectByteBuffer(env, (void *)dst, (jlong)capacity);
if (jByteBuf) {
aws_jni_byte_buffer_set_limit(env, jByteBuf, (jint)capacity);
aws_jni_byte_buffer_set_position(env, jByteBuf, 0);
}
return jByteBuf;
}
struct aws_byte_cursor aws_jni_byte_cursor_from_jstring_acquire(JNIEnv *env, jstring str) {
if (str == NULL) {
aws_jni_throw_null_pointer_exception(env, "string is null");
return aws_byte_cursor_from_array(NULL, 0);
}
const char *bytes = (*env)->GetStringUTFChars(env, str, NULL);
if (bytes == NULL) {
/* GetStringUTFChars() has thrown exception */
return aws_byte_cursor_from_array(NULL, 0);
}
return aws_byte_cursor_from_array(bytes, (size_t)(*env)->GetStringUTFLength(env, str));
}
void aws_jni_byte_cursor_from_jstring_release(JNIEnv *env, jstring str, struct aws_byte_cursor cur) {
if (cur.ptr != NULL) {
(*env)->ReleaseStringUTFChars(env, str, (const char *)cur.ptr);
}
}
struct aws_byte_cursor aws_jni_byte_cursor_from_jbyteArray_acquire(JNIEnv *env, jbyteArray array) {
if (array == NULL) {
aws_jni_throw_null_pointer_exception(env, "byte[] is null");
return aws_byte_cursor_from_array(NULL, 0);
}
jbyte *bytes = (*env)->GetByteArrayElements(env, array, NULL);
if (bytes == NULL) {
/* GetByteArrayElements() has thrown exception */
return aws_byte_cursor_from_array(NULL, 0);
}
size_t len = (*env)->GetArrayLength(env, array);
return aws_byte_cursor_from_array(bytes, len);
}
void aws_jni_byte_cursor_from_jbyteArray_release(JNIEnv *env, jbyteArray array, struct aws_byte_cursor cur) {
if (cur.ptr != NULL) {
(*env)->ReleaseByteArrayElements(env, array, (jbyte *)cur.ptr, JNI_ABORT);
}
}
struct aws_byte_cursor aws_jni_byte_cursor_from_direct_byte_buffer(JNIEnv *env, jobject byte_buffer) {
jlong payload_size = (*env)->GetDirectBufferCapacity(env, byte_buffer);
if (payload_size == -1) {
aws_jni_throw_runtime_exception(
env, "MqttClientConnection.mqtt_publish: Unable to get capacity of payload ByteBuffer");
return aws_byte_cursor_from_array(NULL, 0);
}
jbyte *payload_data = (*env)->GetDirectBufferAddress(env, byte_buffer);
if (!payload_data) {
aws_jni_throw_runtime_exception(
env, "MqttClientConnection.mqtt_publish: Unable to get buffer from payload ByteBuffer");
return aws_byte_cursor_from_array(NULL, 0);
}
return aws_byte_cursor_from_array((const uint8_t *)payload_data, (size_t)payload_size);
}
struct aws_string *aws_jni_new_string_from_jstring(JNIEnv *env, jstring str) {
struct aws_allocator *allocator = aws_jni_get_allocator();
const char *str_chars = (*env)->GetStringUTFChars(env, str, NULL);
if (!str_chars) {
aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
return NULL;
}
struct aws_string *result = aws_string_new_from_c_str(allocator, str_chars);
(*env)->ReleaseStringUTFChars(env, str, str_chars);
return result;
}
jobject aws_jni_new_crt_exception_from_error_code(JNIEnv *env, int error_code) {
jint jni_error_code = error_code;
jobject crt_exception = (*env)->NewObject(
env,
crt_runtime_exception_properties.crt_runtime_exception_class,
crt_runtime_exception_properties.constructor_method_id,
jni_error_code);
AWS_FATAL_ASSERT(crt_exception);
return crt_exception;
}
#define AWS_DEFINE_ERROR_INFO_CRT(CODE, STR) AWS_DEFINE_ERROR_INFO(CODE, STR, "aws-crt-java")
/* clang-format off */
static struct aws_error_info s_crt_errors[] = {
AWS_DEFINE_ERROR_INFO_CRT(
AWS_ERROR_JAVA_CRT_JVM_DESTROYED,
"Attempt to use a JVM that has already been destroyed"),
AWS_DEFINE_ERROR_INFO_CRT(
AWS_ERROR_JAVA_CRT_JVM_OUT_OF_MEMORY,
"OutOfMemoryError has been raised from JVM."),
};
/* clang-format on */
static struct aws_error_info_list s_crt_error_list = {
.error_list = s_crt_errors,
.count = sizeof(s_crt_errors) / sizeof(struct aws_error_info),
};
static struct aws_log_subject_info s_crt_log_subject_infos[] = {
/* clang-format off */
DEFINE_LOG_SUBJECT_INFO(
AWS_LS_JAVA_CRT_GENERAL, "JavaCrtGeneral", "Subject for aws-crt-java logging that defies categorization"),
DEFINE_LOG_SUBJECT_INFO(
AWS_LS_JAVA_CRT_RESOURCE, "JavaCrtResource", "Subject for CrtResource"),
DEFINE_LOG_SUBJECT_INFO(
AWS_LS_JAVA_CRT_S3, "JavaCrtS3", "Subject for the layer binding aws-c-s3 to Java"),
DEFINE_LOG_SUBJECT_INFO(
AWS_LS_JAVA_ANDROID_KEYCHAIN, "android-keychain", "Subject for Android KeyChain"),
/* clang-format on */
};
static struct aws_log_subject_info_list s_crt_log_subject_list = {
.subject_list = s_crt_log_subject_infos,
.count = AWS_ARRAY_SIZE(s_crt_log_subject_infos),
};
static void s_jni_atexit_strict(void) {
aws_unregister_log_subject_info_list(&s_crt_log_subject_list);
aws_unregister_error_info(&s_crt_error_list);
aws_s3_library_clean_up();
aws_event_stream_library_clean_up();
aws_auth_library_clean_up();
aws_http_library_clean_up();
aws_mqtt_library_clean_up();
if (g_memory_tracing) {
struct aws_allocator *tracer_allocator = aws_jni_get_allocator();
aws_mem_tracer_dump(tracer_allocator);
}
aws_jni_cleanup_logging();
if (g_memory_tracing) {
struct aws_allocator *tracer_allocator = aws_jni_get_allocator();
aws_mem_tracer_destroy(tracer_allocator);
}
s_allocator = NULL;
}
#define DEFAULT_MANAGED_SHUTDOWN_WAIT_IN_SECONDS 1
static void s_jni_atexit_gentle(void) {
/* If not doing strict shutdown, wait only a short time before shutting down */
aws_thread_set_managed_join_timeout_ns(
aws_timestamp_convert(DEFAULT_MANAGED_SHUTDOWN_WAIT_IN_SECONDS, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL));
if (aws_thread_join_all_managed() == AWS_OP_SUCCESS) {
/* a successful managed join means it should be safe to do a full, strict clean up */
s_jni_atexit_strict();
} else {
/*
* We didn't successfully join all our threads so it's not really safe to clean up the libraries.
* Just dump memory if applicable and exit.
*/
AWS_LOGF_WARN(
AWS_LS_JAVA_CRT_GENERAL,
"Not all native threads were successfully joined during gentle shutdown. Memory may be leaked.");
if (g_memory_tracing) {
AWS_LOGF_DEBUG(
AWS_LS_JAVA_CRT_GENERAL,
"At shutdown, %u bytes remaining",
(uint32_t)aws_mem_tracer_bytes(aws_jni_get_allocator()));
if (g_memory_tracing > 1) {
aws_mem_tracer_dump(aws_jni_get_allocator());
}
}
}
}
#define KB_256 (256 * 1024)
/* Called as the entry point, immediately after the shared lib is loaded the first time by JNI */
JNIEXPORT
void JNICALL Java_software_amazon_awssdk_crt_CRT_awsCrtInit(
JNIEnv *env,
jclass jni_crt_class,
jint jni_memtrace,
jboolean jni_debug_wait,
jboolean jni_strict_shutdown) {
(void)jni_crt_class;
if (jni_debug_wait) {
bool done = false;
while (!done) {
;
}
}
g_memory_tracing = jni_memtrace;
/*
* Increase the maximum channel message size in order to improve throughput on large payloads.
* Consider adding a system property override in the future.
*/
g_aws_channel_max_fragment_size = KB_256;
/* check to see if we have support for backtraces only if we need to */
void *stack[1];
if (g_memory_tracing > 1 && 0 == aws_backtrace(stack, 1)) {
g_memory_tracing = 1;
}
/* NOT using aws_jni_get_allocator to avoid trace leak outside the test */
struct aws_allocator *allocator = aws_default_allocator();
aws_mqtt_library_init(allocator);
aws_http_library_init(allocator);
aws_auth_library_init(allocator);
aws_event_stream_library_init(allocator);
aws_s3_library_init(allocator);
aws_register_error_info(&s_crt_error_list);
aws_register_log_subject_info_list(&s_crt_log_subject_list);
s_jvm_table_add_jvm_for_env(env);
if (jni_strict_shutdown) {
atexit(s_jni_atexit_strict);
} else {
atexit(s_jni_atexit_gentle);
}
}
JNIEXPORT
void JNICALL Java_software_amazon_awssdk_crt_CRT_onJvmShutdown(JNIEnv *env, jclass jni_crt_class) {
(void)jni_crt_class;
s_jvm_table_remove_jvm_for_env(env);
}
JNIEXPORT
jint JNICALL Java_software_amazon_awssdk_crt_CRT_awsLastError(JNIEnv *env, jclass jni_crt_class) {
(void)env;
(void)jni_crt_class;
return aws_last_error();
}
JNIEXPORT
jstring JNICALL Java_software_amazon_awssdk_crt_CRT_awsErrorString(JNIEnv *env, jclass jni_crt_class, jint error_code) {
(void)jni_crt_class;
const char *error_msg = aws_error_str(error_code);
return (*env)->NewStringUTF(env, error_msg);
}
JNIEXPORT
jstring JNICALL Java_software_amazon_awssdk_crt_CRT_awsErrorName(JNIEnv *env, jclass jni_crt_class, jint error_code) {
(void)jni_crt_class;
const char *error_msg = aws_error_name(error_code);
return (*env)->NewStringUTF(env, error_msg);
}
JNIEXPORT
jlong JNICALL Java_software_amazon_awssdk_crt_CRT_awsNativeMemory(JNIEnv *env, jclass jni_crt_class) {
(void)env;
(void)jni_crt_class;
jlong allocated = 0;
if (g_memory_tracing) {
allocated = (jlong)aws_mem_tracer_bytes(aws_jni_get_allocator());
}
return allocated;
}
JNIEXPORT
void JNICALL Java_software_amazon_awssdk_crt_CRT_dumpNativeMemory(JNIEnv *env, jclass jni_crt_class) {
(void)env;
(void)jni_crt_class;
if (g_memory_tracing > 1) {
aws_mem_tracer_dump(aws_jni_get_allocator());
}
}
jstring aws_jni_string_from_cursor(JNIEnv *env, const struct aws_byte_cursor *native_data) {
struct aws_string *string = aws_string_new_from_array(aws_jni_get_allocator(), native_data->ptr, native_data->len);
AWS_FATAL_ASSERT(string != NULL);
jstring java_string = (*env)->NewStringUTF(env, aws_string_c_str(string));
aws_string_destroy(string);
return java_string;
}
jstring aws_jni_string_from_string(JNIEnv *env, const struct aws_string *string) {
AWS_FATAL_ASSERT(string != NULL);
jstring java_string = (*env)->NewStringUTF(env, aws_string_c_str(string));
return java_string;
}
JNIEXPORT
void JNICALL Java_software_amazon_awssdk_crt_CrtResource_waitForGlobalResourceDestruction(
JNIEnv *env,
jclass jni_crt_resource_class,
jint timeout_in_seconds) {
(void)env;
(void)jni_crt_resource_class;
aws_thread_set_managed_join_timeout_ns(
aws_timestamp_convert(timeout_in_seconds, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL));
aws_thread_join_all_managed();
if (g_memory_tracing) {
AWS_LOGF_DEBUG(
AWS_LS_COMMON_GENERAL,
"At shutdown, %u bytes remaining",
(uint32_t)aws_mem_tracer_bytes(aws_jni_get_allocator()));
if (g_memory_tracing > 1) {
aws_mem_tracer_dump(aws_jni_get_allocator());
}
}
}
JNIEXPORT
void JNICALL Java_software_amazon_awssdk_crt_CRT_nativeCheckJniExceptionContract(
JNIEnv *env,
jclass jni_crt_class,
jboolean clear_exception) {
(*env)->CallStaticVoidMethod(env, jni_crt_class, crt_properties.test_jni_exception_method_id, true);
if (clear_exception) {
(*env)->ExceptionClear(env);
(*env)->CallStaticVoidMethod(env, jni_crt_class, crt_properties.test_jni_exception_method_id, false);
} else {
(*env)->ExceptionCheck(env);
}
}