Hi, this actually boils down to a strong case of read the manual and one surprising effect.
The executive summary: From the Java SE 8 docs description for Thread/sleep: Throws:IllegalArgumentException <https://docs.oracle.com/javase/8/docs/api/java/lang/IllegalArgumentException.html> - if the value of millis is negativeInterruptedException <https://docs.oracle.com/javase/8/docs/api/java/lang/InterruptedException.html> - if any thread has interrupted the current thread. The *interrupted status* of the current thread is cleared when this exception is thrown. The key to my misunderstanding is right there is the InterruptedException doc - the interrupted status is cleared by Thread/sleep. The remaining surprise is that the future-cancel call actually returns true for all the cases (which is different from what I originally wrote). Check out the below examples. Example 1 - just a normal future with no try/catch user> (defn test-interrupt-status-1 [init-v] (let [n (atom init-v)] [n (future (while (not (Thread/interrupted)) (swap! n inc)))])) #'user/test-interrupt-status-1 user> (def test-1 (test-interrupt-status-1 10)) #'user/test-1 user> (future-cancel (test-1 1)) true user> (future-cancelled? (test-1 1)) true user> @(test-1 0) 43202690 user> @(test-1 0) 43202690 Example 2 - a future that contains a try/catch but doesn't have any sleep included Notice that future-cancel works fine even with the try/catch - the atom isn't updated after the future-cancel is called on the future. user> (defn test-interrupt-status-2 [init-v] (let [n (atom init-v)] [n (future (while (not (Thread/interrupted)) (try (swap! n inc) (catch Exception e (println (str "Exception:\t" e "\nisInterrupted:\t" (.isInterrupted (Thread/currentThread))))))))])) #'user/test-interrupt-status-2 user> (def test-2 (test-interrupt-status-2 10)) #'user/test-2 user> (future-cancel (test-2 1)) true user> (future-cancelled? (test-2 1)) true user> @(test-2 0) 44066815 user> @(test-2 0) 44066815 Example 3 - now we have a future which includes a try/catch AND A Thread/sleep Calling future-cancel returns true (!!) and we see that the Thread/sleep has cleared the interrupt status of the thread (e.g., the .isInterrupted is false for the thread). What is interesting here is that future-cancelled? returns true but the thread is actually still happily running because the interrupt status was reset. I'm not sure what I think about this - I hate surprises, but the docs are pretty clear on what is happening here. user> (defn test-interrupt-status-3 [init-v] (let [n (atom init-v)] [n (future (while (not (Thread/interrupted)) (try (Thread/sleep 10000) (swap! n inc) (catch Exception e (println (str "Exception:\t" e "\nisInterrupted:\t" (.isInterrupted (Thread/currentThread))))))))])) #'user/test-interrupt-status-3 user> (def test-3 (test-interrupt-status-3 10)) #'user/test-3 user> (future-cancel (test-3 1)) trueException: java.lang.InterruptedException: sleep interrupted isInterrupted: false user> (future-cancelled? (test-3 1)) true user> @(test-3 0) 12 user> @(test-3 0) 13 user> (future-cancel (test-3 1)) false user> (future-cancelled? (test-3 1)) true On Thursday, April 28, 2016 at 11:48:29 AM UTC-4, Tom Bodenheimer wrote: > > Hi all, > > I have recently been playing with futures and have stumbled on the > following situation. > > (defn test-f1 [initial-v] > (let [n (atom initial-v)] > [n > (future (while (not (Thread/interrupted)) > (Thread/sleep 5000) > (swap! n inc) > (println @n)))])) > > (def t1 (test-f1 11)) > > This works as expected. I see the println outputs, can @(t1 0) and > (future-cancel (t1 1)) returns true. > > Then I have: > > (defn test-f2 [initial-v] > (let [n (atom initial-v)] > [n > (future (while (not (Thread/interrupted)) > (try > (Thread/sleep 5000) > (swap! n inc) > (println @n) > (catch Exception e (println "whoops")))))])) > > (def t2 (test -f2 11)) > > The same except now my while has a (try ... (catch Exception e ...). > However, now (future-cancel (t2 1)) returns false and the future cannot be > cancelled - I also see the "whoops" at the REPL. Seeing that "whoops" made > me wonder if the interrupt that would shutdown the thread is raising an > exception that is being caught by my (try .... (catch ...)) block, thus > never interrupting the thread. > > I made a change to throw the exception again: > > (defn test-f3 [initial-v] > (let [n (atom initial-v)] > [n > (future (while (not (Thread/interrupted)) > (try > (Thread/sleep 5000) > (swap! n inc) > (println @n) > (catch Exception e (do (println "whoops") (throw > e))))))])) > > and the behavior now matches that of test-f1. > > I think I'm just not clear on some fundamental aspect of how > future-cancel, try/catch, and the underlying java are actually working here. > > It seems *very* weird to me that after future-cancel cancels the future, > that the (try . . . (catch . . .)) would ever be invoked in the body of the > while loop. Yet, clearly the interrupt is being handled in the (try . . . > (catch . . . )). It also seems *very* weird that even in this case, that > the next time we get back to (while (not (Thread/interrupted)) that > (not(Thread/interrupted)) would be true . . . I guess the exception sets > the interrupt status for the thread. When exception gets caught in my (try > . . . (catch . . .)), the interrupt status is never set? > > Thanks for any clarification. > > > -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.