0

I have a JRuby engine which evaluates some scripts and I want to close the thread if it takes more than 5 seconds. I tried something like this:

class myThread extends Thread{
    boolean allDone = false;

    public void threadDone() {
        allDone = true;
    }

    public void run() {
        while(true) {
            engine.eval(myScript);
            if(allDone)
                return;
        }
    }

(...)

    th1 = new myThread();
    th1.start();
    try {
        Thread.sleep(5000);
        if(th1.isAlive())
            th1.threadDone();
    } catch(InterruptedException e) {}

    if(th1.isAlive())
        System.out.println("Still alive");

I also tried to kill the thread with th1.stop() or th1.interrupt() but the value retured by th1.isAlive() method is always true.

What can I do? I want to add that myScript could be "while(1) do; end" and I cannot wait until it's completed. So I want to prevent scripts like that and kill the thread if it takes more than 5 seconds.

Cheesebaron
  • 24,131
  • 15
  • 66
  • 118

2 Answers2

2

Another solution would be to use the built-in mechanism to interrupt threads:

public void run() {
    while (!Thread.currentThread().isInterrupted()) {
        engine.eval(myScript);
    }
}

...
th1 = new myThread();
th1.start();
try {
    Thread.sleep(5000);
    th1.interrupt();
} 

This way, no need for an allDone field, and no risk in failing to synchronize.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • Seems like you could put that condition into `while` instead of `true`. – Marko Topolnik Jul 23 '12 at 12:04
  • Yes indeed. I just copied and pasted the original code without much attention. But I'll fix that. – JB Nizet Jul 23 '12 at 12:06
  • 1
    Seems also that the idiom is to just check `Thread.interrupted()`, saving yourself one indirection. That call also clears the interrupted flag, which is supposed to be fine---as soon as you find out you were interrupted, you reset the `interrupted` signal flag. (I read about this in in earlier question here). – Marko Topolnik Jul 23 '12 at 12:11
  • IMHO, it's better to avoid clearing the flag. In this particular case, it doesn't change anything, but suppose this loop is inside a method which is itself called in a loop from the run() method, you would want to exit from the inner loop ASAP, and keep the interrupted flag so that the outer loop can also exit ASAP. – JB Nizet Jul 23 '12 at 12:15
  • Yes, the whole system is full of pitfalls the way it is designed. Generally, if you can't immediately ensure termination, you should throw `InterruptedException`. – Marko Topolnik Jul 23 '12 at 12:17
  • I tried that and even I call th1.interrupt() , th1.isAlive() method returns TRUE – user1521526 Jul 23 '12 at 13:03
  • Interrupting a thread only *asks* for its interruption and returns immediately. You have to `join()` the thread to wait until it's completed. It will only be completed when `engine.eval(myScript)` returns and the loop has a chance to check for the interrupted flag. – JB Nizet Jul 23 '12 at 13:11
  • JB Nizet, myScript could be "while(1) do; end" and I want to prevent that and kill the thread if it takes more than 5 seconds. So I cannot wait until it's completed. – user1521526 Jul 23 '12 at 13:17
  • That's quite an important information. Unless the engine itself can be interrupted/stopped, there is no clean solution. See http://stackoverflow.com/questions/1601246/java-scripting-api-how-to-stop-the-evaluation – JB Nizet Jul 23 '12 at 13:26
  • thx for your link. I followed their answers and it seems that the only option is to kill the thread. What I don't understand is why after I call Thread.stop() the method Thread.isAlive() returns true? – user1521526 Jul 23 '12 at 15:19
0

To make your Thread stoppable you might want something like.

class MyTask implements Runnable {
    public void run() {
        try {
           engine.eval(myScript);
        } catch(ThreadDeath e) {
           engine = null; // sudden death.
        }
    }    
}

You can call Thread.stop(), but I suggest you read the warnings on this method first.


If you want a thread to run for up to 5 seconds, the simplest solution is for the thread to stop itself.

class MyTask implements Runnable {
    public void run() {
        long start = System.currentTimeMillis();
        do {
           engine.eval(myScript);
        } while(System.currentTimeMillis() < start + 5000);
    }    
}

This assumes you want to run engine.eval() repeatedly. If this is not the case you may have to stop() the thread. It is deprecated for a good reason but it might be your only option.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • Peter I want to run engine.eval() only once. The problem occurs when the user try to evaluate scripts like "while(1) do;end". So I want to prevent that and kill the thread if it takes more than 5seconds. – user1521526 Jul 23 '12 at 13:06
  • In that case, Thread.stop() is you only option. This could leave your engine in an inconsistent state so you might want to re-create it. – Peter Lawrey Jul 23 '12 at 13:08
  • well I tried to use Thread.stop() but the thread is still alive. (the Thread.isAlive() method returns true) – user1521526 Jul 23 '12 at 13:15