Skip to content

Commit 4e106dc

Browse files
switch to critical mem access for checksums (#878)
1 parent 13594a1 commit 4e106dc

3 files changed

Lines changed: 77 additions & 6 deletions

File tree

src/native/checksums.c

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,29 @@
99
#include "crt.h"
1010
#include "java_class_ids.h"
1111

12+
/**
13+
* Note: we use critical mem access in below functions to speed up checksums.
14+
* This approach is the same as what OpenJDK uses for their checksum implementation.
15+
* Think twice before using similar approach elsewhere as it can lead to stalling.
16+
*/
17+
1218
jint crc32_common(
1319
JNIEnv *env,
1420
jbyteArray input,
1521
jint previous,
1622
const size_t start,
1723
size_t length,
1824
uint32_t (*checksum_fn)(const uint8_t *, size_t, uint32_t)) {
19-
struct aws_byte_cursor c_byte_array = aws_jni_byte_cursor_from_jbyteArray_acquire(env, input);
25+
struct aws_byte_cursor c_byte_array = aws_jni_byte_cursor_from_jbyteArray_critical_acquire(env, input);
26+
if (AWS_UNLIKELY(c_byte_array.ptr == NULL)) {
27+
return previous;
28+
}
29+
2030
struct aws_byte_cursor cursor = c_byte_array;
2131
aws_byte_cursor_advance(&cursor, start);
2232
cursor.len = aws_min_size(length, cursor.len);
2333
jint res_signed = (jint)checksum_fn(cursor.ptr, cursor.len, previous);
24-
aws_jni_byte_cursor_from_jbyteArray_release(env, input, c_byte_array);
34+
aws_jni_byte_cursor_from_jbyteArray_critical_release(env, input, c_byte_array);
2535
return res_signed;
2636
}
2737

@@ -61,11 +71,15 @@ JNIEXPORT jlong JNICALL Java_software_amazon_awssdk_crt_checksums_CRC64NVME_crc6
6171
(void)jni_class;
6272
aws_cache_jni_ids(env);
6373

64-
struct aws_byte_cursor c_byte_array = aws_jni_byte_cursor_from_jbyteArray_acquire(env, input);
74+
struct aws_byte_cursor c_byte_array = aws_jni_byte_cursor_from_jbyteArray_critical_acquire(env, input);
75+
if (AWS_UNLIKELY(c_byte_array.ptr == NULL)) {
76+
return previous;
77+
}
78+
6579
struct aws_byte_cursor cursor = c_byte_array;
6680
aws_byte_cursor_advance(&cursor, offset);
6781
cursor.len = aws_min_size(length, cursor.len);
6882
jlong res_signed = (jlong)aws_checksums_crc64nvme_ex(cursor.ptr, cursor.len, previous);
69-
aws_jni_byte_cursor_from_jbyteArray_release(env, input, c_byte_array);
83+
aws_jni_byte_cursor_from_jbyteArray_critical_release(env, input, c_byte_array);
7084
return res_signed;
7185
}

src/native/crt.c

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,15 @@ void aws_jni_throw_null_pointer_exception(JNIEnv *env, const char *msg, ...) {
273273
(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/NullPointerException"), buf);
274274
}
275275

276+
void aws_jni_throw_out_of_memory_exception(JNIEnv *env, const char *msg, ...) {
277+
va_list args;
278+
va_start(args, msg);
279+
char buf[1024];
280+
vsnprintf(buf, sizeof(buf), msg, args);
281+
va_end(args);
282+
(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/OutOfMemoryError"), buf);
283+
}
284+
276285
void aws_jni_throw_illegal_argument_exception(JNIEnv *env, const char *msg, ...) {
277286
va_list args;
278287
va_start(args, msg);
@@ -441,12 +450,34 @@ struct aws_byte_cursor aws_jni_byte_cursor_from_jbyteArray_acquire(JNIEnv *env,
441450
return aws_byte_cursor_from_array(bytes, len);
442451
}
443452

453+
struct aws_byte_cursor aws_jni_byte_cursor_from_jbyteArray_critical_acquire(JNIEnv *env, jbyteArray array) {
454+
if (array == NULL) {
455+
aws_jni_throw_null_pointer_exception(env, "byte[] is null");
456+
return aws_byte_cursor_from_array(NULL, 0);
457+
}
458+
459+
jbyte *bytes = (*env)->GetPrimitiveArrayCritical(env, array, NULL);
460+
if (bytes == NULL) {
461+
aws_jni_throw_out_of_memory_exception(env, "failed to acquire array memory");
462+
return aws_byte_cursor_from_array(NULL, 0);
463+
}
464+
465+
size_t len = (*env)->GetArrayLength(env, array);
466+
return aws_byte_cursor_from_array(bytes, len);
467+
}
468+
444469
void aws_jni_byte_cursor_from_jbyteArray_release(JNIEnv *env, jbyteArray array, struct aws_byte_cursor cur) {
445470
if (cur.ptr != NULL) {
446471
(*env)->ReleaseByteArrayElements(env, array, (jbyte *)cur.ptr, JNI_ABORT);
447472
}
448473
}
449474

475+
void aws_jni_byte_cursor_from_jbyteArray_critical_release(JNIEnv *env, jbyteArray array, struct aws_byte_cursor cur) {
476+
if (cur.ptr != NULL) {
477+
(*env)->ReleasePrimitiveArrayCritical(env, array, (jbyte *)cur.ptr, JNI_ABORT);
478+
}
479+
}
480+
450481
struct aws_byte_cursor aws_jni_byte_cursor_from_direct_byte_buffer(JNIEnv *env, jobject byte_buffer) {
451482
jlong payload_size = (*env)->GetDirectBufferCapacity(env, byte_buffer);
452483
if (payload_size == -1) {
@@ -619,7 +650,7 @@ void JNICALL Java_software_amazon_awssdk_crt_CRT_awsCrtInit(
619650
* AWS-LC always defines FIPS_mode() that you can call and check what the library was built with. It does not define
620651
* a public OPENSSL_FIPS/AWSLC_FIPS macro that we can (or need to) check here
621652
*
622-
* Safeguard with macro's, for example because Libressl dosn't define
653+
* Safeguard with macro's, for example because Libressl doesn't define
623654
* FIPS_mode() by default.
624655
* */
625656
#if defined(OPENSSL_FIPS) || defined(OPENSSL_IS_AWSLC)

src/native/crt.h

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ void aws_jni_throw_runtime_exception(JNIEnv *env, const char *msg, ...);
4646
******************************************************************************/
4747
void aws_jni_throw_null_pointer_exception(JNIEnv *env, const char *msg, ...);
4848

49+
/*******************************************************************************
50+
* Throws java OutOfMemoryError
51+
******************************************************************************/
52+
void aws_jni_throw_out_of_memory_exception(JNIEnv *env, const char *msg, ...);
53+
4954
/*******************************************************************************
5055
* Throws java IllegalArgumentException
5156
******************************************************************************/
@@ -172,19 +177,40 @@ void aws_jni_byte_cursor_from_jstring_release(JNIEnv *env, jstring str, struct a
172177
/*******************************************************************************
173178
* aws_jni_byte_cursor_from_jbyteArray_acquire - Creates an aws_byte_cursor from the
174179
* bytes extracted from the supplied jbyteArray.
175-
* The aws_byte_cursor MUST be given to aws_jni_byte_cursor_from jstring_release() when
180+
* The aws_byte_cursor MUST be given to aws_jni_byte_cursor_from_jbyteArray_release() when
176181
* it's no longer needed, or it will leak.
177182
*
178183
* If there is an error, the returned aws_byte_cursor.ptr will be NULL and
179184
* and a java exception is being thrown.
180185
******************************************************************************/
181186
struct aws_byte_cursor aws_jni_byte_cursor_from_jbyteArray_acquire(JNIEnv *env, jbyteArray str);
182187

188+
/*******************************************************************************
189+
* aws_jni_byte_cursor_from_jbyteArray_critical_acquire - Creates an aws_byte_cursor from the
190+
* bytes extracted from the supplied jbyteArray.
191+
* The aws_byte_cursor MUST be given to aws_jni_byte_cursor_from_jbyteArray_critical_release() when
192+
* it's no longer needed, or it will leak.
193+
*
194+
* If there is an error, the returned aws_byte_cursor.ptr will be NULL and
195+
* and a java exception is being thrown.
196+
* **** WARNING: Here be dragons ****
197+
* This function uses critical jvm functions to access memory, which is faster, but comes
198+
* with a litany of limitations, so use at your own risk.
199+
* Spending too much time in a critical section will stall gc and mem functionality, so under
200+
* no conditions use jni functions while in critical section on current thread (ok, on other threads).
201+
******************************************************************************/
202+
struct aws_byte_cursor aws_jni_byte_cursor_from_jbyteArray_critical_acquire(JNIEnv *env, jbyteArray str);
203+
183204
/********************************************************************************
184205
* aws_jni_byte_cursor_from_jbyteArray_release - Releases the array back to the JVM
185206
********************************************************************************/
186207
void aws_jni_byte_cursor_from_jbyteArray_release(JNIEnv *env, jbyteArray str, struct aws_byte_cursor cur);
187208

209+
/********************************************************************************
210+
* aws_jni_byte_cursor_from_jbyteArray_critical_release - Releases critical array access back to JVM
211+
********************************************************************************/
212+
void aws_jni_byte_cursor_from_jbyteArray_critical_release(JNIEnv *env, jbyteArray str, struct aws_byte_cursor cur);
213+
188214
/*******************************************************************************
189215
* aws_jni_byte_cursor_from_direct_byte_buffer - Creates an aws_byte_cursor from the
190216
* direct byte buffer. Note that the buffer is not reference pinned, so the cursor

0 commit comments

Comments
 (0)