/** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ #include #include #include #include "crt.h" #include "java_class_ids.h" #if _MSC_VER # pragma warning(disable : 4204) /* non-constant aggregate initializer */ #endif /* on 32-bit platforms, casting pointers to longs throws a warning we don't need */ #if UINTPTR_MAX == 0xffffffff # if defined(_MSC_VER) # pragma warning(push) # pragma warning(disable : 4305) /* 'type cast': truncation from 'jlong' to 'jni_tls_ctx_options *' */ # else # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wpointer-to-int-cast" # pragma GCC diagnostic ignored "-Wint-to-pointer-cast" # endif #endif struct event_loop_group_cleanup_callback_data { JavaVM *jvm; jobject java_event_loop_group; }; static void s_event_loop_group_cleanup_completion_callback(void *user_data) { struct event_loop_group_cleanup_callback_data *callback_data = user_data; AWS_LOGF_DEBUG(AWS_LS_IO_EVENT_LOOP, "Event Loop Shutdown Complete"); // Tell the Java event loop group that cleanup is done. This lets it release its references. JavaVM *jvm = callback_data->jvm; JNIEnv *env = NULL; /* fetch the env manually, rather than through the helper which will install an exit callback */ #ifdef ANDROID (*jvm)->AttachCurrentThread(jvm, &env, NULL); #else /* awkward temp to get around gcc 4.1 strict aliasing incorrect warnings */ void *temp_env = NULL; (*jvm)->AttachCurrentThread(jvm, (void **)&temp_env, NULL); env = temp_env; #endif /* * The likely cause of env being null is the JVM shutting down before our stuff completely shuts down. In that * case, let's not even free memory. This is most likely a consequence of a "failed" gentle shutdown so all * the library clean up and allocator/logging clean up wont get called, but let's not even take that risk. */ if (env != NULL) { (*env)->CallVoidMethod( env, callback_data->java_event_loop_group, event_loop_group_properties.onCleanupComplete); AWS_FATAL_ASSERT(!aws_jni_check_and_clear_exception(env)); // Remove the ref that was probably keeping the Java event loop group alive. (*env)->DeleteGlobalRef(env, callback_data->java_event_loop_group); // We're done with this callback data, free it. struct aws_allocator *allocator = aws_jni_get_allocator(); aws_mem_release(allocator, callback_data); (*jvm)->DetachCurrentThread(jvm); } } JNIEXPORT jlong JNICALL Java_software_amazon_awssdk_crt_io_EventLoopGroup_eventLoopGroupNew( JNIEnv *env, jclass jni_elg, jobject elg_jobject, jint num_threads) { (void)jni_elg; aws_cache_jni_ids(env); struct aws_allocator *allocator = aws_jni_get_allocator(); struct event_loop_group_cleanup_callback_data *callback_data = aws_mem_acquire(allocator, sizeof(struct event_loop_group_cleanup_callback_data)); if (callback_data == NULL) { aws_jni_throw_runtime_exception( env, "EventLoopGroup.event_loop_group_new: shutdown callback data allocation failed"); goto on_error; } jint jvmresult = (*env)->GetJavaVM(env, &callback_data->jvm); AWS_FATAL_ASSERT(jvmresult == 0); struct aws_shutdown_callback_options shutdown_options = { .shutdown_callback_fn = s_event_loop_group_cleanup_completion_callback, .shutdown_callback_user_data = callback_data, }; struct aws_event_loop_group *elg = aws_event_loop_group_new_default(allocator, (uint16_t)num_threads, &shutdown_options); if (elg == NULL) { aws_jni_throw_runtime_exception( env, "EventLoopGroup.event_loop_group_new: aws_event_loop_group_new_default failed"); goto on_error; } callback_data->java_event_loop_group = (*env)->NewGlobalRef(env, elg_jobject); return (jlong)elg; on_error: aws_mem_release(allocator, callback_data); return (jlong)NULL; } JNIEXPORT jlong JNICALL Java_software_amazon_awssdk_crt_io_EventLoopGroup_eventLoopGroupNewPinnedToCpuGroup( JNIEnv *env, jclass jni_elg, jobject elg_jobject, jint cpu_group, jint num_threads) { (void)jni_elg; aws_cache_jni_ids(env); struct aws_allocator *allocator = aws_jni_get_allocator(); struct event_loop_group_cleanup_callback_data *callback_data = aws_mem_acquire(allocator, sizeof(struct event_loop_group_cleanup_callback_data)); if (callback_data == NULL) { aws_jni_throw_runtime_exception( env, "EventLoopGroup.event_loop_group_new: shutdown callback data allocation failed"); goto on_error; } jint jvmresult = (*env)->GetJavaVM(env, &callback_data->jvm); AWS_FATAL_ASSERT(jvmresult == 0); struct aws_shutdown_callback_options shutdown_options = { .shutdown_callback_fn = s_event_loop_group_cleanup_completion_callback, .shutdown_callback_user_data = callback_data, }; struct aws_event_loop_group *elg = aws_event_loop_group_new_default_pinned_to_cpu_group( allocator, (uint16_t)num_threads, (uint16_t)cpu_group, &shutdown_options); if (elg == NULL) { aws_jni_throw_runtime_exception( env, "EventLoopGroup.event_loop_group_new: eventLoopGroupNewPinnedToCpuGroup failed"); goto on_error; } callback_data->java_event_loop_group = (*env)->NewGlobalRef(env, elg_jobject); return (jlong)elg; on_error: aws_mem_release(allocator, callback_data); return (jlong)NULL; } JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_io_EventLoopGroup_eventLoopGroupDestroy( JNIEnv *env, jclass jni_elg, jlong elg_addr) { (void)jni_elg; aws_cache_jni_ids(env); struct aws_event_loop_group *elg = (struct aws_event_loop_group *)elg_addr; if (!elg) { aws_jni_throw_runtime_exception( env, "EventLoopGroup.eventLoopGroupDestroy: instance should be non-null at release time"); return; } aws_event_loop_group_release(elg); } #if UINTPTR_MAX == 0xffffffff # if defined(_MSC_VER) # pragma warning(pop) # else # pragma GCC diagnostic pop # endif #endif