Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the correct way to perform side effects in a clojure atom swap

Tags:

clojure

I'm keeping a registry of processes in an atom.

I want to start one and only one process (specifically a core.async go-loop) per id.

However, you're not supposed to perform side-effects in a swap!, so this code is no good:

(swap! processes-atom
       (fn [processes]
         (if (get processes id)
           processes ;; already exists, do nothing
           (assoc processes id (create-process! id)))))

How would I go about doing this correctly?

I have looked at locking, which takes an object as a monitor for the lock. I would prefer that each id - which are dynamic - have their own lock.

like image 681
Magnar Avatar asked Jan 06 '17 12:01

Magnar


Video Answer


1 Answers

It seems that you need to protect processes-atom from concurrent modification, so that only single thread can have access to it. locking will work in this case. Since, by usage of locking, we will manage thread safety by ourselves, we can use volatile instead of atom (volatile is faster, but doesn't provide any thread-safety and atomicity guaranees).

Summing up the above, something like below should work fine:

(def processes-volatile (volatile! {}))

(defn create-and-save-process! [id]
  (locking processes-volatile
    (vswap! processes-volatile
            (fn [processes]
              (if (get processes id)
                processes
                (assoc processes id (create-process! id)))))))
like image 155
OlegTheCat Avatar answered Oct 02 '22 21:10

OlegTheCat