Search code examples
javacmultithreadingjava-native-interfacefortran

Calling Java from Fortran using JNI, multithreading


I am trying to pass an array from Fortran to Java, make some computations in Java and return the value to my Fortran program. I am using JNI to call Java from Fortran. I wrote a sample program and my code is as follows:

main.f95:

PROGRAM MAIN
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER, C_INT, C_DOUBLE
USE array_example
IMPLICIT NONE
INTERFACE
  FUNCTION  obj(c, c_size) RESULT(f) BIND(C, NAME='obj')
    USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_DOUBLE, C_PTR, C_INT
    TYPE(C_PTR), INTENT(IN), VALUE :: c
    INTEGER(C_INT), INTENT(IN):: c_size
    REAL(C_DOUBLE):: f
  END FUNCTION
END INTERFACE

INTEGER :: x, y
x=5

CALL example(obj,x)

END PROGRAM MAIN

mod.f95:

MODULE array_example
CONTAINS
SUBROUTINE example(obj, N)
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER, C_INT, C_DOUBLE, C_LOC
IMPLICIT NONE
INTEGER, INTENT(IN) :: N
REAL(C_DOUBLE) :: res
INTEGER :: j
INTEGER(C_INT) :: flag

INTERFACE
  FUNCTION  obj(c, c_size) RESULT(f) BIND(C, NAME='obj')
    USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_DOUBLE, C_PTR, C_INT
    IMPLICIT NONE
    TYPE(C_PTR), INTENT(IN), VALUE :: c
    INTEGER(C_INT), INTENT(IN):: c_size
    REAL(C_DOUBLE):: f
  END
END INTERFACE

REAL(C_DOUBLE), TARGET :: c_array(1:N)
TYPE(C_PTR) :: cptr

cptr = c_loc(c_array(1))

c_array(1:N)=(/2.6179917, 1.570795, 1.570795, 1.570795, 1.570795/) 

res = obj(cptr, N)

PRINT *, res

END SUBROUTINE
END MODULE

objFuncC.c

#include <stdio.h>
#include <stdlib.h>
#include <jni.h>  

extern double obj(double *, int *);

JNIEnv* create_vm(JavaVM **jvm)
{
    JNIEnv* env;
    JavaVMInitArgs args;
    JavaVMOption options;
    args.version = JNI_VERSION_1_6;
    args.nOptions = 1;
    options.optionString = "-Djava.class.path=./";
    args.options = &options;
    args.ignoreUnrecognized = 0;
    int rv;
    rv = JNI_CreateJavaVM(jvm, (void**)&env, &args);
    if (rv < 0 || !env)
        printf("Unable to Launch JVM %d\n",rv);
    else
        printf("Launched JVM successfully\n");
    return env;
}


double obj(double *ptr, int *c_size)
{
    JavaVM *jvm;
    JNIEnv *env;
    jdouble *dptr;
    jdoubleArray newArray;
    jint size;
    double result;


    size = *c_size;
    dptr = (double *)ptr;

    env = create_vm(&jvm);
    if(env == NULL)
        return 1;
    newArray = (*env)->NewDoubleArray(env, size);

     (*env)->SetDoubleArrayRegion(env, newArray, 0, size, (const jdouble*)dptr); 

    jclass objFuncJ_class;
    jmethodID main_method;
    jmethodID evaluate_method;

    objFuncJ_class = (*env)->FindClass(env, "objFuncJ"); 
    evaluate_method = (*env)->GetStaticMethodID(env, objFuncJ_class, "evaluate", "([D)D"); 
    result = (*env)->CallStaticDoubleMethod(env, objFuncJ_class, evaluate_method, newArray);

    printf("Result = %f\n",result);
    return result;

}

objFunc.java

import java.io.*;
import java.lang.*;

public class objFuncJ
{
    public static double evaluate(double[] position) 
    {
      int m = 10;
      double sum = 0.0;
      for (int i = 1; i <= position.length; i++) 
      {
        double xi = position[(i - 1)];
        double pow = 1.0;
        double xiPow = Math.sin(i * (xi * xi) / Math.PI);
        for (int j = 1; j <= (2 * m); j++) 
        {
          pow *= xiPow;
        }
        sum += Math.sin(xi) * pow;
        }
        return -sum;
    }
}

I get the following output:

Launched JVM successfully
Result = -1.011206
Unable to Launch JVM -5
Unable to Launch JVM -5
Unable to Launch JVM -5
1.0000000000000000   

The value in 'Result' is the correct output. However,multiple executions fail. How do I change my code to handle multiple evaluations of the function and create the Java JVM only once?


Solution

  • Okay I figured where I was going wrong. I changed objFuncC.c as follows:

    extern double obj(double *, int *);
    extern int init(); 
    static JavaVM *jvm;
    
    JNIEnv* create_vm(JavaVM **jvm)
    {
        JNIEnv* env;
        JavaVMInitArgs args;
        JavaVMOption options;
        args.version = JNI_VERSION_1_6;
        args.nOptions = 1;
        options.optionString = "-Djava.class.path=./";
        args.options = &options;
        args.ignoreUnrecognized = 0;
        int rv;
        rv = JNI_CreateJavaVM(jvm, (void**)&env, &args);
        if (rv < 0 || !env)
            printf("Unable to Launch JVM %d\n",rv);
        else
            printf("Launched JVM successfully\n");
        return env;
    }
    
    int init() 
    {
        JNIEnv *env_init;
        env_init = create_vm(&jvm);
        if(env_init == NULL)
          return 1;
    }
    
    double obj(double *ptr, int *c_size)
    {
        JNIEnv *env;
        jdouble *dptr;
        jdoubleArray newArray;
        jint size;
        double result;
    
        size = *c_size;
        dptr = (double *)ptr;
    
        jint rs = (*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL);
        newArray = (*env)->NewDoubleArray(env, size);
    
        (*env)->SetDoubleArrayRegion(env, newArray, 0, size, (const jdouble*)dptr); 
    
        jclass objFuncJ_class;
        jmethodID main_method;
        jmethodID evaluate_method;
    
        objFuncJ_class = (*env)->FindClass(env, "objFuncJ"); 
        evaluate_method = (*env)->GetStaticMethodID(env, objFuncJ_class, "evaluate", "([D)D"); 
        result = (*env)->CallStaticDoubleMethod(env, objFuncJ_class, evaluate_method, newArray);
    
        printf("Result = %f\n",result);
        return result;
    }
    

    I added an interface for function init in my Fortran program (mod.f95) and called init() before calling obj(double *, int *). It works fine and I get the following output:

    Launched JVM successfully
    Result = -1.011206
    Result = -1.011206
    Result = -1.011206
    Result = -1.011206
      -1.0112061116510644  
    

    Thanks for your help :)