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.
Related
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 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.
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.
Say I have a very simple Android app that has just one activity - the activity displays a plain screen. Lets say I have overridden the onCreate() method of the activity and it simply sets the screen as described in activity_main.xml and then returns as shown below:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//boolean BT_success = turnBluetoothOn();
}
In a desktop Java (or C or Python) program, execution starts at the "main" method/function and the program finishes executing when main has finished executing (and once all the functions called by main have returned). In this simple app described above, when the common set of callback functions like onCreate(), onStart() and onResume() are finished executing, is there any part of my code that is executing?
In this sense, there is no "main" method, like in the case of desktop Java, C or Python, right? Even if we had a couple of buttons in this main screen, we would have callback functions for those buttons.
So is it fair to say that the Android API callback based in the sense that an app developer has to implement certain callback functions (and those can in turn call other functions)?
Not in the way Win32 is. For one thing, the Android flavor of Java doesn't readily accommodate the notion of a canned function; it has neither delegates (C#) nor functors (C++, Python) nor function pointers (C, C++).
The Android API is still event driven, like most GUI systems are; but the primary ways for you to provide hooks into your code to the framework are:
inheriting from library classes and then overriding functions that were meant to be overridden;
implementing abstract interfaces in your classes (possibly anonymous) and providing those objects to the framework.
This is, generally, the Java way.
EDIT: depends on your definition of callback :) Normally, when people say that, they mean a function. In this sense, it's not callback-based. But if you mean "the framework calls you whenever something interesting takes place", then yes, it is. Event-driven, like I said.
EDIT2: Preamble: C has function pointers. It's a datatype that you can initialize with a function name, pass around like a primitive value, and then call with arguments at some point down the road. The call will be received by the function that the pointer was initialized with originally.
Windows, like Android, is an event-driven GUI system. The event-driven nature of Windows is implemented mainly via said function pointers - you pass a pointer to your function to the framework, the framework calls it back when something interesting occurs. This was designed in mid-1980's, before the advent of object oriented languages.
Now, those functions that are meant to be called by the framework are referred to as "callback functions" or simply "callbacks". I come from the Windows background, so for me, "callback" primarily means "callback function". As opposed to Android-style callback objects (is that even a term?).
This is not dissimilar to the way any GUI application/framework is designed. Even Java Swing works in a similar fashion. You implement the "callbacks" that are hooked to UI control events, and your "main" function usually only serves to kick off the main event loop and exit. Note that here, when main() exits, the program itself does not exit.
To simply answer your question, yes it is based on callbacks. You specify which activity should be the starting point for your application in your androidmanifest.xml. This activity's onCreate() is called to initialize the layout. Every interaction that you perform on the screen triggers a callback (which you will override to implement what you need). But just because the main activity exits does not mean that the application will exit.
This is where you go into the activity lifecycle. All activities sit on the main thread in a stack like manner. When one activity is killed, you go to the next activity in the thread and so on. The application itself exits when all it's activities in this stack have been killed or Android decides to terminate it. Keep in mind that you might normally expect onDestroy() to be called when Android terminates the application but this is not the case - Android may or may not call onDestroy(). In this case, there was no callback for the exit.
I would say the Android API is heavily based on Extension. You almost always have to extend their Classes and override their methods.
super.onCreate(savedInstanceState);
Is a call to the Super Class method that would normally be hidden by you override. You are, however, free to call back to classes that provide the logic you need but you are not forced to. I recommend that you do, and that you put those Classes under UnitTest.
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.