My team ran into some dosync behaviour that surprised us:
(let [some-ref (ref 1)
t (doto (Thread. (fn []
(dosync (ref-set some-ref 2))
(Thread/sleep 1000)))
(.start))]
(.interrupt t)
(.join t 100)
(.isAlive t))
; true
(let [some-ref (ref 1)
t (doto (Thread. (fn []
(Thread/sleep 1000)))
(.start))]
(.interrupt t)
(.join t 100)
(.isAlive t))
; false
the dosync and ref operation appears to https://github.com/clojure/clojure/blob/6a4ba6aedc8575768b2fff6d9c9c7e6503a0a93a/src/jvm/clojure/lang/LockingTransaction.java#L114-L124 the InterruptedException, resetting Thread/isInterrupted. Is this common knowledge?
We ran into this when investigating a bug in a 'shutdown' procedure for a component that does some work in a long-running thread. That thread happens to use refs a few layers down. We could change the shutdown approach for this component so that it e.g. checks a flag in an atom. However, thread interrupts are convenient as they interrupt blocking operations, Thread/sleeps etc.
Is it fair to say clojure applications should shy away from interrupting threads in case those threads are using refs?Semantically an InterruptedException isn’t a signal you should retry, it means you should abort the tx/rollback anything uncommitted and exit. But @lambeauxworks’s suggestion of simply setting the interrupted flag would be good enough because it means the caller can check the flag after the dosync, and would be a less invasive change.
I think that is this issue https://clojure.atlassian.net/browse/CLJ-2909 ?
Ah yes, that's it. Sorry, I'm not sure how I didn't find that when searching!
I think there are actually 2 potential issues:
1. (the one you linked) if a thread is interrupted before you call dosync, the interrupted flag gets cleared. A workaround could be to check the interrupted flag before tryWriteLock() is called, and throw an interrupted exception if it’s set.
2. if you interrupt a thread while it’s in tryWriteLock() then I think there’s no hope, as you will never see the exception nor the interrupted flag?
Hey Alex, I looked at that Jira issue. I don't think the exception needs to be re-thrown. I think the interrupted flag just needs to be set again. E.g.
Thread.currentThread().interrupt();
It just resets the status.Maybe not until all tx retries are complete. But still.