Search code examples
javaandroidregexjava-native-interfacefuturetask

Avoid JNI ERROR generated by Java code in Android


I'm developing a test for some hundreds of regex I have to manage in Android. I encountered a catastrophic backtracking I cannot prevent, (i.e., the matcher enters an exponential complexity and it seems it is in an infinite loop, while, in reality, it is exploring a very huge number of possible matches), so I need to limit the overall execution of the matching using a timeout.

I've already found a possible approach here, but I also have to get the boolean return value from the find() method, so the Runnable is not the best choice. Even the little variation proposed among the other answers in the link above, to avoid the use of thread is not applicable, because it is based upon an extension of CharSequence which simply doesn't work because charAt is not used in the matcher.find() (checked this twice, both with a breakpoint during debug and also reading the Matcher source). Edit: I found in a second time that also @NullPointerException already found that the charAt gets never called, but I don't know if since 3 years ago he found out a solution

So, the best option I found until now seems to be using a FutureTask, which has the possibility to specify a timeout and can also return a value. I implemented the following code:

private boolean interruptMatch(final Matcher matcher){

    boolean res = false;
    ExecutorService executor = Executors.newSingleThreadExecutor();
    FutureTask<Boolean> future =
        new FutureTask(new Callable() {
            public Boolean call() {
                return matcher.find();
            }
        });
    executor.execute(future);

    try {
        res = future.get(2000, TimeUnit.MILLISECONDS);
    } catch (InterruptedException e) {
        Log.d("TESTER","Find interrupted after 2000 ms");
    } catch (ExecutionException e) {
        Log.d("TESTER","Find ExecException after 2000 ms");
    } catch (TimeoutException e) {
        Log.d("TESTER","Find timeout after 2000 ms");
    }
    future.cancel(true);
    executor.shutdownNow();
    return res;
}

This part of code is being called by the main method, in an almost "classic" way:

pattern = Pattern.compile(pattern, java.util.regex.Pattern.CASE_INSENSITIVE);
matcher = pattern.matcher(inputString);
if (interruptMatch(matcher)) { // before the need to manage catastrophic backtracking here there was a simple if (matcher.find()){
    // Do something
}

So, everythings seemed to work, at least for the first some hundreds patterns (also limiting in the timeout time the catastrophic backtracking long running find), until I got the following error:

JNI ERROR (app bug): weak global reference table overflow (max=51200)

It has been generated by the above java code (before this error didn't appear - cancelling the pattern which caused the catastrophic backtracking, obviously), but I cannot find out how to clean the global reference table, (I found a number of answers about similar issues generated directly by JNi code, but not from Java), not how to find a workaround or another valid approach. EDIT: I further tried to debug and I found that the issue arises when I call the get method. I tried to follow the code of FutureTask, but I didn't find anything useful (and I get bored too fast).

Can you help me, please? Thank you in advance


Solution

  • After other digging I found out that there is a tracked issue in Android, (it seems it deals with other topics, but it also answer mine) and from the replies I understand that it is just an issue which appear during debugging. I tested again my tester app and I found it's true: without debugging the above error doesn't happens. So, the severity of the issue is much lower and I can live with it (for me this is a closed issue) –