Skip to content
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

ARROW-16329: [Java][C++] Keep more context when marshalling errors through JNI #13246

Merged
merged 5 commits into from
Jul 6, 2022

Conversation

zhztheplayer
Copy link
Member

@zhztheplayer zhztheplayer commented May 27, 2022

@github-actions
Copy link

Thanks for opening a pull request!

If this is not a minor PR. Could you open an issue for this pull request on JIRA? https://issues.apache.org/jira/browse/ARROW

Opening JIRAs ahead of time contributes to the Openness of the Apache Arrow project.

Then could you also rename pull request title in the following format?

ARROW-${JIRA_ID}: [${COMPONENT}] ${SUMMARY}

or

MINOR: [${COMPONENT}] ${SUMMARY}

See also:

@zhztheplayer zhztheplayer changed the title Return exception details in arrow::dataset::jni::CheckException ARROW-16329: Return exception details in arrow::dataset::jni::CheckException May 27, 2022
@zhztheplayer zhztheplayer changed the title ARROW-16329: Return exception details in arrow::dataset::jni::CheckException ARROW-16329: [Java][C++] Keep more context when marshalling errors through JNI May 27, 2022
@github-actions
Copy link

@github-actions
Copy link

⚠️ Ticket has not been started in JIRA, please click 'Start Progress'.

std::string description = JStringToCString(
env, (jstring)env->CallStaticObjectMethod(describer_class, describe_method, t));
return Status::Invalid("Error during calling Java code from native code: " +
description);
Copy link
Member

@pitrou pitrou May 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... I don't know what is possible exactly, but ideally we should:

  1. map Java exception classes to specific C++ status codes (for example IOException would map to Status::IOError while InvalidTypeException would map to Status::TypeError)
  2. keep the Java exception object in a Java-specific StatusDetail subclass, just like we do for Python exceptions

cc @lwhite1

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also this helper function should be moved out of dataset-specific code to be reused generically in all JNI bindings.

Copy link
Member Author

@zhztheplayer zhztheplayer May 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also this helper function should be moved out of dataset-specific code to be reused generically in all JNI bindings.

Was considering doing the similar thing but maybe after we have a common code module for JNI stuffs? Which may require some redesign for other parts of JNI codes, orc, gandiva, plasma, etc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If having a common module for JNI utilities is non-trivial, then can you open a JIRA for it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zhztheplayer Do you want to address the other parts of the initial comment in this PR?

@pitrou
Copy link
Member

pitrou commented Jun 30, 2022

@lidavidm @lwhite1 Does either of you want to take this up?

@lidavidm
Copy link
Member

lidavidm commented Jul 1, 2022

I'm not going to have time. Since Larry is also working on reorganizing the JNI code anyways I'll leave it up to him whether/how to approach this

@zhztheplayer zhztheplayer force-pushed the ARROW-16329 branch 2 times, most recently from 1004d62 to 24bb7ab Compare July 2, 2022 14:52
@zhztheplayer
Copy link
Member Author

@pitrou @lidavidm I have updated the PR to address the early comments. Would you like to take a look again? Thanks.

/**
* For native code to invoke to convert a java/lang/Throwable to jstring.
*/
public class JniExceptionDescriber {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be a public class?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to use package private.

@@ -17,14 +17,14 @@

#include <mutex>

#include "./jni_util.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#include "./jni_util.h"
#include "jni_util.h"

since Kou just fixed this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

arrow::Result<bool> IsErrorInstanceOf(JNIEnv* env, jthrowable t, std::string class_name) {
jclass jclass = env->FindClass(class_name.c_str());
if (jclass == nullptr) {
return arrow::Status::Invalid("Class not found: " + class_name);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the way this is used below, maybe this should be a DCHECK(jclass) << ...; or just return false?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is DCHECK suitable for null-checking + returning? Anyway I have changed to return false in latest commit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, DCHECK will abort the program (in a debug build). Presumably it's a bug if we can't find any of these classes/we shouldn't continue execution? But returning false is OK

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's a bug that we can't find them (i.e. they should always be loaded), I would favour returning an error, or at least emitting a warning message.

Copy link
Member Author

@zhztheplayer zhztheplayer Jul 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed to use DCHECK to guard against debug build since class_name is hard coded. Would you please take a look? Thanks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems ok to me, thanks.

// Reattach current thread to JVM
getEnvStat = vm->AttachCurrentThread(reinterpret_cast<void**>(&env), nullptr);
if (getEnvStat != JNI_OK) {
std::cout << "Failed to attach current thread to JVM. " << std::endl;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ARROW_LOG instead of std::cout?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -17,6 +17,7 @@

#include "./jni_util.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#include "./jni_util.h"
#include "jni_util.h"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -17,6 +17,7 @@

#include "jni_util.h"

#include <iostream>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused include now?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -162,11 +165,105 @@ std::shared_ptr<ReservationListener> ReservationListenableMemoryPool::get_listen

ReservationListenableMemoryPool::~ReservationListenableMemoryPool() {}

std::string Describe(JNIEnv* env, jthrowable t) {
jclass describer_class =
env->FindClass("org/apache/arrow/dataset/jni/JniExceptionDescriber");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're going to check the result of FindClass below, let's also check it here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

arrow::StatusCode MapJavaError(JNIEnv* env, jthrowable t) {
StatusCode code;
if (IsErrorInstanceOf(env, t, "org/apache/arrow/memory/OutOfMemoryException") ==
JNI_TRUE) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: the method returns bool so no need for explicit == JNI_TRUE

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

std::string JavaErrorDetail::ToString() const {
JNIEnv* env = GetEnvOrAttach(vm_);
if (env == nullptr) {
return "Java Exception, ID: " + std::to_string(reinterpret_cast<size_t>(cause_));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cause_ is a pointer, so use uintptr_t?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -91,6 +93,24 @@ void ReleaseNativeRef(jlong ref) {
delete retrieved_ptr;
}

const char kJavaErrorDetailTypeId[] = "arrow::dataset::jni::JavaErrorDetail";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a static member inside JavaErrorDetail, or perhaps extern and defined in the .cc?
As is, it seems there will be symbol clashes if jni_util.h is included by several .cc files.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have moved the variable into .cc.

Copy link
Member

@pitrou pitrou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll let a Java expert do the final approval but this looks basically ok to me (and a very marked improvement IMHO).

@lidavidm lidavidm merged commit 2a2d01d into apache:master Jul 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants