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