Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do cancelled Clojure futures continue using CPU?

I have many examples of Java bytecode, all of which I'd like to execute from Clojure. Each sequence of bytecode may contain an infinite loop, in which case I'd like to stop running it after a couple of seconds. I've been looking at futures as a means of doing this. Having hunted around for a couple of implementations I've tried both this code:

(deref (future (loop[a 1] (recur a)) :done!) 1000 :impatient!)

...and also the code at https://gist.github.com/3124000

In both cases, the loop appears to be timed out appropriately (and in the latter case the future is reported to have been both done and cancelled), but I see my CPU usage rise to 99% or thereabouts and stay there. I also see that each time I run this code, my Java process gains an extra thread.

It looks to me like the future is being cancelled, but the code is still running. In my program I will need to run, and time out, some very tight infinite loops (e.g. the Java bytecode equivalent of "20 PRINT GOTO 10") and I don't have the option of modifying the code that I'm running.

Any ideas why I'm seeing this behaviour; what I could do to prevent it; or alternative methods for me to realise my aim of running and timing out such code?

like image 341
Tom Hume Avatar asked Jul 17 '12 10:07

Tom Hume


2 Answers

Java's supported mechanism for thread cancellation is interrupts. The .stop() method is deprecated for a reason - see the docs, Effective Java, Java Concurrency in Practice, etc.

In fact, the implementation of FutureTask (which backs future-cancel) is interruption-based.

As of today, each Clojure future is backed by an unbounded thread pool (just like send-off actions). So you may leverage the thread interruption primitives:

(def worker
  (let [p (promise)]
    {:future (future
               (let [t (Thread/currentThread)]
                 (deliver p t)
                 (while (not (Thread/interrupted))
                   (println 42)
                   (Thread/sleep 400))))
     :thread @p}))

Now one can successfully execute either (.interrupt (:thread worker)) or (future-cancel (:future worker)).

Although this couples the thread cancellation mechanism to that of Futures, the available Executor implementations are smart enough to clear the interrupted status that a previous task may have been set, so interruptions to one task never affect other tasks - even if they are run on the same thread.

The point is that preemptively killing threads is generally a bad idea - if you want to cancel tasks, you have to make them explicitly cancellable, in some or other way. OTOH, in your particular case you are executing opaque bytecode so there's no more option than resorting to .stop().

like image 122
deprecated Avatar answered Sep 22 '22 16:09

deprecated


The only way I've found to actual kill a the code executing inside a thread is to use the deprecated .stop method. In a lot of cases, the reason that it is deprecated isn't really important.

I had a function in clojail for doing just that. Feel free to snatch the function out or just pull in the library.

like image 45
Rayne Avatar answered Sep 25 '22 16:09

Rayne