-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Java: Handle panics and errors in the Java FFI layer #1601
Merged
acarbonetto
merged 15 commits into
valkey-io:main
from
Bit-Quill:java/integ_lotjonat_panics
Jun 29, 2024
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
5bfccd1
Restructure Java FFI layer to handle errors properly
jonathanl-bq f517a9a
Merge branch 'java/integ_lotjonat_panics' into java/dev_lotjonat_panics
jonathanl-bq af41556
Fix failing tests
jonathanl-bq f6b5b17
Address clippy lints
jonathanl-bq de68abd
Add tests for error and panic handling
jonathanl-bq bcdb92a
Add missing errors module
jonathanl-bq 7c297ad
Fix clippy lint
jonathanl-bq 87cf9cd
Fix FFI tests
jonathanl-bq e430a45
Apply Spotless
jonathanl-bq d890c84
Fix some minor issue I forgot about
jonathanl-bq 7ca187c
Add some comments
jonathanl-bq 88f1f46
Apply Spotless
jonathanl-bq 3f04b40
Merge pull request #355 from Bit-Quill/java/dev_lotjonat_panics
jonathanl-bq d4298cd
Resolve merge conflict
jonathanl-bq ee09467
Make handle_panics return Option<T> instead
jonathanl-bq File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/** | ||
* Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 | ||
*/ | ||
use jni::{errors::Error as JNIError, JNIEnv}; | ||
use log::error; | ||
use std::string::FromUtf8Error; | ||
|
||
pub enum FFIError { | ||
Jni(JNIError), | ||
Uds(String), | ||
Utf8(FromUtf8Error), | ||
} | ||
|
||
impl From<jni::errors::Error> for FFIError { | ||
fn from(value: jni::errors::Error) -> Self { | ||
FFIError::Jni(value) | ||
} | ||
} | ||
|
||
impl From<FromUtf8Error> for FFIError { | ||
fn from(value: FromUtf8Error) -> Self { | ||
FFIError::Utf8(value) | ||
} | ||
} | ||
|
||
impl std::fmt::Display for FFIError { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
match self { | ||
FFIError::Jni(err) => write!(f, "{}", err), | ||
FFIError::Uds(err) => write!(f, "{}", err), | ||
FFIError::Utf8(err) => write!(f, "{}", err), | ||
} | ||
} | ||
} | ||
|
||
#[derive(Copy, Clone)] | ||
pub enum ExceptionType { | ||
Exception, | ||
RuntimeException, | ||
} | ||
|
||
impl std::fmt::Display for ExceptionType { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
match self { | ||
ExceptionType::Exception => write!(f, "java/lang/Exception"), | ||
ExceptionType::RuntimeException => write!(f, "java/lang/RuntimeException"), | ||
} | ||
} | ||
} | ||
|
||
// This handles `FFIError`s by converting them to Java exceptions and throwing them. | ||
pub fn handle_errors<T>(env: &mut JNIEnv, result: Result<T, FFIError>) -> Option<T> { | ||
match result { | ||
Ok(value) => Some(value), | ||
Err(err) => { | ||
match err { | ||
FFIError::Utf8(utf8_error) => throw_java_exception( | ||
env, | ||
ExceptionType::RuntimeException, | ||
&utf8_error.to_string(), | ||
), | ||
error => throw_java_exception(env, ExceptionType::Exception, &error.to_string()), | ||
}; | ||
// Return `None` because we need to still return a value after throwing. | ||
// This signals to the caller that we need to return the default value. | ||
None | ||
} | ||
} | ||
} | ||
|
||
// This function handles Rust panics by converting them into Java exceptions and throwing them. | ||
// `func` returns an `Option<T>` because this is intended to wrap the output of `handle_errors`. | ||
pub fn handle_panics<T, F: std::panic::UnwindSafe + FnOnce() -> Option<T>>( | ||
func: F, | ||
ffi_func_name: &str, | ||
) -> Option<T> { | ||
match std::panic::catch_unwind(func) { | ||
Ok(value) => value, | ||
Err(_err) => { | ||
// Following https://github.com/jni-rs/jni-rs/issues/76#issuecomment-363523906 | ||
// and throwing a runtime exception is not feasible here because of https://github.com/jni-rs/jni-rs/issues/432 | ||
error!("Native function {} panicked.", ffi_func_name); | ||
None | ||
} | ||
} | ||
} | ||
|
||
pub fn throw_java_exception(env: &mut JNIEnv, exception_type: ExceptionType, message: &str) { | ||
match env.exception_check() { | ||
Ok(true) => (), | ||
Ok(false) => { | ||
env.throw_new(exception_type.to_string(), message) | ||
.unwrap_or_else(|err| { | ||
error!( | ||
"Failed to create exception with string {}: {}", | ||
message, | ||
err.to_string() | ||
); | ||
}); | ||
} | ||
Err(err) => { | ||
error!( | ||
"Failed to check if an exception is currently being thrown: {}", | ||
err.to_string() | ||
); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I would do this a bit differently:
And use it like this: