xref: /aosp_15_r20/external/aws-crt-java/src/native/crt.c (revision 3c7ae9de214676c52d19f01067dc1a404272dc11)
1 /**
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  * SPDX-License-Identifier: Apache-2.0.
4  */
5 
6 #include <aws/auth/auth.h>
7 #include <aws/common/allocator.h>
8 #include <aws/common/atomics.h>
9 #include <aws/common/clock.h>
10 #include <aws/common/common.h>
11 #include <aws/common/hash_table.h>
12 #include <aws/common/logging.h>
13 #include <aws/common/rw_lock.h>
14 #include <aws/common/string.h>
15 #include <aws/common/system_info.h>
16 #include <aws/common/thread.h>
17 #include <aws/event-stream/event_stream.h>
18 #include <aws/http/connection.h>
19 #include <aws/http/http.h>
20 #include <aws/io/channel.h>
21 #include <aws/io/io.h>
22 #include <aws/io/logging.h>
23 #include <aws/io/tls_channel_handler.h>
24 #include <aws/mqtt/mqtt.h>
25 #include <aws/s3/s3.h>
26 
27 #include <stdio.h>
28 
29 #include "crt.h"
30 #include "java_class_ids.h"
31 #include "logging.h"
32 
33 /* 0 = off, 1 = bytes, 2 = stack traces, see aws_mem_trace_level */
34 int g_memory_tracing = 0;
s_init_allocator(void)35 static struct aws_allocator *s_init_allocator(void) {
36     if (g_memory_tracing) {
37         struct aws_allocator *allocator = aws_default_allocator();
38         allocator = aws_mem_tracer_new(allocator, NULL, (enum aws_mem_trace_level)g_memory_tracing, 8);
39         return allocator;
40     }
41     return aws_default_allocator();
42 }
43 
44 static struct aws_allocator *s_allocator = NULL;
aws_jni_get_allocator(void)45 struct aws_allocator *aws_jni_get_allocator(void) {
46     if (AWS_UNLIKELY(s_allocator == NULL)) {
47         s_allocator = s_init_allocator();
48     }
49     return s_allocator;
50 }
51 
s_detach_jvm_from_thread(void * user_data)52 static void s_detach_jvm_from_thread(void *user_data) {
53     AWS_LOGF_DEBUG(AWS_LS_COMMON_GENERAL, "s_detach_jvm_from_thread invoked");
54     JavaVM *jvm = user_data;
55 
56     /* we don't need this JNIEnv, but this is an easy way to verify the JVM is still valid to use */
57     /********** JNI ENV ACQUIRE **********/
58     JNIEnv *env = aws_jni_acquire_thread_env(jvm);
59     if (env != NULL) {
60         (*jvm)->DetachCurrentThread(jvm);
61 
62         aws_jni_release_thread_env(jvm, env);
63         /********** JNI ENV RELEASE **********/
64     }
65 }
66 
s_aws_jni_get_thread_env(JavaVM * jvm)67 static JNIEnv *s_aws_jni_get_thread_env(JavaVM *jvm) {
68 #ifdef ANDROID
69     JNIEnv *env = NULL;
70 #else
71     void *env = NULL;
72 #endif
73     if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_6) == JNI_EDETACHED) {
74         AWS_LOGF_DEBUG(AWS_LS_COMMON_GENERAL, "s_aws_jni_get_thread_env returned detached, attaching");
75 
76         struct aws_string *thread_name = NULL;
77         if (aws_thread_current_name(aws_jni_get_allocator(), &thread_name)) {
78             /* Retrieving thread name can fail for multitude of reasons and is
79             not fatal. Ignore the error and continue. */
80             AWS_LOGF_DEBUG(AWS_LS_COMMON_GENERAL, "Failed to retrieve name for the thread.");
81         }
82 
83         struct JavaVMAttachArgs attach_args = {
84             .version = JNI_VERSION_1_6,
85             .name = NULL,
86             .group = NULL,
87         };
88 
89         if (thread_name != NULL) {
90             attach_args.name = (char *)aws_string_c_str(thread_name);
91         }
92 
93 #ifdef ANDROID
94         jint result = (*jvm)->AttachCurrentThreadAsDaemon(jvm, &env, &attach_args);
95 #else
96         jint result = (*jvm)->AttachCurrentThreadAsDaemon(jvm, (void **)&env, &attach_args);
97 #endif
98 
99         aws_string_destroy(thread_name);
100 
101         /* Ran out of memory, don't log in this case */
102         AWS_FATAL_ASSERT(result != JNI_ENOMEM);
103         if (result != JNI_OK) {
104             fprintf(stderr, "Unrecoverable AttachCurrentThreadAsDaemon failed, JNI error code is %d\n", (int)result);
105             return NULL;
106         }
107         /* This should only happen in event loop threads, the JVM main thread attachment is
108          * managed by the JVM, so we only need to clean up event loop thread attachments */
109         AWS_FATAL_ASSERT(AWS_OP_SUCCESS == aws_thread_current_at_exit(s_detach_jvm_from_thread, (void *)jvm));
110     }
111 
112     return env;
113 }
114 
115 /*
116 A simple system to support unpredictable JVM shutdowns.  In an ideal world, everyone would correctly use the
117 CrtResource ref counting and strict (aws_thread_managed_join_all) shutdown, but given the difficulty of using
118 them correctly, that's not a realistic expectation.  So we need to come up with a way for JVM shutdowns to
119 not trigger crashes from native threads that try and call back to Java (where even the JavaVM pointer cached
120 on the binding object is now garbage) after the JVM has shutdown (but before the process has killed all of its
121 threads).
122 
123 Our system works as follows:
124 
125 We track the set of all active JVMs (since we don't correctly support multiple JVMs yet, this is always going to be
126 either one or zero for now).  We protect this set with a read-write lock.  Adding (CRT init) or removing (JVM
127 shutdown hook) a JVM from this set will take a write lock.  Acquiring a JNIEnv from a tracked JVM, will take a read
128 lock, and releasing a JNIEnv will release the read lock.
129 
130 Acquiring a JNIEnv succeeds if the JVM in question is in our set, and fails otherwise.  All users of a JNIEnv have
131 been hardened to check for null and just not call to Java in that case.
132 
133 Since we don't have RAII in C, bindings must be very careful to release once, and exactly once, every JNIEnv that
134 they acquire.  An alternative approach would be to replace all of the JNIEnv usage with a new API that
135 takes the lock, calls a supplied callback (which does all the JNIEnv operations), and then releases the lock.  This
136 approach was tried but was so disruptive refactor-wise that I deemed it too dangerous to try and push through.  So
137 instead, we just have to be careful with acquire/release.
138 
139 In this way, the vast majority of usage is relatively contentionless; it's just a bunch of native threads taking
140 read locks on a shared rw lock.  Only when the JVM shutdown hook calls into native is there read-write contention.
141  */
142 static struct aws_rw_lock s_jvm_table_lock = AWS_RW_LOCK_INIT;
143 static struct aws_hash_table *s_jvms = NULL;
144 
s_jvm_table_add_jvm_for_env(JNIEnv * env)145 static void s_jvm_table_add_jvm_for_env(JNIEnv *env) {
146     aws_rw_lock_wlock(&s_jvm_table_lock);
147 
148     if (s_jvms == NULL) {
149         /* use default allocator so that tracing allocator doesn't flag this as a leak during tests */
150         s_jvms = aws_mem_calloc(aws_default_allocator(), 1, sizeof(struct aws_hash_table));
151         AWS_FATAL_ASSERT(
152             AWS_OP_SUCCESS ==
153             aws_hash_table_init(s_jvms, aws_default_allocator(), 1, aws_hash_ptr, aws_ptr_eq, NULL, NULL));
154     }
155 
156     JavaVM *jvm = NULL;
157     jint jvmresult = (*env)->GetJavaVM(env, &jvm);
158     AWS_FATAL_ASSERT(jvmresult == 0 && jvm != NULL);
159 
160     int was_created = 0;
161     AWS_FATAL_ASSERT(AWS_OP_SUCCESS == aws_hash_table_put(s_jvms, jvm, NULL, &was_created));
162     AWS_FATAL_ASSERT(was_created == 1);
163 
164     aws_rw_lock_wunlock(&s_jvm_table_lock);
165 }
166 
s_jvm_table_remove_jvm_for_env(JNIEnv * env)167 static void s_jvm_table_remove_jvm_for_env(JNIEnv *env) {
168     aws_rw_lock_wlock(&s_jvm_table_lock);
169 
170     if (s_jvms == NULL) {
171         goto done;
172     }
173 
174     JavaVM *jvm = NULL;
175     jint jvmresult = (*env)->GetJavaVM(env, &jvm);
176     AWS_FATAL_ASSERT(jvmresult == 0 && jvm != NULL);
177 
178     AWS_FATAL_ASSERT(AWS_OP_SUCCESS == aws_hash_table_remove(s_jvms, jvm, NULL, NULL));
179 
180     if (aws_hash_table_get_entry_count(s_jvms) == 0) {
181         aws_hash_table_clean_up(s_jvms);
182         aws_mem_release(aws_default_allocator(), s_jvms);
183         s_jvms = NULL;
184     }
185 
186 done:
187 
188     aws_rw_lock_wunlock(&s_jvm_table_lock);
189 }
190 
aws_jni_acquire_thread_env(JavaVM * jvm)191 JNIEnv *aws_jni_acquire_thread_env(JavaVM *jvm) {
192     /*
193      * We use try-lock here in order to avoid the re-entrant deadlock case that could happen if we have a read
194      * lock already, the JVM shutdown hooks causes another thread to block on taking the write lock, and then
195      * we try to reacquire the read-lock recursively due to some synchronous code path.  That case can deadlock
196      * but since the JVM is going away, it's safe to just fail completely from here on out.
197      */
198     if (aws_rw_lock_try_rlock(&s_jvm_table_lock)) {
199         if (aws_last_error() != AWS_ERROR_UNSUPPORTED_OPERATION) {
200             aws_raise_error(AWS_ERROR_JAVA_CRT_JVM_DESTROYED);
201         }
202         return NULL;
203     }
204 
205     if (s_jvms == NULL) {
206         aws_raise_error(AWS_ERROR_JAVA_CRT_JVM_DESTROYED);
207         goto error;
208     }
209 
210     struct aws_hash_element *element = NULL;
211     int find_result = aws_hash_table_find(s_jvms, jvm, &element);
212     if (find_result != AWS_OP_SUCCESS || element == NULL) {
213         aws_raise_error(AWS_ERROR_JAVA_CRT_JVM_DESTROYED);
214         goto error;
215     }
216 
217     JNIEnv *env = s_aws_jni_get_thread_env(jvm);
218     if (env == NULL) {
219         aws_raise_error(AWS_ERROR_JAVA_CRT_JVM_DESTROYED);
220         goto error;
221     }
222 
223     return env;
224 
225 error:
226 
227     aws_rw_lock_runlock(&s_jvm_table_lock);
228 
229     return NULL;
230 }
231 
aws_jni_release_thread_env(JavaVM * jvm,JNIEnv * env)232 void aws_jni_release_thread_env(JavaVM *jvm, JNIEnv *env) {
233     (void)jvm;
234     (void)env;
235 
236     if (env != NULL) {
237         aws_rw_lock_runlock(&s_jvm_table_lock);
238     }
239 }
240 
aws_jni_throw_runtime_exception(JNIEnv * env,const char * msg,...)241 void aws_jni_throw_runtime_exception(JNIEnv *env, const char *msg, ...) {
242     va_list args;
243     va_start(args, msg);
244     char buf[1024];
245     vsnprintf(buf, sizeof(buf), msg, args);
246     va_end(args);
247 
248     int error = aws_last_error();
249     char exception[1280];
250     snprintf(
251         exception,
252         sizeof(exception),
253         "%s (aws_last_error: %s(%d), %s)",
254         buf,
255         aws_error_name(error),
256         error,
257         aws_error_str(error));
258     jclass runtime_exception = crt_runtime_exception_properties.crt_runtime_exception_class;
259     (*env)->ThrowNew(env, runtime_exception, exception);
260 }
261 
aws_jni_throw_null_pointer_exception(JNIEnv * env,const char * msg,...)262 void aws_jni_throw_null_pointer_exception(JNIEnv *env, const char *msg, ...) {
263     va_list args;
264     va_start(args, msg);
265     char buf[1024];
266     vsnprintf(buf, sizeof(buf), msg, args);
267     va_end(args);
268     (*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/NullPointerException"), buf);
269 }
270 
aws_jni_throw_illegal_argument_exception(JNIEnv * env,const char * msg,...)271 void aws_jni_throw_illegal_argument_exception(JNIEnv *env, const char *msg, ...) {
272     va_list args;
273     va_start(args, msg);
274     char buf[1024];
275     vsnprintf(buf, sizeof(buf), msg, args);
276     va_end(args);
277     (*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/IllegalArgumentException"), buf);
278 }
279 
aws_jni_check_and_clear_exception(JNIEnv * env)280 bool aws_jni_check_and_clear_exception(JNIEnv *env) {
281     bool exception_pending = (*env)->ExceptionCheck(env);
282     if (exception_pending) {
283         (*env)->ExceptionDescribe(env);
284         (*env)->ExceptionClear(env);
285     }
286     return exception_pending;
287 }
288 
aws_jni_get_and_clear_exception(JNIEnv * env,jthrowable * out)289 bool aws_jni_get_and_clear_exception(JNIEnv *env, jthrowable *out) {
290     bool exception_pending = (*env)->ExceptionCheck(env);
291     if (exception_pending) {
292         (*env)->DeleteGlobalRef(env, *out);
293         *out = (jthrowable)(*env)->NewGlobalRef(env, (*env)->ExceptionOccurred(env));
294         (*env)->ExceptionClear(env);
295     }
296     return exception_pending;
297 }
298 
aws_size_t_from_java(JNIEnv * env,size_t * out_size,jlong java_long,const char * errmsg_prefix)299 int aws_size_t_from_java(JNIEnv *env, size_t *out_size, jlong java_long, const char *errmsg_prefix) {
300     if (java_long < 0) {
301         aws_jni_throw_illegal_argument_exception(env, "%s cannot be negative", errmsg_prefix);
302         goto error;
303     }
304 
305     if ((uint64_t)java_long > (uint64_t)SIZE_MAX) {
306         aws_jni_throw_illegal_argument_exception(
307             env, "%s cannot exceed %zu when running 32bit", errmsg_prefix, SIZE_MAX);
308         goto error;
309     }
310 
311     *out_size = (size_t)java_long;
312     return AWS_OP_SUCCESS;
313 error:
314     *out_size = 0;
315     return AWS_OP_ERR;
316 }
317 
aws_java_byte_array_new(JNIEnv * env,size_t size)318 jbyteArray aws_java_byte_array_new(JNIEnv *env, size_t size) {
319     jbyteArray jArray = (*env)->NewByteArray(env, (jsize)size);
320     return jArray;
321 }
322 
aws_copy_native_array_to_java_byte_array(JNIEnv * env,jbyteArray dst,uint8_t * src,size_t amount)323 bool aws_copy_native_array_to_java_byte_array(JNIEnv *env, jbyteArray dst, uint8_t *src, size_t amount) {
324     (*env)->SetByteArrayRegion(env, dst, 0, (jsize)amount, (jbyte *)src);
325     return aws_jni_check_and_clear_exception(env);
326 }
327 
328 /**
329  * Converts a Native aws_byte_cursor to a Java byte[]
330  */
aws_jni_byte_array_from_cursor(JNIEnv * env,const struct aws_byte_cursor * native_data)331 jbyteArray aws_jni_byte_array_from_cursor(JNIEnv *env, const struct aws_byte_cursor *native_data) {
332     jbyteArray jArray = aws_java_byte_array_new(env, native_data->len);
333     if (jArray) {
334         if (!aws_copy_native_array_to_java_byte_array(env, jArray, native_data->ptr, native_data->len)) {
335             return jArray;
336         }
337     }
338     return NULL;
339 }
340 
341 /**
342  * Get the Buffer Position (the next element to read/write)
343  */
aws_jni_byte_buffer_get_position(JNIEnv * env,jobject java_byte_buffer)344 int aws_jni_byte_buffer_get_position(JNIEnv *env, jobject java_byte_buffer) {
345     jint position = (*env)->CallIntMethod(env, java_byte_buffer, byte_buffer_properties.get_position);
346     return (aws_jni_check_and_clear_exception(env)) ? -1 : (int)position;
347 }
348 
349 /**
350  * Set the Buffer Position (the next element to read/write)
351  */
aws_jni_byte_buffer_set_position(JNIEnv * env,jobject jByteBuf,jint position)352 void aws_jni_byte_buffer_set_position(JNIEnv *env, jobject jByteBuf, jint position) {
353     jobject val = (*env)->CallObjectMethod(env, jByteBuf, byte_buffer_properties.set_position, position);
354     AWS_FATAL_ASSERT(!aws_jni_check_and_clear_exception(env));
355     (*env)->DeleteLocalRef(env, val);
356 }
357 
358 /**
359  * Set the Buffer Limit (the max allowed element to read/write)
360  */
aws_jni_byte_buffer_set_limit(JNIEnv * env,jobject jByteBuf,jint limit)361 void aws_jni_byte_buffer_set_limit(JNIEnv *env, jobject jByteBuf, jint limit) {
362     jobject val = (*env)->CallObjectMethod(env, jByteBuf, byte_buffer_properties.set_limit, limit);
363     AWS_FATAL_ASSERT(!aws_jni_check_and_clear_exception(env));
364     (*env)->DeleteLocalRef(env, val);
365 }
366 
aws_jni_direct_byte_buffer_from_raw_ptr(JNIEnv * env,const void * dst,size_t capacity)367 jobject aws_jni_direct_byte_buffer_from_raw_ptr(JNIEnv *env, const void *dst, size_t capacity) {
368 
369     jobject jByteBuf = (*env)->NewDirectByteBuffer(env, (void *)dst, (jlong)capacity);
370     if (jByteBuf) {
371         aws_jni_byte_buffer_set_limit(env, jByteBuf, (jint)capacity);
372         aws_jni_byte_buffer_set_position(env, jByteBuf, 0);
373     }
374 
375     return jByteBuf;
376 }
377 
aws_jni_byte_cursor_from_jstring_acquire(JNIEnv * env,jstring str)378 struct aws_byte_cursor aws_jni_byte_cursor_from_jstring_acquire(JNIEnv *env, jstring str) {
379     if (str == NULL) {
380         aws_jni_throw_null_pointer_exception(env, "string is null");
381         return aws_byte_cursor_from_array(NULL, 0);
382     }
383 
384     const char *bytes = (*env)->GetStringUTFChars(env, str, NULL);
385     if (bytes == NULL) {
386         /* GetStringUTFChars() has thrown exception */
387         return aws_byte_cursor_from_array(NULL, 0);
388     }
389 
390     return aws_byte_cursor_from_array(bytes, (size_t)(*env)->GetStringUTFLength(env, str));
391 }
392 
aws_jni_byte_cursor_from_jstring_release(JNIEnv * env,jstring str,struct aws_byte_cursor cur)393 void aws_jni_byte_cursor_from_jstring_release(JNIEnv *env, jstring str, struct aws_byte_cursor cur) {
394     if (cur.ptr != NULL) {
395         (*env)->ReleaseStringUTFChars(env, str, (const char *)cur.ptr);
396     }
397 }
398 
aws_jni_byte_cursor_from_jbyteArray_acquire(JNIEnv * env,jbyteArray array)399 struct aws_byte_cursor aws_jni_byte_cursor_from_jbyteArray_acquire(JNIEnv *env, jbyteArray array) {
400     if (array == NULL) {
401         aws_jni_throw_null_pointer_exception(env, "byte[] is null");
402         return aws_byte_cursor_from_array(NULL, 0);
403     }
404 
405     jbyte *bytes = (*env)->GetByteArrayElements(env, array, NULL);
406     if (bytes == NULL) {
407         /* GetByteArrayElements() has thrown exception */
408         return aws_byte_cursor_from_array(NULL, 0);
409     }
410 
411     size_t len = (*env)->GetArrayLength(env, array);
412     return aws_byte_cursor_from_array(bytes, len);
413 }
414 
aws_jni_byte_cursor_from_jbyteArray_release(JNIEnv * env,jbyteArray array,struct aws_byte_cursor cur)415 void aws_jni_byte_cursor_from_jbyteArray_release(JNIEnv *env, jbyteArray array, struct aws_byte_cursor cur) {
416     if (cur.ptr != NULL) {
417         (*env)->ReleaseByteArrayElements(env, array, (jbyte *)cur.ptr, JNI_ABORT);
418     }
419 }
420 
aws_jni_byte_cursor_from_direct_byte_buffer(JNIEnv * env,jobject byte_buffer)421 struct aws_byte_cursor aws_jni_byte_cursor_from_direct_byte_buffer(JNIEnv *env, jobject byte_buffer) {
422     jlong payload_size = (*env)->GetDirectBufferCapacity(env, byte_buffer);
423     if (payload_size == -1) {
424         aws_jni_throw_runtime_exception(
425             env, "MqttClientConnection.mqtt_publish: Unable to get capacity of payload ByteBuffer");
426         return aws_byte_cursor_from_array(NULL, 0);
427     }
428     jbyte *payload_data = (*env)->GetDirectBufferAddress(env, byte_buffer);
429     if (!payload_data) {
430         aws_jni_throw_runtime_exception(
431             env, "MqttClientConnection.mqtt_publish: Unable to get buffer from payload ByteBuffer");
432         return aws_byte_cursor_from_array(NULL, 0);
433     }
434     return aws_byte_cursor_from_array((const uint8_t *)payload_data, (size_t)payload_size);
435 }
436 
aws_jni_new_string_from_jstring(JNIEnv * env,jstring str)437 struct aws_string *aws_jni_new_string_from_jstring(JNIEnv *env, jstring str) {
438     struct aws_allocator *allocator = aws_jni_get_allocator();
439     const char *str_chars = (*env)->GetStringUTFChars(env, str, NULL);
440     if (!str_chars) {
441         aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
442         return NULL;
443     }
444     struct aws_string *result = aws_string_new_from_c_str(allocator, str_chars);
445     (*env)->ReleaseStringUTFChars(env, str, str_chars);
446     return result;
447 }
448 
aws_jni_new_crt_exception_from_error_code(JNIEnv * env,int error_code)449 jobject aws_jni_new_crt_exception_from_error_code(JNIEnv *env, int error_code) {
450     jint jni_error_code = error_code;
451 
452     jobject crt_exception = (*env)->NewObject(
453         env,
454         crt_runtime_exception_properties.crt_runtime_exception_class,
455         crt_runtime_exception_properties.constructor_method_id,
456         jni_error_code);
457     AWS_FATAL_ASSERT(crt_exception);
458     return crt_exception;
459 }
460 
461 #define AWS_DEFINE_ERROR_INFO_CRT(CODE, STR) AWS_DEFINE_ERROR_INFO(CODE, STR, "aws-crt-java")
462 
463 /* clang-format off */
464 static struct aws_error_info s_crt_errors[] = {
465     AWS_DEFINE_ERROR_INFO_CRT(
466         AWS_ERROR_JAVA_CRT_JVM_DESTROYED,
467         "Attempt to use a JVM that has already been destroyed"),
468     AWS_DEFINE_ERROR_INFO_CRT(
469         AWS_ERROR_JAVA_CRT_JVM_OUT_OF_MEMORY,
470         "OutOfMemoryError has been raised from JVM."),
471 };
472 /* clang-format on */
473 
474 static struct aws_error_info_list s_crt_error_list = {
475     .error_list = s_crt_errors,
476     .count = sizeof(s_crt_errors) / sizeof(struct aws_error_info),
477 };
478 
479 static struct aws_log_subject_info s_crt_log_subject_infos[] = {
480     /* clang-format off */
481     DEFINE_LOG_SUBJECT_INFO(
482         AWS_LS_JAVA_CRT_GENERAL, "JavaCrtGeneral", "Subject for aws-crt-java logging that defies categorization"),
483     DEFINE_LOG_SUBJECT_INFO(
484         AWS_LS_JAVA_CRT_RESOURCE, "JavaCrtResource", "Subject for CrtResource"),
485     DEFINE_LOG_SUBJECT_INFO(
486         AWS_LS_JAVA_CRT_S3, "JavaCrtS3", "Subject for the layer binding aws-c-s3 to Java"),
487     DEFINE_LOG_SUBJECT_INFO(
488         AWS_LS_JAVA_ANDROID_KEYCHAIN, "android-keychain", "Subject for Android KeyChain"),
489     /* clang-format on */
490 };
491 
492 static struct aws_log_subject_info_list s_crt_log_subject_list = {
493     .subject_list = s_crt_log_subject_infos,
494     .count = AWS_ARRAY_SIZE(s_crt_log_subject_infos),
495 };
496 
s_jni_atexit_strict(void)497 static void s_jni_atexit_strict(void) {
498 
499     aws_unregister_log_subject_info_list(&s_crt_log_subject_list);
500     aws_unregister_error_info(&s_crt_error_list);
501 
502     aws_s3_library_clean_up();
503     aws_event_stream_library_clean_up();
504     aws_auth_library_clean_up();
505     aws_http_library_clean_up();
506     aws_mqtt_library_clean_up();
507 
508     if (g_memory_tracing) {
509         struct aws_allocator *tracer_allocator = aws_jni_get_allocator();
510         aws_mem_tracer_dump(tracer_allocator);
511     }
512 
513     aws_jni_cleanup_logging();
514 
515     if (g_memory_tracing) {
516         struct aws_allocator *tracer_allocator = aws_jni_get_allocator();
517         aws_mem_tracer_destroy(tracer_allocator);
518     }
519 
520     s_allocator = NULL;
521 }
522 
523 #define DEFAULT_MANAGED_SHUTDOWN_WAIT_IN_SECONDS 1
524 
s_jni_atexit_gentle(void)525 static void s_jni_atexit_gentle(void) {
526 
527     /* If not doing strict shutdown, wait only a short time before shutting down */
528     aws_thread_set_managed_join_timeout_ns(
529         aws_timestamp_convert(DEFAULT_MANAGED_SHUTDOWN_WAIT_IN_SECONDS, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL));
530 
531     if (aws_thread_join_all_managed() == AWS_OP_SUCCESS) {
532         /* a successful managed join means it should be safe to do a full, strict clean up */
533         s_jni_atexit_strict();
534     } else {
535         /*
536          * We didn't successfully join all our threads so it's not really safe to clean up the libraries.
537          * Just dump memory if applicable and exit.
538          */
539         AWS_LOGF_WARN(
540             AWS_LS_JAVA_CRT_GENERAL,
541             "Not all native threads were successfully joined during gentle shutdown.  Memory may be leaked.");
542 
543         if (g_memory_tracing) {
544             AWS_LOGF_DEBUG(
545                 AWS_LS_JAVA_CRT_GENERAL,
546                 "At shutdown, %u bytes remaining",
547                 (uint32_t)aws_mem_tracer_bytes(aws_jni_get_allocator()));
548             if (g_memory_tracing > 1) {
549                 aws_mem_tracer_dump(aws_jni_get_allocator());
550             }
551         }
552     }
553 }
554 
555 #define KB_256 (256 * 1024)
556 
557 /* Called as the entry point, immediately after the shared lib is loaded the first time by JNI */
558 JNIEXPORT
Java_software_amazon_awssdk_crt_CRT_awsCrtInit(JNIEnv * env,jclass jni_crt_class,jint jni_memtrace,jboolean jni_debug_wait,jboolean jni_strict_shutdown)559 void JNICALL Java_software_amazon_awssdk_crt_CRT_awsCrtInit(
560     JNIEnv *env,
561     jclass jni_crt_class,
562     jint jni_memtrace,
563     jboolean jni_debug_wait,
564     jboolean jni_strict_shutdown) {
565     (void)jni_crt_class;
566 
567     if (jni_debug_wait) {
568         bool done = false;
569         while (!done) {
570             ;
571         }
572     }
573 
574     g_memory_tracing = jni_memtrace;
575 
576     /*
577      * Increase the maximum channel message size in order to improve throughput on large payloads.
578      * Consider adding a system property override in the future.
579      */
580     g_aws_channel_max_fragment_size = KB_256;
581 
582     /* check to see if we have support for backtraces only if we need to */
583     void *stack[1];
584     if (g_memory_tracing > 1 && 0 == aws_backtrace(stack, 1)) {
585         g_memory_tracing = 1;
586     }
587 
588     /* NOT using aws_jni_get_allocator to avoid trace leak outside the test */
589     struct aws_allocator *allocator = aws_default_allocator();
590     aws_mqtt_library_init(allocator);
591     aws_http_library_init(allocator);
592     aws_auth_library_init(allocator);
593     aws_event_stream_library_init(allocator);
594     aws_s3_library_init(allocator);
595 
596     aws_register_error_info(&s_crt_error_list);
597     aws_register_log_subject_info_list(&s_crt_log_subject_list);
598 
599     s_jvm_table_add_jvm_for_env(env);
600 
601     if (jni_strict_shutdown) {
602         atexit(s_jni_atexit_strict);
603     } else {
604         atexit(s_jni_atexit_gentle);
605     }
606 }
607 
608 JNIEXPORT
Java_software_amazon_awssdk_crt_CRT_onJvmShutdown(JNIEnv * env,jclass jni_crt_class)609 void JNICALL Java_software_amazon_awssdk_crt_CRT_onJvmShutdown(JNIEnv *env, jclass jni_crt_class) {
610 
611     (void)jni_crt_class;
612 
613     s_jvm_table_remove_jvm_for_env(env);
614 }
615 
616 JNIEXPORT
Java_software_amazon_awssdk_crt_CRT_awsLastError(JNIEnv * env,jclass jni_crt_class)617 jint JNICALL Java_software_amazon_awssdk_crt_CRT_awsLastError(JNIEnv *env, jclass jni_crt_class) {
618     (void)env;
619     (void)jni_crt_class;
620     return aws_last_error();
621 }
622 
623 JNIEXPORT
Java_software_amazon_awssdk_crt_CRT_awsErrorString(JNIEnv * env,jclass jni_crt_class,jint error_code)624 jstring JNICALL Java_software_amazon_awssdk_crt_CRT_awsErrorString(JNIEnv *env, jclass jni_crt_class, jint error_code) {
625     (void)jni_crt_class;
626     const char *error_msg = aws_error_str(error_code);
627     return (*env)->NewStringUTF(env, error_msg);
628 }
629 
630 JNIEXPORT
Java_software_amazon_awssdk_crt_CRT_awsErrorName(JNIEnv * env,jclass jni_crt_class,jint error_code)631 jstring JNICALL Java_software_amazon_awssdk_crt_CRT_awsErrorName(JNIEnv *env, jclass jni_crt_class, jint error_code) {
632     (void)jni_crt_class;
633     const char *error_msg = aws_error_name(error_code);
634     return (*env)->NewStringUTF(env, error_msg);
635 }
636 
637 JNIEXPORT
Java_software_amazon_awssdk_crt_CRT_awsNativeMemory(JNIEnv * env,jclass jni_crt_class)638 jlong JNICALL Java_software_amazon_awssdk_crt_CRT_awsNativeMemory(JNIEnv *env, jclass jni_crt_class) {
639     (void)env;
640     (void)jni_crt_class;
641     jlong allocated = 0;
642     if (g_memory_tracing) {
643         allocated = (jlong)aws_mem_tracer_bytes(aws_jni_get_allocator());
644     }
645     return allocated;
646 }
647 
648 JNIEXPORT
Java_software_amazon_awssdk_crt_CRT_dumpNativeMemory(JNIEnv * env,jclass jni_crt_class)649 void JNICALL Java_software_amazon_awssdk_crt_CRT_dumpNativeMemory(JNIEnv *env, jclass jni_crt_class) {
650     (void)env;
651     (void)jni_crt_class;
652     if (g_memory_tracing > 1) {
653         aws_mem_tracer_dump(aws_jni_get_allocator());
654     }
655 }
656 
aws_jni_string_from_cursor(JNIEnv * env,const struct aws_byte_cursor * native_data)657 jstring aws_jni_string_from_cursor(JNIEnv *env, const struct aws_byte_cursor *native_data) {
658     struct aws_string *string = aws_string_new_from_array(aws_jni_get_allocator(), native_data->ptr, native_data->len);
659     AWS_FATAL_ASSERT(string != NULL);
660 
661     jstring java_string = (*env)->NewStringUTF(env, aws_string_c_str(string));
662     aws_string_destroy(string);
663 
664     return java_string;
665 }
666 
aws_jni_string_from_string(JNIEnv * env,const struct aws_string * string)667 jstring aws_jni_string_from_string(JNIEnv *env, const struct aws_string *string) {
668     AWS_FATAL_ASSERT(string != NULL);
669 
670     jstring java_string = (*env)->NewStringUTF(env, aws_string_c_str(string));
671 
672     return java_string;
673 }
674 
675 JNIEXPORT
Java_software_amazon_awssdk_crt_CrtResource_waitForGlobalResourceDestruction(JNIEnv * env,jclass jni_crt_resource_class,jint timeout_in_seconds)676 void JNICALL Java_software_amazon_awssdk_crt_CrtResource_waitForGlobalResourceDestruction(
677     JNIEnv *env,
678     jclass jni_crt_resource_class,
679     jint timeout_in_seconds) {
680     (void)env;
681     (void)jni_crt_resource_class;
682 
683     aws_thread_set_managed_join_timeout_ns(
684         aws_timestamp_convert(timeout_in_seconds, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL));
685     aws_thread_join_all_managed();
686 
687     if (g_memory_tracing) {
688         AWS_LOGF_DEBUG(
689             AWS_LS_COMMON_GENERAL,
690             "At shutdown, %u bytes remaining",
691             (uint32_t)aws_mem_tracer_bytes(aws_jni_get_allocator()));
692         if (g_memory_tracing > 1) {
693             aws_mem_tracer_dump(aws_jni_get_allocator());
694         }
695     }
696 }
697 
698 JNIEXPORT
Java_software_amazon_awssdk_crt_CRT_nativeCheckJniExceptionContract(JNIEnv * env,jclass jni_crt_class,jboolean clear_exception)699 void JNICALL Java_software_amazon_awssdk_crt_CRT_nativeCheckJniExceptionContract(
700     JNIEnv *env,
701     jclass jni_crt_class,
702     jboolean clear_exception) {
703 
704     (*env)->CallStaticVoidMethod(env, jni_crt_class, crt_properties.test_jni_exception_method_id, true);
705 
706     if (clear_exception) {
707         (*env)->ExceptionClear(env);
708         (*env)->CallStaticVoidMethod(env, jni_crt_class, crt_properties.test_jni_exception_method_id, false);
709     } else {
710         (*env)->ExceptionCheck(env);
711     }
712 }
713