blob: 98b330cb6ac8f27897b76d250ccfe979d7705a75 [file] [log] [blame] [edit]
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <jni.h>
#include <aws/io/event_loop.h>
#include <aws/io/logging.h>
#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