I'm looking for a macro that will throw an exception if an expression takes longer than X seconds to complete.
I'm not sure this is possible without running the expression in a separate thread. The reason being, if the thread is busy processing the expression, you can't inject code to throw an exception.
A version with a monitor thread that throws an exception if the expression takes too long is definitely possible, however, the exception thrown would be from the monitor thread, not the thread in which the expression is running. Then, there'd be no way of stopping it short of sending that thread an interrupt, which it might ignore if you haven't coded for it in the expression.
If it's acceptable to have a version which runs the expression in a separate thread, let me know and I can post some sample code. Otherwise, your best bet sound like it would be to write the main loop/recursion of the expression in such a way that it checks to see how long it has taken at every iteration and throws an exception if it's exceeded the bound. Sorry if that's not quite what you need...
Futures to the rescue!
user=> (let [f (future (reduce * (range 1 1001)))]
(.get f 1 java.util.concurrent.TimeUnit/MILLISECONDS))
java.util.concurrent.TimeoutException (NO_SOURCE_FILE:0)
And to make a macro of it:
(defmacro time-limited [ms & body]
`(let [f# (future ~@body)]
(.get f# ~ms java.util.concurrent.TimeUnit/MILLISECONDS)))
So you can do this:
user=> (time-limited 1 (reduce * (range 1 1001)))
java.util.concurrent.TimeoutException (NO_SOURCE_FILE:0)
user=> (time-limited 1 (reduce * (range 1 101)))
93326215443944152681699238856266700490715968264381621468592963895217599993229915
608941463976156518286253697920827223758251185210916864000000000000000000000000
(try (time-limited 1 (reduce * (range 1 100000))) (catch java.util.concurrent.TimeoutException _ nil))
I tried above code. Does the execution of (reduce * (range 1 100000)) stop after returning nil? CPU usage by JVM remains very high even after the return.