JNI сохраняет ошибки? - PullRequest
1 голос
/ 16 февраля 2020

Я пишу код JNI, чтобы обернуть некоторые C функции. Некоторые из этих C функций установлены errno в случае ошибки. Мой код был бы проще, если бы я мог переместить обработку исключений из кода JNI в код Java. Мой план состоит в том, чтобы JNI-функция возвращала null в случае ошибки, а затем в Java коде запроса errno и создавала и выбрасывала исключение. Для этого JNI придется оставить errno нетронутым.

1 Ответ

3 голосов
/ 16 февраля 2020

На вашем месте я бы пошел за исключением как можно скорее

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/errno.h>
#include "recipeNo019_ThrowException.h"

JNIEXPORT void JNICALL Java_recipeNo019_ThrowException_throwException
  (JNIEnv * env, jobject obj) {

  char exceptionBuffer[1024];

  if(fopen("/this_will_fail", "r") == NULL) {

    sprintf (exceptionBuffer, "%d:%s", errno, strerror(errno));
    (*env)->ThrowNew (env,
                    (*env)->FindClass (env, "java/lang/Exception"),
                    exceptionBuffer);
  }

}

, а затем в коде Java я бы разложил код ошибки и сообщение об ошибке.

Затем внутри Java вы можете легко получить доступ к errno и message.


public static native void throwException();

...
...

try {
  throwException();  
} catch(Exception ex) {
  ex.getMessage();
}

Обновление

Обратите внимание, что при JNI вовлекаясь в игру, все может усложниться (особенно с общими данными - errno).

Вы можете представить себе один из сценариев ios - например:

public static class ErrnoGenerator implements Runnable {

  boolean shouldIFail = false;
  ThrowException exceptionGenerator = null;

  public ErrnoGenerator(
             ThrowException exceptionGenerator, 
             boolean shouldIFail) {
    this.exceptionGenerator = exceptionGenerator;
    this.shouldIFail = shouldIFail;
  }

  public void run() {
    try {
      if(shouldIFail) {
        exceptionGenerator.throwException( shouldIFail );
        Thread.sleep(1000);
        System.out.println(
            "true:    expected == 2: " 
          + exceptionGenerator.getErrno());
      } else {
        Thread.sleep(500);
        exceptionGenerator.throwException( shouldIFail );
        System.out.println(
            "false: no expectations: " 
          + exceptionGenerator.getErrno());
      }
    } catch (InterruptedException e) {
    }
  }
}

public static void main(String[] args) {
  ThrowException thx = new ThrowException();

  Thread t1 = new Thread(new ErrnoGenerator(thx, true));
  Thread t2 = new Thread(new ErrnoGenerator(thx, false));
  t1.start();
  t2.start();
  while(t1.isAlive() || t2.isAlive());
}

Однако даже без потоков на основе Java, вызывающих JNI, вы также можете ввести условие гонки

// This will set errno to "2"
thx.throwException( true );
try {
  Path file = Paths.get("/tmp/path-to-file");
  byte[] buf = "hello".getBytes();
  Files.write(file, buf);
} catch(Exception ex) {
}

// it's gone - errno contains something completely
// different to what you have expected to be found
System.out.println(
    "true:    expected == 2: " 
  + thx.getErrno());
...