I'm trying to handle duplicate events (MIDI note-on and note-off signals coming in triplicate). It seems like using agents and locking works, unless the events happen (nearly) simultaneously.
Here's an example:
(def test-val (agent 0))
(def allow-update (agent true))
(defn increase-val []
(locking allow-update
(when @allow-update
(send allow-update (fn [_ x] x) false)
(send test-val + 1)))
(print @test-val))
(defn run-futures [delay-time]
(send allow-update (fn [_ x] x) true)
(send test-val * 0)
(dotimes [_ 20]
(Thread/sleep delay-time)
(future (increase-val))))
If I test it with a slight delay between calls to increase-val:
(run-futures 2)
;outputs 0111111111111111111 every time, as expected
But if I let all the calls to increase-val happen all at once:
(run-futures 0)
;001222222222222222
(run-futures 0)
;000000145555555555
(run-futures 0)
;000000013677777777
It seems like the lock doesn't have time to get turned on, so the agent gets increased by multiple futures.
I'm hoping I'm missing something here that will allow me to make sure I don't act on duplicate simultaneous events.
Thanks!
Because Agents are asynchronous and uncoordinated, there can be a delay between when you send them a message and the actual running of the action that was sent. During this delay
it's possible for most, or all, of the calls to (send test-val + 1) to make it through the locked section before the first one actually runs and sets @allow-update to false. You can see this in the leading 0s before the state of test-val gets changed.
atoms may be better suited to interacting with locks, though refs may be the right tool for coordinated access to multiple identities.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With