xref: /aosp_15_r20/external/aws-crt-java/src/native/event_loop_group.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 <jni.h>
7 
8 #include <aws/io/event_loop.h>
9 #include <aws/io/logging.h>
10 
11 #include "crt.h"
12 #include "java_class_ids.h"
13 
14 #if _MSC_VER
15 #    pragma warning(disable : 4204) /* non-constant aggregate initializer */
16 #endif
17 
18 /* on 32-bit platforms, casting pointers to longs throws a warning we don't need */
19 #if UINTPTR_MAX == 0xffffffff
20 #    if defined(_MSC_VER)
21 #        pragma warning(push)
22 #        pragma warning(disable : 4305) /* 'type cast': truncation from 'jlong' to 'jni_tls_ctx_options *' */
23 #    else
24 #        pragma GCC diagnostic push
25 #        pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
26 #        pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
27 #    endif
28 #endif
29 
30 struct event_loop_group_cleanup_callback_data {
31     JavaVM *jvm;
32     jobject java_event_loop_group;
33 };
34 
s_event_loop_group_cleanup_completion_callback(void * user_data)35 static void s_event_loop_group_cleanup_completion_callback(void *user_data) {
36     struct event_loop_group_cleanup_callback_data *callback_data = user_data;
37 
38     AWS_LOGF_DEBUG(AWS_LS_IO_EVENT_LOOP, "Event Loop Shutdown Complete");
39 
40     // Tell the Java event loop group that cleanup is done.  This lets it release its references.
41     JavaVM *jvm = callback_data->jvm;
42     JNIEnv *env = NULL;
43     /* fetch the env manually, rather than through the helper which will install an exit callback */
44 #ifdef ANDROID
45     (*jvm)->AttachCurrentThread(jvm, &env, NULL);
46 #else
47     /* awkward temp to get around gcc 4.1 strict aliasing incorrect warnings */
48     void *temp_env = NULL;
49     (*jvm)->AttachCurrentThread(jvm, (void **)&temp_env, NULL);
50     env = temp_env;
51 #endif
52 
53     /*
54      * The likely cause of env being null is the JVM shutting down before our stuff completely shuts down.  In that
55      * case, let's not even free memory.  This is most likely a consequence of a "failed" gentle shutdown so all
56      * the library clean up and allocator/logging clean up wont get called, but let's not even take that risk.
57      */
58     if (env != NULL) {
59         (*env)->CallVoidMethod(
60             env, callback_data->java_event_loop_group, event_loop_group_properties.onCleanupComplete);
61         AWS_FATAL_ASSERT(!aws_jni_check_and_clear_exception(env));
62 
63         // Remove the ref that was probably keeping the Java event loop group alive.
64         (*env)->DeleteGlobalRef(env, callback_data->java_event_loop_group);
65 
66         // We're done with this callback data, free it.
67         struct aws_allocator *allocator = aws_jni_get_allocator();
68         aws_mem_release(allocator, callback_data);
69 
70         (*jvm)->DetachCurrentThread(jvm);
71     }
72 }
73 
74 JNIEXPORT
Java_software_amazon_awssdk_crt_io_EventLoopGroup_eventLoopGroupNew(JNIEnv * env,jclass jni_elg,jobject elg_jobject,jint num_threads)75 jlong JNICALL Java_software_amazon_awssdk_crt_io_EventLoopGroup_eventLoopGroupNew(
76     JNIEnv *env,
77     jclass jni_elg,
78     jobject elg_jobject,
79     jint num_threads) {
80     (void)jni_elg;
81     aws_cache_jni_ids(env);
82 
83     struct aws_allocator *allocator = aws_jni_get_allocator();
84 
85     struct event_loop_group_cleanup_callback_data *callback_data =
86         aws_mem_acquire(allocator, sizeof(struct event_loop_group_cleanup_callback_data));
87     if (callback_data == NULL) {
88         aws_jni_throw_runtime_exception(
89             env, "EventLoopGroup.event_loop_group_new: shutdown callback data allocation failed");
90         goto on_error;
91     }
92 
93     jint jvmresult = (*env)->GetJavaVM(env, &callback_data->jvm);
94     AWS_FATAL_ASSERT(jvmresult == 0);
95 
96     struct aws_shutdown_callback_options shutdown_options = {
97         .shutdown_callback_fn = s_event_loop_group_cleanup_completion_callback,
98         .shutdown_callback_user_data = callback_data,
99     };
100 
101     struct aws_event_loop_group *elg =
102         aws_event_loop_group_new_default(allocator, (uint16_t)num_threads, &shutdown_options);
103     if (elg == NULL) {
104         aws_jni_throw_runtime_exception(
105             env, "EventLoopGroup.event_loop_group_new: aws_event_loop_group_new_default failed");
106         goto on_error;
107     }
108 
109     callback_data->java_event_loop_group = (*env)->NewGlobalRef(env, elg_jobject);
110 
111     return (jlong)elg;
112 
113 on_error:
114 
115     aws_mem_release(allocator, callback_data);
116 
117     return (jlong)NULL;
118 }
119 
120 JNIEXPORT
Java_software_amazon_awssdk_crt_io_EventLoopGroup_eventLoopGroupNewPinnedToCpuGroup(JNIEnv * env,jclass jni_elg,jobject elg_jobject,jint cpu_group,jint num_threads)121 jlong JNICALL Java_software_amazon_awssdk_crt_io_EventLoopGroup_eventLoopGroupNewPinnedToCpuGroup(
122     JNIEnv *env,
123     jclass jni_elg,
124     jobject elg_jobject,
125     jint cpu_group,
126     jint num_threads) {
127     (void)jni_elg;
128     aws_cache_jni_ids(env);
129 
130     struct aws_allocator *allocator = aws_jni_get_allocator();
131 
132     struct event_loop_group_cleanup_callback_data *callback_data =
133         aws_mem_acquire(allocator, sizeof(struct event_loop_group_cleanup_callback_data));
134     if (callback_data == NULL) {
135         aws_jni_throw_runtime_exception(
136             env, "EventLoopGroup.event_loop_group_new: shutdown callback data allocation failed");
137         goto on_error;
138     }
139 
140     jint jvmresult = (*env)->GetJavaVM(env, &callback_data->jvm);
141     AWS_FATAL_ASSERT(jvmresult == 0);
142 
143     struct aws_shutdown_callback_options shutdown_options = {
144         .shutdown_callback_fn = s_event_loop_group_cleanup_completion_callback,
145         .shutdown_callback_user_data = callback_data,
146     };
147 
148     struct aws_event_loop_group *elg = aws_event_loop_group_new_default_pinned_to_cpu_group(
149         allocator, (uint16_t)num_threads, (uint16_t)cpu_group, &shutdown_options);
150     if (elg == NULL) {
151         aws_jni_throw_runtime_exception(
152             env, "EventLoopGroup.event_loop_group_new: eventLoopGroupNewPinnedToCpuGroup failed");
153         goto on_error;
154     }
155 
156     callback_data->java_event_loop_group = (*env)->NewGlobalRef(env, elg_jobject);
157 
158     return (jlong)elg;
159 
160 on_error:
161 
162     aws_mem_release(allocator, callback_data);
163 
164     return (jlong)NULL;
165 }
166 
167 JNIEXPORT
Java_software_amazon_awssdk_crt_io_EventLoopGroup_eventLoopGroupDestroy(JNIEnv * env,jclass jni_elg,jlong elg_addr)168 void JNICALL Java_software_amazon_awssdk_crt_io_EventLoopGroup_eventLoopGroupDestroy(
169     JNIEnv *env,
170     jclass jni_elg,
171     jlong elg_addr) {
172     (void)jni_elg;
173     aws_cache_jni_ids(env);
174 
175     struct aws_event_loop_group *elg = (struct aws_event_loop_group *)elg_addr;
176     if (!elg) {
177         aws_jni_throw_runtime_exception(
178             env, "EventLoopGroup.eventLoopGroupDestroy: instance should be non-null at release time");
179         return;
180     }
181 
182     aws_event_loop_group_release(elg);
183 }
184 
185 #if UINTPTR_MAX == 0xffffffff
186 #    if defined(_MSC_VER)
187 #        pragma warning(pop)
188 #    else
189 #        pragma GCC diagnostic pop
190 #    endif
191 #endif
192