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