In the general case, side effects within the swap! function are a bad idea because of the optimistic locking. In your first code snippet, if there is any contention on the atom (and maybe in your app you know there is none because it's only ever accesses by the same single thread), you run the risk of having orphaned futures.
As far as I know there should be no such problem with the agent version. I'm not really sure about the nesting of send-off calls though; that might be the source of your stack overflow. I seem to remember that this was not supported up until 1.4 or 1.5; not sure what the current semantics is. Depending on how many different event types you're watching for (and how many differet actions you need to take), it might be worth having a single thread managing the queue. Somethin along the line of having a single atom containing a priority queue (or a sorted map?) with, for each event type, the last time the event was observed. At some interval, that event thread could check the queue and run the required handlers based on the current time. When an event arrives, it resets the time associated to its type in the queue. Whether this is better will depend on your usage pattern. I would just like to point out that creating a future has some non trivial overhead as it also creates a thread (at least, the last time I checked, futures where not created out of a limited thread pool). On Tuesday, 2 December 2014, Erik Price <e...@zensight.co> wrote: > Coincidentally, we recently wrote code to do something very similar. The > following function will invoke f after period milliseconds, unless a > value is sent on events-ch, in which case the timeout is reset (and > starts counting down again): > > (defn invoke-after-uninterrupted-delay > ([period events-ch f] > (invoke-after-uninterrupted-delay period events-ch f [])) > ([period events-ch f & args] > (async/go-loop [] > (let [[_ p] (async/alts! [(async/timeout period) events-ch])] > (if (= p events-ch) > (recur) > (apply f args)))))) > > e > > > On Mon, Dec 1, 2014 at 6:50 PM, Brian Craft <craft.br...@gmail.com > <javascript:_e(%7B%7D,'cvml','craft.br...@gmail.com');>> wrote: > >> That version has the unfortunate behavior that (func) can be interrupted >> if (event) is called while it is running. Here's another version using an >> agent: >> >> (defn queue-with-delay2 [period func] >> (let [q (agent nil)] >> (fn [] >> (send-off q (fn [t] >> (when t >> (future-cancel t)) >> (future (Thread/sleep period) (send-off q (fn [_] >> (func) nil)))))))) >> >> Running with a sleep to see that (func) is not canceled by subsequence >> (event) calls: >> >> (def event (queue-with-delay2 2000 #(do (println "running") (Thread/sleep >> 2000) (println "ending")))) >> >> Oddly, if calling (event) between "running" and "ending" messages, the >> repl will stack-overflow on the return value. No idea what that's about. >> But, running like this is fine: >> >> (do (event) nil) >> >> >> >> >> >> On Monday, December 1, 2014 1:37:56 PM UTC-8, Brian Craft wrote: >>> >>> I have need to perform an action when a series of events is quiet for >>> some period. That is, if one event arrives an action is queued to execute >>> after some timeout. If a second event arrives the timeout is reset, and >>> so-forth. >>> >>> The following code seems to work, however I'm wondering if calling >>> 'future' from 'swap!' is a bad idea (side effecting), and if there's a >>> better way. >>> >>> (defn queue-with-delay [period func] >>> (let [f (atom nil)] >>> (fn [] >>> (when @f >>> (future-cancel @f)) >>> (swap! f (fn [_] (future (Thread/sleep period) (func))))))) >>> >>> >>> Use like >>> >>> (def event (queue-with-delay 2000 #(println "running"))) >>> (event) >>> (event) >>> (event) ; pause 2 sec >>> "running" >>> >>> >>> >>> -- >> 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 >> <javascript:_e(%7B%7D,'cvml','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 >> <javascript:_e(%7B%7D,'cvml','clojure%2bunsubscr...@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 >> <javascript:_e(%7B%7D,'cvml','clojure%2bunsubscr...@googlegroups.com');>. >> For more options, visit https://groups.google.com/d/optout. >> > > -- > 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 > <javascript:_e(%7B%7D,'cvml','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 > <javascript:_e(%7B%7D,'cvml','clojure%2bunsubscr...@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 > <javascript:_e(%7B%7D,'cvml','clojure%2bunsubscr...@googlegroups.com');>. > For more options, visit https://groups.google.com/d/optout. > -- 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.