I am writing a program that uses JNI to interface with a simple c program. I have created the following programs:
public static void main(String[] args) {
Hello h = new Hello();
System.out.println("before");
int number = h.sayHello();
System.out.println(number);
System.out.println("after");
}
and
JNIEXPORT int JNICALL Java_Hello_sayHello (JNIEnv *env, jobject obj) {
printf("Hello JNI\n");
return 10;
}
To my surprise this program returns:
before
10
after
Hello JNI
To me this is very strange because it is clear that the c program is executed between the "before" and "after" statement (The number 10 is printed). But why is the printf statement not executed when it is called. Is it somehow blocked by the jvm because only one program is allowed to write to the output at the same time? Is there a way to correct this behavior?
Yes. You will need to call flush.
In C, that is the fflush call -
printf("Hello JNI\n");
fflush(stdout);
return 10;
In Java, that is just flush on the Stream -
System.out.println("before");
System.out.flush();
Related
Calling get or set methods crashes. Object array is valid. It prints till line 2.Intially was getting error while inserting into the jobjectArray .Also tried with getting the value from Jobject .Both getter and setters fail .
JNICode is as below :
JNIEXPORT jint JNICALL Java_demo_JNIWrapper_pax_1store_1get_1data_1avail_1info
(JNIEnv *env, jclass jclass1, jobjectArray jobj)
{
.....
.....
int len = (*env)->GetArrayLength(env, jobj);
printf ("Incoming object array length = %d\n", len);// - - - > Works.. shows 2 (I am passing 2 objects)
jobject j = (*env)->GetObjectArrayElement(env, jobj,0);
printf("This line 1 \n ");
jmethodID meth1=(*env)->GetMethodID(env,jclass1,"getTimestamp","()Ljava/lang/String;");
printf("This line 2 \n "); // - - - - > Works..
jstring string_from_obj = (*env)->CallObjectMethod(env,j,meth1); // - - - - > Crashes..
printf("This line 3 \n "); // - - -> does not print this..
printf("Contents are =%s\n",(*env)->GetStringUTFChars(env, string_from_obj, 0));
............
}
My Java Object is as Below :
public class DataAvailable {
String timestamp;
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
}
Call to JNI is as below :
public native int pax_store_get_data_avail_info(DataAvailable[] stats_array);
The glaring issue is that pax_store_get_data_avail_info() is not a method of DataAvailable. It is a method of some other class? You don't actually say what it is. But let's suppose it is:
class X {
public native int pax_store_get_data_avail_info(DataAvailable[] stats_array);
}
When you call this method, the jclass jclass1 argument you get is for "X", not for DataAvailable. So your call to GetMethodID() probably fails. You don't check the return, so you re probably handing a bad method ID to the next call. JNI fails are hard crashes and very ugly.
You need to query the jclass for DataAvailable and use that.
In a Java project that uses C code via JNI I have a piece of native C code that obtains references to an object and one of its methods, then starts a native thread, passing these references to it in a struct. When the thread tries to call the method, the code crashes with a SIGSEGV. Calling that same method from the main thread works.
Doing some research I learned that the env reference is only valid within the thread and that any other native thread must be attached first. I did this but the code still crashes on the first call to the method.
Strangely, when I call the same method from the main thread before I create the other thread (just uncomment the line in the main code), things work for some time. The output thread loops for some 10,000 times before it crashes.
The method call is to DataOutputStream.writeShort(). The thread in question is the only one writing to the DataOutputStream. However, the DataOutputStream is connected to a DataInputStream.
Simplified native code:
static void write_output(struct output_state *s) {
int i;
jint sample;
for (i = 0; i < 2 * s->result_len; i += 2) {
sample = (s->result[i] << 8) + s->result[i+1];
(*(s->env))->CallVoidMethod(s->env, s->tunerOut, s->writeShort, sample);
}
}
static void *output_thread_fn(void *arg)
{
struct output_state *s = arg;
(*(s->jvm))->AttachCurrentThread(s->jvm, &(s->env), NULL);
while (!do_exit) {
// use timedwait and pad out under runs
safe_cond_wait(&s->ready, &s->ready_m);
pthread_rwlock_rdlock(&s->rw); //sync access to s with producer thread
write_output(s);
pthread_rwlock_unlock(&s->rw);
}
(*(s->jvm))->DetachCurrentThread(s->jvm);
return 0;
}
JNIEXPORT jboolean JNICALL Java_eu_jacquet80_rds_input_SdrGroupReader_open
(JNIEnv *env, jobject self) {
jclass clsSelf = (*env)->GetObjectClass(env, self);
jfieldID fTunerOut = (*env)->GetFieldID(env, clsSelf, "tunerOut", "Ljava/io/DataOutputStream;");
jobject tunerOut = (*env)->GetObjectField(env, self, fTunerOut);
jclass cls = (*env)->GetObjectClass(env, tunerOut);
jmethodID writeShortID = (*env)->GetMethodID(env, cls, "writeShort", "(I)V");
if (!writeShortID || !cls)
return 0;
(*env)->GetJavaVM(env, &(output.jvm));
output.tunerOut = tunerOut;
output.writeShort = writeShortID;
// (*env)->CallVoidMethod(env, tunerOut, writeShortID, 0); // just for testing
pthread_create(&controller.thread, NULL, controller_thread_fn, (void *)(&controller));
usleep(100000);
pthread_create(&output.thread, NULL, output_thread_fn, (void *)(&output));
pthread_create(&demod.thread, NULL, demod_thread_fn, (void *)(&demod));
pthread_create(&dongle.thread, NULL, dongle_thread_fn, (void *)(&dongle));
return 1;
}
Are JNI references such as jclass, jfieldID, jobject and jmethodID subject to the same limitations as JNIEnv, i.e. valid only within the same thread?
Suspecting this, I moved the JNI reference stuff from open() to output_thread(), right after the call to AttachCurrentThread(). However, I still need to pass a jobject reference (self) across thread borders, and the call to GetObjectClass() crashes.
What is the correct way to create a thread native code and have that thread call a particular method of a given class instance?
Turns out my suspicion was correct: jobject and jclass references are indeed local, i.e. valid only within the same thread and only until the current native method returns. See http://developer.android.com/training/articles/perf-jni.html#local_and_global_references.
My approach of moving the reference-related code to the thread function was correct, except that I need to first convert self into a global reference by calling NewGlobalRef().
Updated code:
static void write_output(struct output_state *s) {
int i;
jint sample;
for (i = 0; i < 2 * s->result_len; i += 2) {
sample = (s->result[i] << 8) + s->result[i+1];
(*(s->env))->CallVoidMethod(s->env, s->tunerOut, s->writeShort, sample);
}
}
static void *output_thread_fn(void *arg)
{
struct output_state *s = arg;
(*(s->jvm))->AttachCurrentThread(s->jvm, &(s->env), NULL);
jclass clsSelf = (*(s->env))->GetObjectClass(s->env, s->self);
jfieldID fTunerOut = (*(s->env))->GetFieldID(s->env, clsSelf, "tunerOut", "Ljava/io/DataOutputStream;");
s->tunerOut = (*(s->env))->GetObjectField(s->env, s->self, fTunerOut);
jclass cls = (*(s->env))->GetObjectClass(s->env, s->tunerOut);
s->writeShort = (*(s->env))->GetMethodID(s->env, cls, "writeShort", "(I)V");
while (!do_exit) {
// use timedwait and pad out under runs
safe_cond_wait(&s->ready, &s->ready_m);
pthread_rwlock_rdlock(&s->rw); //sync access to s with producer thread
write_output(s);
pthread_rwlock_unlock(&s->rw);
}
(*(s->jvm))->DetachCurrentThread(s->jvm);
return 0;
}
JNIEXPORT jboolean JNICALL Java_eu_jacquet80_rds_input_SdrGroupReader_open
(JNIEnv *env, jobject self) {
jclass clsSelf = (*env)->GetObjectClass(env, self);
if (!writeShortID || !cls)
return 0;
output.self = (*env)->NewGlobalRef(env, self);
(*env)->GetJavaVM(env, &(output.jvm));
(*env)->CallVoidMethod(env, tunerOut, writeShortID, 0); // just for testing
pthread_create(&controller.thread, NULL, controller_thread_fn, (void *)(&controller));
usleep(100000);
pthread_create(&output.thread, NULL, output_thread_fn, (void *)(&output));
pthread_create(&demod.thread, NULL, demod_thread_fn, (void *)(&demod));
pthread_create(&dongle.thread, NULL, dongle_thread_fn, (void *)(&dongle));
return 1;
}
One thing still missing is a call to DeleteGlobalRef() when the output thread is done. This is to make sure the global reference is released when it is no longer needed, so that the garbage collector can pick it up.
I'm using a call native code from a Java (vr1.7) application (as the code below), from a GUI with swing.
public class ImageProcessContainer {
static {
System.load("c:\\ImageProcDLL.dll");
}
native public int processImage(String filename, String args);
public int execute(String filename, String args){
return processImage(filename, args)
}
}
When I call the first time, the code works perfectly. From the second call before the code runs but the C variables continue with data from the previous session!
Why does this happen?
The variables in C++ code are not static! Fragment code C++ below. The global variables are kept initialized.
For example: variables corner1 and corner2 (both globals) its some of continue initialized
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <time.h>
//Some others includes
#include "ImageProc.h"
using namespace std;
using namespace cv;
Mat frameFeed;
Point corner1, corner2;
Rect box;
.
.
//some other globals
JNIEXPORT jint JNICALL _processImage (JNIEnv * env, jclass, jstring filename, jstring buffer){
return processVideo(NULL, NULL);
}
int processVideo(char * filename, char * buffer) {
namedWindow("Video Process");
//Initializing some local variables
int inc = 0;
ImageProc * imageproc = NULL;
.
.
//some code here
while(capture.read(frameFeed) && !terminateApp){
if (startImageProc) {
if (!imageproc) {
int securityArea = 10;
Point p1, p2;
p1.x = corner1.x;
p1.y = corner1.y;
p2.x = corner2.x;
p2.y = corner2.y;
imageproc = new ImageProc(1, box, p1, p2);
}
if (initParameters){
//Some code Here
initParameters = false;
} else {
imageproc->analiseMoment(frameFeed);
}
}
.
.
//some code here
return 1;
}
To solve the problem just make the global variables into local variables. Pretty simple. The problem is that once the dll is loaded into memory all the global variables with the values are populated with data from the last call. [sugested by #doctorlove in comments].
I´m trying to call java methods from simulink Matlab using JNI. I developed a small code in C++ that calls a main method which only prints helloworld on screen as a first test but at the line that it calls to find the class, matlab crashes.
The C++ code is this :
#include <stdio.h>
#include <jni.h>
#include "mex.h"
class MatlabAmbassador {
public:
MatlabAmbassador ();
//Destructor
~MatlabAmbassador ();
void run();
JNIEnv* create_vm() ;
void invoke_class(JNIEnv* env);
private:
}; // end class
MatlabAmbassador::MatlabAmbassador() {
}
MatlabAmbassador::~MatlabAmbassador() {
}
// -------------------------------------------------------------------------
JNIEnv* MatlabAmbassador::create_vm() {
JavaVM* jvm;
JNIEnv* env;
JavaVMInitArgs args;
JavaVMOption options[1];
long status;
/* There is a new JNI_VERSION_1_4, but it doesn't add anything for the purposes of our example. */
args.version = JNI_VERSION_1_6;
args.nOptions = 1;
options[0].optionString = "-Djava.class.path=C:\\Apps\\Projetos em java\\Portico\\Portico_Agent\\bin";
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
status=JNI_CreateJavaVM(&jvm, (void **)&env, &args);
return env;
}
// -------------------------------------------------------------------------
void MatlabAmbassador::invoke_class(JNIEnv* env) {
jclass helloWorldClass;
jmethodID mainMethod;
jobjectArray applicationArgs;
jstring applicationArg0;
mexPrintf("First\n");
helloWorldClass = env->FindClass("Teste"); <--- MATLAB CRASHES HERE
mexPrintf("second\n");
if (env->ExceptionOccurred()) {
env->ExceptionDescribe();
env->ExceptionClear() ;
}
if ( helloWorldClass == NULL ) {
mexPrintf( "%s%s\n", "Unable to obtain class reference for ",
helloWorldClass );
return;
} else {
mexPrintf( "%s%s\n", "Sucessfully created class reference for ",
helloWorldClass );
}
mainMethod = env->GetStaticMethodID( helloWorldClass, "main", "([Ljava/lang/String;)V");
applicationArgs = env->NewObjectArray(1, env->FindClass("java/lang/String"), NULL);
applicationArg0 = env->NewStringUTF( "From-C-program");
env->SetObjectArrayElement( applicationArgs, 0, applicationArg0);
env->CallStaticVoidMethod( helloWorldClass, mainMethod, applicationArgs);
}
// -------------------------------------------------------------------------
void MatlabAmbassador::run() {
char str [80];
mexPrintf(" INITIALIZING....\n" );
JNIEnv* env = create_vm();
invoke_class( env );
}
// -------------------------------------------------------------------------
Simulink Matlab has some methods to be used. One of this method is used to call the method run() described above. Here follows a piece of code:
static void mdlStart(SimStruct *S)
{
char *buf;
size_t buflen;
int status;
buflen = mxGetN((ssGetSFcnParam(S, 2)))*sizeof(mxChar)+1 ; // le o 3o parametro passado pela funcao (nome do arq)
buf = (char *)mxMalloc(buflen); // aloca memoria
status = mxGetString((ssGetSFcnParam(S, 2)), buf,(mwSize)buflen);
ssGetPWork(S)[0] = (void *) new MatlabAmbassador; // store new C++ object in the
MatlabAmbassador *c = (MatlabAmbassador *) ssGetPWork(S)[0];
c->run();
and the simple java code is:
public class Teste
{
public static void main(String[] args)
{
System.out.println(" INITALIZING...");
}
}
So, can anyone explain what am I missing or explain if there is a real problem calling JNI inside Matlab. Matlab is version 2011b and java version installed is JDK 1.0.6_45.
I´d appreciate any help as soon as possible.
Best regards
André Nudel
andre.nudel#gmail.com
Are you sure JNI_CreateJavaVM succeeds? Your code is not checking the returned status code or resulting env value (which is not getting initialized, so it may contain garbage), and the crash is apparently occurring at the first attempt to use env.
If you're running this inside Matlab, the JVM creation may be failing because there's already a JVM running inside the process as part of Matlab's normal environment. The JNI documents say that "creation of multiple VMs in a single process is not supported" (http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/invocation.html#wp636). If this is what's happening, JNI_CreateJavaVM will return JNI_EEXIST (-5). Even if that's not what's going on here, it's good practice to check the status codes returned from functions that can fail. The examples in the JNI documentation omit error checking "for clarity", but you should include it in code you're actually going to run.
Check the status returned by JNI_CreateJavaVM and print it out to make sure it's succeeding. And maybe initialize env to 0 so it's clear whether you got a pointer from JNI or just random data.
I am not very familiar with Simulink, so I'm showing an example of a regular MEX-function instead.
As suggested by #AndrewJanke, I retrieve the existing JVM instance running inside the MATLAB process instead of creating a new one.
jni.cpp
#include "mex.h"
#include "jni.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
// get MATLAB Java virtual machine instance
JavaVM *vm = NULL;
int n; jint res;
res = JNI_GetCreatedJavaVMs(&vm, 1, (jsize*) &n);
if (res != JNI_OK || n != 1) {
mexErrMsgIdAndTxt("mex:jni", "Couldn't find existing Java VM");
}
// get JNI interface instance
JNIEnv *env = NULL;
res = vm->GetEnv((void**) &env, JNI_VERSION_1_6);
if (res != JNI_OK || env == NULL) {
mexErrMsgIdAndTxt("mex:jni", "Couldn't get Java JNI environment");
}
mexPrintf("using Java %d.%d\n",
env->GetVersion() >> 16, env->GetVersion() & 0xFFFF);
}
I compiled the file using:
mex -I"C:\Program Files\Java\jdk1.6.0_45\include"
-I"C:\Program Files\Java\jdk1.6.0_45\include\win32" jni.cpp
"C:\Program Files\Java\jdk1.6.0_45\lib\jvm.lib"
Run in MATLAB, I get:
>> jni
using Java 1.6
You should be able to extend the example above to call an external Java class. Note that I have not been able to pass data between MATLAB MEX-API and Java JNI API (by that I mean passing Java objects as they are to a MEX-function, or the other way around, passing some kind of jobject back directly as is to MATLAB, without conversion that is), as in:
>> x = java.lang.Double(1)
>> my_mex_jni_fcn(x)
The problem is that mxArray is an opaque type, so I do not know how to interpret the pointer returned by mxGetData
I've spent whole day on this problem and still have no idea how to solve it.
Here is the simplified code
JAVA
class javaclass{
private volatile boolean isTerminated;
public void javamethod()
{
log.logInfo("java :"+isTerminated());
}
public int isTerminated()
{
return (isTerminated) ? 1 : 0;
}
public doJob()
{
executeNative();
}
private native int executeNative() throws Exception;
}
C++
bool cmethod()
{
cerr << "JNI " << wrapper::isTerminated() << "\n";
if(wrapper::isTerminated)
return false;
jni->CallVoidMethod(self, apiJavamethod, xPoint);
return true;
//apiJavamethod is jMethodId of javamethod
}
int wrapper::isTerminated()
{
return jni->CallIntMethod(self, apiIsTerminated);
}
JNIEXPORT jint JNICALL NAMESPACE_javaclass_executeNative(JNIEnv* env, jobject self) {
for(int i=0;i < _ITERATIONS;++i)
{
if(!cmethod())
break;
}
}
C++ cmethod is executed in some loop, which stops when C++ catches isTerminated().
In output i see:
....
....
in log:
java 0
java 1
java 1
java 1
in console:
JNI 0
JNI 0
JNI 0
JNI 1
So, JNI is somehow behind the real state of variable.
It produces an important bug in application (((
Maybe it's some problem with java itself? (v.1.5, i forced to used it)
Any help is greatly appreciated.
I would suggest doing all the synchronization in one language or the other. It's looking like "volatile" isn't being respected across the boundary for some reason. Something like:
public doJob()
{
while(!isTerminated)
executeNative();
}