Database Reference
In-Depth Information
(swap! retries inc)
(Thread/sleep sleep-for)
(ref-set counter (inc c))
(send *agent* starve-out sleep-for)
tag))))
5.
If we send
starve-out
to both agents with very different sleep periods and look
at the output, we'll see that
:a2
is consistently getting starvedout. (You can stop the
agents by calling
shutdown-agents
):
user=> (start-agents starve-out 50 1000)
:starve-out :a2, :try 1, :counter 19
:starve-out :a2, :try 2, :counter 39
:starve-out :a2, :try 3, :counter 59
:starve-out :a2, :try 4, :counter 78
6.
In order to make this safe, we have to move all of the side effects out of the
dosync
block. This means that we'll move the
debug
call out of the STM. While we're at it,
we'll move the
send
call since it's theoretically a side effect, even though it should
be safe enough here. To be safer, we'll use a new output function, one that uses
io!
(highlighted). The
io!
block will throw an exception if it is executed inside
a transaction:
(defn debug! [msg] (
io!
(debug msg)))
(defn starve-safe [tag sleep-for]
(let [retries (atom 0)]
(dosync
(let [c @counter]
(swap! retries inc)
(Thread/sleep sleep-for)
(ref-set counter (inc c))))
(when-not (zero? @retries)
(debug! (str ":safe-starve " tag
", :try " @retries
", " @counter)))
(send *agent* starve-safe sleep-for)
tag))
This version safely handles the I/O in the STM. Moreover, if we forget and refactor the call to
debug!
back inside the transaction, our code will stop working.