FATAL Error in native method: Using JNIEnv in non-java thread - java

I'm implementing some JNI callback (native -> java) functionality alongside another larger native framework and on one of the framework's callbacks I'm getting this error when trying to callback to java.
FATAL Error in native method: Using JNIEnv in non-java thread
What exactly does this mean? What is a java-thread and how do I go about using JNI in another native thread?

Java must be attached to the thread on which it is to be executing.
The JNI interface pointer (JNIEnv) is valid only in the current thread. Should another thread need to access the Java VM, it must first call AttachCurrentThread() to attach itself to the VM and obtain a JNI interface pointer. Once attached to the VM, a native thread works just like an ordinary Java thread running inside a native method.
To do this, you must store a pointer to the JVM object, either through the JNI_OnLoad() export or by storing it via a JNI native call you've implemented using (JNIEnv*)java->GetJavaVm(&(JavaVM*)jvm);.
From there, each time you need to use JNI, simply call the following in order to attach to the current thread and retrieve a new JNIEnv* pointer.
JNIEnv* AttachJava()
{
JavaVMAttachArgs args = {JNI_VERSION_1_2, 0, 0};
JNIEnv* java;
jvm->AttachCurrentThread((void**) &java, &args);
return java;
}
Do not save instances of JNIEnv* unless you are sure they will be referenced in the same thread.
As the documentation states, calling AttachCurrentThread on an already attached thread is a no-op and thus is innocuous.

Related

JNI call fails in driver callback

I am developing an application that interacts with a hardware device. Using the dll file provided with the hardware's official application, I initialize the device and register some function as a callback to be called upon some user interaction. In this callback function, I want to call a Java function to transfer data. However, the whole application exits without any error log just at this call in the callback:
jclass cls = env->FindClass("java/lang/String");
The same call works if it is executed in the function that is called directly by Java.
What is the reason of this behaviour? How does it make difference to call JNI from device driver calls? Any help is appreciated.
EDIT: I tried Vernee's suggestion and tried to attach the driver thread to JVM, however the behaviour didn't change. Moreover, I lost the printf outputs, which are -unfortunately- my only option to debug JNI side. They work before the attach operation, but stop working thereafter.
If you are developing on Windows, I strongly suggest you that you use visual studio to debug the C code. You can start your java program and place a breakpoint on System.load, when the Java program stops at this point, go to Visual studio and from tools > attach process, this way you can stop at breakpoints placed in the C code. After that just resume the java code. Calling a java method from C thread requires some preperation:
1- Cache JVM object
JavaVM * javaVm;
(*jenv)->GetJavaVM(jenv, &javaVm);
2- Cache the Class object of the Class that contains your java callback method.
clazz = (*jenv)->NewGlobalRef(jenv, (*jenv)->FindClass(jenv, "com/something/somepackage/SomeClass"));
3- If you are calling instance method, you will also need to cahce the instance being called
callback = (*jenv)->NewGlobalRef(jenv, callbackInstance);
4- Attach native thread to virtual machine (When you need to make the call to java method)
JNIEnv * jenv;
int errorCode = (*j_javaVm)->AttachCurrentThread(j_javaVm, (void**) &jenv, NULL);
5- Get the method ID you need to call (When you need to make the call to java method)
jmethodID methodID = (*jenv)->GetMethodID(jenv, cachedhandlerClass, "methodNameHere", "methodSignetureHere");
6- Make the method call
(*jenv)->CallVoidMethod(jenv, cachedCallbackInstance, methodID, param1, param2,....);
7- deattach the native thread
(*j_javaVm)->DetachCurrentThread(j_javaVm);
Steps 1,2 and 3 require a java environment and they can be done in JNI_OnLoad method or in the implementation of a native Java method.

IllegalMonitorStateException when creating an object

I'm getting IllegalMonitorStateException when I'm trying to create an instance of an object. The code looks like the following:
public int signIn(parameters...)
{
...check some stuff...
new Thread(... just a simple log here...).start();//IllegalMonitorStateException
return result;
}
MORE INFO:
The actual application consists of 2 programs (C++ and java) interacting via JNI). The scenario in which I'm getting exception is as follows.
The c++ program asks java to connect to a server. (this is a non blocking operation)
Java program informs c++ about connection success. (in a new thread so that java can continue doing other tasks)
When receiving connection success, c++ program asks java to login
Exception occurs.
I should note that this exception only happens in this special scenario and if I call login sometime after connection success everything works fine.
What I've tried:
In the beginning informing connection success was not in a new thread, but creating the thread did not solve the problem.
The java login code had some synchronization stuff but removing them and replacing it with a simple log still produces the problem.
EDIT:
Here's the stacktrace:
Phoenix.client.ClientAPI.NativeInterface.NativeAPIEventListener.onConnectingFinished(Native
Method)
Phoenix.client.ClientAPI.NativeInterface.NativeAPIEventListener.access$000(NativeAPIEventListener.java:12)
Phoenix.client.ClientAPI.NativeInterface.NativeAPIEventListener$1.run(NativeAPIEventListener.java:30)
java.lang.Thread.run(Unknown Source)
I created a new thread in C++ code when java code called back into it This broke the jthread:java -> c++ -> java chain into jthread:java -> c++ and cthread:c++ -> java. This solved the problem I was facing. However I ran into a different problem which lead me into reading a bit of JNI documentation. Quoting JNI doc:
The JNI interface pointer (JNIEnv) is valid only in the current
thread. Should another thread need to access the Java VM, it must
first call AttachCurrentThread() to attach itself to the VM and obtain
a JNI interface pointer. Once attached to the VM, a native thread
works just like an ordinary Java thread running inside a native
method. The native thread remains attached to the VM until it calls
DetachCurrentThread() to detach itself.
So I guess I should've called AttachCurrentThread before calling back into java. However this does not exactly fit in the above description since the thread was not a native thread (it was a thread originally created in java code, could I call DetachCurrentThread afterwards?). I did not test this solution since I had to create a new thread other reasons too. But if I get a chance to try this out I'll confirm.

What does a JVM have to do when calling a native method?

What are the usual steps that the JVM runtime has to perform when calling a Java method that is declared as native?
How does a HotSpot 1.8.0 JVM implement a JNI function call? What checking steps are involved (e.g. unhandled exceptions after return?), what bookkeeping has the JVM to perform (e.g. a local reference registry?), and where does the control go after the call of the native Java method? I would also appreciate it if someone could provide the entry point or important methods from the native HotSpot 1.8.0 code.
Disclaimer: I know that I can read the code myself but a prior explanation helps in quickly finding my way through the code. Additionally, I found this question worthwhile to be Google searchable. ;)
Calling a JNI method from Java is rather expensive comparing to a simple C function call.
HotSpot typically performs most of the following steps to invoke a JNI method:
Create a stack frame.
Move arguments to proper register or stack locations according to ABI.
Wrap object references to JNI handles.
Obtain JNIEnv* and jclass for static methods and pass them as additional arguments.
Check if should call method_entry trace function.
Lock an object monitor if the method is synchronized.
Check if the native function is linked already. Function lookup and linking is performed lazily.
Switch thread from in_java to in_native state.
Call the native function
Check if safepoint is needed.
Return thread to in_java state.
Unlock monitor if locked.
Notify method_exit.
Unwrap object result and reset JNI handles block.
Handle JNI exceptions.
Remove the stack frame.
The source code for this procedure can be found at SharedRuntime::generate_native_wrapper.
As you can see, an overhead may be significant. But in many cases most of the above steps are not necessary. For example, if a native method just performs some encoding/decoding on a byte array and does not throw any exceptions nor it calls other JNI functions. For these cases HotSpot has a non-standard (and not known) convention called Critical Natives, discussed here.

What is the correct way to use v8::Locker, and why must I use it?

I'm trying to embed v8 in an Android application using NDK.
I have a JNI module that looks something like this (JNI mapping code not shown):
#include <jni.h>
#include <android/log.h>
#include <v8.h>
using namespace v8;
static jlong getMagicNumber() {
HandleScope handle_scope;
Persistent<Context> context = Context::New();
Context::Scope context_scope(context);
Handle<String> source = String::New("40 + 2");
Handle<Script> script = Script::Compile(source);
Handle<Value> result = script->Run();
context.Dispose();
return result->NumberValue();
}
The first time I run getMagicNumber, it correctly runs and returns 42. The second time I try to run it, it crashes.
Specifically, this ASSERT seen in v8's isolate.h fails:
// Returns the isolate inside which the current thread is running.
INLINE(static Isolate* Current()) {
Isolate* isolate = reinterpret_cast<Isolate*>(
Thread::GetExistingThreadLocal(isolate_key_));
ASSERT(isolate != NULL);
return isolate;
}
It sounds a lot like this problem, which suggests using v8::Locker to obtain "exclusive access to the isolate".
By adding a simple Locker l; to the top of getMagicNumber, the crash no longer occurs. Problems that fix themselves that easily tend to break themselves when I'm not paying attention.
I only have the most tenuous understanding of why this fixes my problem, and I'm getting compiler warnings that I'm using v8::Locker in a deprecated fashion. The recommended method is to provide it with a v8::Isolate as an argument to v8::Locker's constructor, but I have no idea how I'm supposed to "obtain" an isolate.
Ultimately: What is the proper way to solve this problem according to the current state of v8, and why?
As I understand it, a V8 isolate is an instance of the V8 runtime, complete with a heap, a garbage collector, and zero or more V8 contexts. Isolates are not thread-safe and must be protected via v8::Locker.
In general, to use V8 you must first create an isolate:
v8::Isolate* isolate = v8::Isolate::New();
Then, to use the isolate from any thread:
v8::Locker locker(isolate);
v8::Isolate::Scope isolateScope(isolate);
At this point the thread owns the isolate and is free to create contexts, execute scripts, etc.
Now, for the benefit of very simple applications, V8 provides a default isolate and relaxes the locking requirement, but you can only use these crutches if you always access V8 from the same thread. My guess is that your application failed because the second call was made from a different thread.
I am just learning V8 now, but I think you need to call:
v8::Locker locker(isolate);
This will create a stack allocated Locker object which will block the Isolate from being used on another thread. When the current function returns this stack object's destructor will be called automatically causing the Isolate to be unlocked.
The you need to call:
v8::Isolate::Scope isolateScope(isolate);
This sets the current thread to run this Isolate. Isolates can only be used on one thread. The Locker enforces this, but the Isolate itself needs to be configured for the current thread. This creates a stack allocated object which specifies which Isolate is associated with the current thread. Just like the Locker, when this variable goes out of scope (when the current function returns) the Scope destructor gets called to un-set the Isolate as the default. I believe this is needed because many of the V8 API calls need a reference to an Isolate, but don't take one as a parameter. Therefore they need one they can access directly (probably through per-thread variables).
All the Isolate::Scope class does is call isolate::Enter() in the constructor and isolate::Exit() in the destructor. Therefore if you want more control you can call Enter()/Exit() yourself.

.NET GC stuck on JNI call from finalizer()

I have a .NET application that is using JNI to call Java code. On the .NET finalizer we call a JNI call to clean the connected resource on Java. But from time to time this JNI gets stuck.
This as expected stuck the all .NET process and never releases.
Bellow you can see the thread dump we got from .NET:
NET Call Stack
Function
.JNIEnv_.NewByteArray(JNIEnv_*, Int32)
Bridge.NetToJava.JVMBridge.ExecutePBSCommand(Byte[], Int32, Byte[])
Bridge.Core.Internal.Pbs.Commands.PbsDispatcher.Execute(Bridge.Core.Internal.Pbs.PbsOutputStream, Bridge.Core.Internal.DispatcherObjectProxy)
Bridge.Core.Internal.Pbs.Commands.PbsCommandsBundle.ExecuteGenericDestructCommand(Byte, Int64, Boolean)
Bridge.Core.Internal.DispatcherObjectProxy.Dispose(Boolean)
Bridge.Core.Internal.Transaction.Dispose(Boolean)
Bridge.Core.Internal.DispatcherObjectProxy.Finalize()
Full Call Stack
Function
ntdll!KiFastSystemCallRet
ntdll!NtWaitForSingleObject+c
kernel32!WaitForSingleObjectEx+ac
kernel32!WaitForSingleObject+12
jvm!JVM_FindSignal+5cc49
jvm!JVM_FindSignal+4d0be
jvm!JVM_FindSignal+4d5fa
jvm!JVM_FindSignal+beb8e
jvm+115b
jvm!JNI_GetCreatedJavaVMs+1d26
Bridge_NetToJava+1220
clr!MethodTable::SetObjCreateDelegate+bd
clr!MethodTable::CallFinalizer+ca
clr!SVR::CallFinalizer+a7
clr!WKS::GCHeap::TraceGCSegments+239
clr!WKS::GCHeap::TraceGCSegments+415
clr!WKS::GCHeap::FinalizerThreadWorker+cd
clr!Thread::DoExtraWorkForFinalizer+114
clr!Thread::ShouldChangeAbortToUnload+101
clr!Thread::ShouldChangeAbortToUnload+399
clr!ManagedThreadBase_NoADTransition+35
clr!ManagedThreadBase::FinalizerBase+f
clr!WKS::GCHeap::FinalizerThreadStart+10c
clr!Thread::intermediateThreadProc+4b
kernel32!BaseThreadStart+34
I have no idea whether .NET finalizers are equally bad idea to Java finalizers, but using a potentially (dead)locking code (i see Win32 condition call at the very bottom) from anything like finalizer (regardless of the platform) is definitely a bad idea. You need to clean your native code of any potential locking, or have an emergency brake timeout at the level of .NET
As I didn't find a question I won't post a formal answer here but rather tell a story about something similar I underwent sometimes:
We created C ojects via JNI, that were backed by java object, and we decided to clean the C objects within the finalize method. However, we envisioned deadlocks, as the finalize is called from a non-application thread, the garbage-collector. As the entire wolrd is stopped while collecting the garbage, whenever the finalizer meets a lock it's immediately a dead lock. Thus we decided to use a java mechnism called phantom references. It's possible to bind a number to each of these 'references' (the C pointer) and then the VM removes an referenced object it puts such an reference into a queue. And one can pull this data whenever appropriate and remove the C object.
I think at least your problem is the same.

Categories