This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-10-22
Channels
- # announcements (9)
- # asami (52)
- # aws (1)
- # babashka (7)
- # babashka-sci-dev (12)
- # beginners (72)
- # calva (24)
- # cider (9)
- # clj-kondo (76)
- # cljs-dev (15)
- # clojure (19)
- # clojure-australia (4)
- # clojure-europe (33)
- # clojure-france (9)
- # clojure-gamedev (17)
- # clojure-nl (6)
- # clojure-portugal (5)
- # clojure-uk (5)
- # clojurescript (61)
- # clojureverse-ops (4)
- # code-reviews (23)
- # conjure (1)
- # data-science (2)
- # datalevin (6)
- # datomic (49)
- # gratitude (1)
- # helix (24)
- # holy-lambda (14)
- # jobs (3)
- # lsp (92)
- # malli (7)
- # missionary (8)
- # pathom (12)
- # proletarian (3)
- # re-frame (4)
- # remote-jobs (1)
- # shadow-cljs (4)
- # spacemacs (3)
- # sql (9)
- # tools-build (90)
- # vim (1)
- # xtdb (11)
Hey team, I created a quick “delay” fn.
(defn delay-f
[f delay-ms]
(let [timer (Timer.)
task (atom nil)
latest-batch (atom [])]
(fn [& args]
(swap! latest-batch conj args)
(when-not @task
(let [new-task (proxy [TimerTask] []
(run []
(f @latest-batch)
(reset! task nil)
(reset! latest-batch [])
(.purge timer)))]
(reset! task new-task)
(.schedule timer new-task delay-ms))))))
The idea is, based on an ms
, I batched the calls
to f within that ms, and send it all out at once.
I am a bit nervous about my use of atoms, and potential race conditions. If ya’ll have thoughts would appreciate it!Alternatively, you can use a https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/DelayQueue.html
A common pattern is to create a delay that does side effects, then swap in the delay if needed, and force whatever delay is there (the new one if swapped, the old one if not)
(defn delay-f
[f delay-ms]
(let [timer (Timer.)
lock (Object.)
task (volatile! nil)
latest-batch (volatile! [])]
(fn [& args]
(locking lock
(vswap! latest-batch conj args)
(when-not @task
(let [new-task (proxy [TimerTask] []
(run []
(locking lock
(f @latest-batch)
(vreset! task nil)
(vreset! latest-batch [])
(.purge timer)))]
(vreset! task new-task)
(.schedule timer new-task delay-ms))))))
Fantastic — thank you @U3JH98J4R
One noob question:
Here, do you use volatile!
strictly because it’s faster than an atom, or for some other reason?
Mainly, my assumption is that if I were to replace the volatile variables there with an atom, I assume things will still work because of the lock. If that’s not the case, would love to know, to get a better grasp of how things work
Things would still work, yeah. Volatile would just be faster since the lock makes CAS redundant
But I recommend nitro cold brew coffee, around 2 Liters, if you want to dive into JVM concurrency and properly understand volatile
Aweesome! thanks @U3JH98J4R — learning bit by bit!
One more noob question:
would we need to add one more (locking
inside the TimerTask? (reason being that it does vreset!
outside of initial lock)
a delayqueue would still need a worker thread to service it, something like a scheduled executor bundles a delay queue with a threadpool