I'm writing some JNI code to wrap some C functions. Some of these C functions set errno
is case of an error. My code would be simpler if I could move the exception handling from the JNI code to the Java code. My plan is to have to JNI function return null
in case of an error and then in the Java code query errno
and construct and throw and exception there. For this to work JNI would have to leave errno
untouched.
Asked
Active
Viewed 281 times
1

Philippe Marschall
- 4,452
- 1
- 34
- 52
-
I would think that it would be better to have the JNI code to set something other than `errno` and to fetch that error value through a JNI exposed interface. The problem with `errno` is that it can be touched by a number of actions. Probably what would be better is to have your own error values to indicate the error rather than depending on `errno` standard values. That way you can report errors that are not covered by the `errno` range of values and have better and more informative diagnostics. – Richard Chambers Feb 16 '20 at 14:58
-
See as well the discussion in https://stackoverflow.com/questions/49701612/jni-c-socket-connection-error-on-solaris-11 – Richard Chambers Feb 16 '20 at 15:00
-
There are likely no guarantees that `errno` won't be changed. All any code has to do is make a call to a function that the documentation in the C standard does **not** mention `errno`. [Such functions are free to modify the value of `errno`](https://port70.net/~nsz/c/c11/n1570.html#7.5p3): "The value of errno may be set to nonzero by a library function call whether or not there is an error, provided the use of errno is not documented in the description of the function in this International Standard." – Andrew Henle Feb 16 '20 at 18:22
1 Answers
4
If I were you, I'd have gone with the exception as soon as possible
#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);
}
}
and then, in Java
code, I'd have decomposed the error code and error message.
Then, inside Java
you can easily access both: errno
and message
.
public static native void throwException();
...
...
try {
throwException();
} catch(Exception ex) {
ex.getMessage();
}
Update
Note that with JNI
being involved into play, things may get complicated (especially with shared data - errno
).
You can imagine one of the scenarios - like this:
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());
}
However, even without Java
based threads calling JNI
, you can enter race condition as well
// 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());

Oo.oO
- 12,464
- 3
- 23
- 45
-
Yeah, this seems like the best thing to do. Unfortunately `FindClass` and `ThrowNew` can fail as well. – Philippe Marschall Feb 17 '20 at 18:42
-
@PhilippeMarschall If `FindClass "java/lang/Exception" )` or `ThrowNew` fails, there's not much you can do about anything other than call `abort()` - which might very well be the best approach depending on your requirements.\ – Andrew Henle Feb 17 '20 at 22:55