1

I am developing a small game, (Java, LibGdx) where the player fills cloze-style functions with predefined lines of code. The game would then compile the code and run a small test suite to verify that the function does the stuff it is supposed to.

Compiling and running the code already works, but I am faced with the problem of detecting infinite loops. Consider the following function:

// should compute the sum of [1 .. n]
public int foo(int n) {
    int i = 0;
    while (n > 0) {
        i += n;
        // this is the place where the player inserts one of many predefined lines of code
        // the right one would be: n--;
        // but the player could also insert something silly like: i++;
    }
    return i;
}

Please note that the functions actually used may be more complex and in general it is not possible to make sure that there cannot be any infinite loops.

Currently I am running the small test suite (provided for every function) in a Thread using an ExecutorService, setting a timeout to abort waiting in case the thread is stuck. The problem with this is, that the threads stuck in an endless loop will run forever in the background, which of course will at some point have a considerable impact on game performance.

// TestClass is the compiled class containing the function above and the corresponding test suite
Callable<Boolean> task = new Callable<Boolean>() {
    @Override
    public Boolean call() throws Exception {
        // call the test suite
        return new TestClass().test();
    }
};
Future<Boolean> future = executorService.submit(task);
try {
    Boolean result = future.get(100, TimeUnit.MILLISECONDS);
    System.out.println("result: " + (result == null ? "null" : result.toString()));
} catch (InterruptedException e) {
    e.printStackTrace();
} catch (ExecutionException e) {
    e.printStackTrace();
} catch (TimeoutException e) {
    e.printStackTrace();
    future.cancel(true);
}

My question is now: How can I gracefully end the threads that accidentally spin inside an endless loop?

*EDIT To clarify why in this case, preventing infinite loops is not possible/feasable: The functions, their test suite and the lines to fill the gaps are loaded from disk. There will be hundrets of functions with at least two lines of code that could be inserted. The player can drag any line into any gap. The effort needed to make sure no combination of function gap/code line produces something that loops infinitely or even runs longer than the timeout grows exponentially with the number of functions. This quickly gets to the point where nobody has the time to check all of these combinations manually. Also, in general, determining, whether a function will finish in time is pretty much impossible because of the halting problem.

LostMekkaSoft
  • 143
  • 1
  • 7

3 Answers3

1

The right way is to change the design and avoids never ending loops.

For the time being, inside your loop you could check if the thread is interrupted some way by: isInterrupted() or even isAlive().

And if it is you just exit.

Mario Santini
  • 2,905
  • 2
  • 20
  • 27
  • This would mean to inject these checks into every loop before compiling the code, am I right? In theory this would be possible, but it seems quite ugly... I was wondering if i can somehow do something from **outside** the function itself, like when calling the test suite... Do you have any idea on how to avoiding the infinite loops in the first place? That would indeed be the cleanest solution, but I am not sure that this is possible for this problem... (see my question edit) – LostMekkaSoft Oct 06 '16 at 10:39
  • The thread should be responsible to check if has been stopped from outside. This is a proper way to code in multithreading, expecially if the thread is going to perform a blocking and very long task. i get your point, you have a huge amount of code to change. Have a look here http://stackoverflow.com/questions/10961714/how-to-properly-stop-the-thread-in-java is a more complete answer, but not sure is what you're looking for. – Mario Santini Oct 06 '16 at 11:12
1

There is no such thing as "graceful termination" of a thread inside the same process. The terminated thread can leave inconsistent shared-memory state behind it.

You can either organize things so that each task is started in its own JVM, or make do with forceful termination using the deprecated Thread.stop() method.

Another option is inserting a check into the generated code, but this would require much more effort to implement properly.

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • Thanks for the directions :) Unfortunately, `Thread.stop()` does not work anymore. Do you have any information on how to run the task in another JVM? (also I am really afraid this will kill performance...) – LostMekkaSoft Oct 06 '16 at 11:43
  • `Thread.stop()` does work, try it. Child JVMs are started using `Runtime.exec()`. If you have a lot of small tasks, it will kill performance. – Marko Topolnik Oct 06 '16 at 11:45
  • Youre right, it works. The only thing what does not work is stopping the thread I fed into the ExecutorService :) Now I only need a setup where I have control over the thread while still being able to detect a timeout. The ExecutorService does not give me the thread object. Any ideas on how to do that? – LostMekkaSoft Oct 06 '16 at 12:17
  • That would be even dirtier hacking than what you're doing so far. You can submit a custom task that saves the `currentThread` to its field and also catches the `ThreadDeath` exception so it doesn't actually kill the thread, just abort the task. Your outside code would read the `currentThread` field and `stop` it. – Marko Topolnik Oct 06 '16 at 12:21
  • Wow this finally works :D I agree though that this is quite dirty. The only excuse that I could bring forth is that, in this specific use case, the actual task with all its data is completely irrelevant once there was a timeout. Thx for being that patient with me :) – LostMekkaSoft Oct 06 '16 at 12:40
  • Yes, I assume the task will do some self-contained computation and won't affect the stability of the rest of the program. – Marko Topolnik Oct 06 '16 at 12:42
0

It is not normal to have a never ending loop if it not wanted.

To solve the problem You can add a counter in the loop and if you reach a limit you can exit.

int counter = 0;
while (n > 0) {
    counter++;
    if (counter > THRESHOLD) {
       break; 
    }
    i += n;
    // this is the place where the player inserts one of many predefined lines of code
    // the right one would be: n--;
    // but the player could also insert something silly like: i++;
}
Davide Lorenzo MARINO
  • 26,420
  • 4
  • 39
  • 56