0

thread 1 is main thread. I have a c++ code that executes in thread 2 ( my worker thread that I have spawned ). I would like to execute a native function in main thread and wait for the result

printf("I am on thread : %s", getthreadid());
int ret = executeOnMainWait(mynativefunc1("hello"));
printf("ret : %d", ret);
printf("I am on thread : %s", getthreadid());
bool b = executeOnMainWait(mynativefunc2(4, 5));
printf("b : %d", b);

int mynativefunc1(char* param) {
    printf("mynativefunc1 I am on thread : %s", getthreadid());
    if(strcmp(param, "hello")) {
        return 1;
    }
    return 2;
}

bool mynativefunc2(int val1, int val2) {
    printf("mynativefunc2 I am on thread : %s", getthreadid());
    return (val1 + val2) == 5;
 }

so this code should display :

I am on thread 2
mynativefunc1 I am on thread 1
ret : 1
I am on thread 2
mynativefunc2 I am on thread 1
b : true

I think we need to go to the java world through jni and use the handler and post something to main thread then wait it is complete, but the problem I don't know how to pass a function pointer directly with its own parameters. this example just show 2 native functions, but in reality I have 20. many thanks

windev92
  • 1
  • 1
  • Does this answer your question? [How do you properly synchronize threads on the native side of a JNI environment?](https://stackoverflow.com/questions/44420937/how-do-you-properly-synchronize-threads-on-the-native-side-of-a-jni-environment) – emandt Mar 15 '21 at 14:03
  • no, this is synchronisation with mutexes. What I need to passing a lambda from c++ to java and wait(by using a semaphore) – windev92 Mar 15 '21 at 14:09
  • Java cannot call a lambda but can call a JNI method. So you have to define a SPECIFIC C++ method, then call Java with your normal arguments and when Java has finished it will call THAT specific C++ method. It seems you already know about Mutex/Semaphore so I will not write you about them. – emandt Mar 15 '21 at 14:29
  • thanks, and what do you think I need to do to define this SPECIFIC c++ function that takes variable arguments and return a variable return code? – windev92 Mar 15 '21 at 14:34
  • Just call (from Java) this "SPECIFIC C++ method" as you're calling current C++ method that runs code you have posted here. Something similar to "JNIEXPORT JNICALL jint Java_com_your_package_cppMethod(env, clazz, arg1, arg, ....) { ... }" and in this method you will use Mutex/Semaphore for Thread Syncing. – emandt Mar 15 '21 at 14:47
  • Of course. The first used for launch C++ Thread and the second one as "Callback" from Java. – emandt Mar 15 '21 at 14:57
  • However pay attention that Java is running in a DIFFERENT thread, so calling Java method from your Secondary Thread (the one created from C++) will need a call to "jvm->AttachCurrentThread" ("Attaching to the VM": https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html) – emandt Mar 15 '21 at 15:02
  • I don't know how to do this without creating 2 java functions Func1(String param) and Func2(final int val1, final int val2) that calls Java_com_your_package_mynativefunc1 and Java_com_your_package_mynativefunc2. – windev92 Mar 15 '21 at 15:07
  • otherwise I need to cache the params and the command to execute with an enum in c++ code. when java executes the single function on main thread, I need to find the params to use and the command to execute. – windev92 Mar 15 '21 at 15:09

1 Answers1

0

How to do (some parts are pseudo-code):

Java code:
  public native void startSecondThread();
  public native void fakeCallbackFromJava();
  public native void fakeCallbackFromJava2();
  public void blockingMethod(int chooseCallbackMethod, @Nullable Object obj, @Nullable String string) {
    final Object syncObj = new Object();
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
          ...run Java code here in UiThread...
         ...here you can use OBJ and STRING arguments...
            //call callbacks according to Argument
          if (chooseCallbackMethod == 1) fakeCallbackFromJava();
          else if (chooseCallbackMethod == 2) fakeCallbackFromJava2();
          syncObj.notify();
        }
    });
    syncObj.wait();
      //we arrive HERE only after "syncObj.notify()" is called
  }
C++ Thread code:
  void foo() {
    JNIEnv *env;
    jvm->AttachCurrentThread((void **)&env, NULL);
    ..."env" is currently synced object that could be used for Java calls...
    ...all following rows depends of what you want to call in Java...
    jclass class = env->GetObjectClass(...);
    jmethod method = env->GetMethodID(class, "blockingMethod", "ILjava/lang/Object;Ljava/lang/String;");
      //first call
    env->CallVoidMethod(method, 1, null, env->NewStringUTF("hello"));
      //(you will arrive HERE only after "blockingmethod(1,null," hello")" is finished from Java UiThread)
      //second call
    env->CallVoidMethod(method, 2, null, null);
      //finishing second thread
    jvm->DetachCurrentThread();
  }
C++ starting Thread:
  #include <thread>
  JavaVM *jvm;
  JNIEXPORT JNICALL void Java_com_your_package_startSecondThread(JNIEnv* env, jclass clazz) {
    env->GetJavaVM(&jvm);
    std::thread first (foo);
    first.join();
  }
C++ Callback:
  JNIEXPORT JNICALL void Java_com_your_package_fakeCallbackFromJava(JNIEnv* env, jclass clazz) {
    ...code called from Java MainThread/UiThread as CALLBACK for arguments: 1,null,"hello"...
  }
  JNIEXPORT JNICALL void Java_com_your_package_fakeCallbackFromJava2(JNIEnv* env, jclass clazz) {
    ...code called from Java MainThread/UiThread as CALLBACK for arguments: 2,null,null...
  }
emandt
  • 2,547
  • 2
  • 16
  • 20
  • thanks but I don't think it answers the problem :( – windev92 Mar 15 '21 at 16:17
  • the problem is more like how java calls mynativefunc1 and mynativefunc2 with their different parameters. To be more precise, I am using a 3rd party lib, and I reach at some point c++ code in a worker thread. From this point I need to execute on main thread and syncrhonously mynativefunc1 with a string param and wait it completes and return the error code to the 3rd party lib. then the 3rd party lib wants me to execute on main thread mynativefunc2 with val1 and val2 and wants me to return him the result as boolean. KR – windev92 Mar 15 '21 at 16:27
  • What do you intend for Thread1 and Thread2? My code considered Thread1 as Android MainThread/UiThread (which should not be blocked) and Thread2 as a secondary Thread that parallelize calls to T1. If you just want to serialize them you can convert "nonBlockingMethod()" to "blockingMethod()" by calling a second "env->CallVoidMethod(method, ...);" after the other one, in this way each Java "blockingMethod()" should be terminated before the next one is executed. Obliviously each "blockingMethod()" should call a different "fakeCallbackFromJava()" at its ends. – emandt Mar 15 '21 at 16:42
  • I need to block the main thread (Ui thread) because mynativefunc1 and mynativefunc2 perform UI related code. I am not sure that you really grasped the exact reason of my question. the example I have written that thread1 = main, and thread2 is the worker. if I call nonBlockingMethod with CallVoidMethod, how can Java know what function to call? and how do we pass the parameters. the flow is: 1. I am in worker thread 2 in c++. 2. I need to exec mynativefunc1("hello") in main thread and wait that it returns 1. 3. I return this error code to the 3rd party lib – windev92 Mar 15 '21 at 16:53
  • Just put an Argument to "(non)BlockingMethod()" to choose (in Java) what to do according to that Argument, in this way that Java method can choose which "fakeCallback()" method to call at its end. However to WAIT between those 2 Java calls you just convert the NoBlockingMethod to BlockingMethod like I said before but you have to add a Mutex/Semaphore even in Java after "AsyncTask.execute()" (I'm changing my answer now) – emandt Mar 15 '21 at 17:04
  • and how do I pass this Argument (it needs to be a kinda enum or so?). And how to I pass the other arguments... I need 1 string for the mynativefunc1 and 2 int for the mynativefunc2. and then I need to expose all native functions to java? – windev92 Mar 15 '21 at 17:11
  • I edited again the example. It seems you are not so much used to JNI, so I suggest to try something simple in JNI and THEN trying multithreading. – emandt Mar 15 '21 at 17:17
  • the problem here is the variable arguments, we use in c++ VA_ARGS and myfunc(a, ...). in your example you call blockingMethod with the function id ( which I have understood from your previous message). how do you call blockingMethod(1, "hello") in the first case, and blockingMethod(2, 44, 55). :-) – windev92 Mar 15 '21 at 17:23
  • I wrote you that you need to understand how call Java methods from JNI. I edited again the code adding more argument types. You can create as many Java "blockingMethod()" you want (overloading is supported by JNI) with different arguments. Just do the right GetMethodID() from C++ for each of them. Sorry but I cannot continue to help you if you don't want to read a tutorial on "How call Java methods", because all your doubts will be solved during the read of tutorial/guide/examples. – emandt Mar 15 '21 at 18:30
  • I know how to call java methods even with objects, the problem is not really there. The solution with your object needs to serialize the parameters and then check from the id which c++ function to call. It will make the code too big and unboxing the parameters to be passed to the functions. the solution is here : https://stackoverflow.com/questions/13108663/storing-functions-call-and-list-of-parameters-to-invoke-later. I will have one java func ExecMainBlocking() and expose just one c++ func in c++ : Jni_exec_handler(). – windev92 Mar 15 '21 at 19:10
  • i need to save mynativefunc1 in c++ as std::function and call ExecMainBlocking which will call Jni_exec_handler() in main thread. from here I grab the functor I saved and execute it, and return the error code to the 3rd party lib – windev92 Mar 15 '21 at 19:10
  • NO, you can create "blockingMethod1(int, string, byte[])" and "blockingMethod(string, string, string)" and "blockingMethod(long, float, boolean)", and etc... and each of these methods can call its relative "fakeCallbackFromJava...()" method. This method could be changed in any way you need. It's not difficult to imagine all the ways this method allows to achieve.... – emandt Mar 15 '21 at 20:24
  • Or just create a single "blockingMethod(Object[] objectArray)" and put how many args you want from C++ to Java....but I discourage to use this because difficult to mantain. – emandt Mar 15 '21 at 20:26
  • why doing this if I can do everything in c++ with the std::bind? you think it cannot work? my original idea was to do something like blockingMethod(Object[] objectArray) but it will be boxing types and unboxing + a switch case. the first option you are talking about is not viable with too many functions ( i have 20 and it will grow). KR – windev92 Mar 15 '21 at 22:08