Search code examples
multithreadingclojureatomic

Best way to remove item in a list for an atom in Clojure


I'm using server.socket to stream data to multiple clients, server.socket uses Threads for each client connection. I currently have something like this:

(def clients (atom ())) ; connected clients defined globally for that namespace

(swap! clients conj a)  ; adds a client (which is an atom itself as well), this is in a function that is run on the client's thread

;I want to better the process of removing a client!
(dosync (reset! clients (remove #{a} @clients))) ; removes client from list, run in a function on the client's thread

I run a function which runs through each client and grabs the content, it is in an infinite loop on each of the multiple client threads, so it's run simultaneously:

(doseq [c @clients]
  (print ((deref c) :content))
  (flush))

I sort of came to the conclusion using Atoms in the threads really makes the program work smoothly and allows non-blocking Reads, so I'm happy with this except I feel that resetting the global client's Atom just so I can remove a single client from the list is a bad move. Is there a more appropriate way to accomplish this using swap! ? I chose list for the clients atom as I'm running doseq on each connected client to grab the content and flush it to the output stream socket.


Solution

  • Avoid deref-ing the atom within a swap! or reset!.

    Here swap! is going to give you what you need. It takes a function that receives the current value, which you can use for your update:

    (def clients (atom '(:a :b :c :d)))
    (swap! clients (fn [s] (remove #{:a} s)))
    

    You may be used to not seeing the function argument of swap! as explicitly as above because swap! will apply the function to any additional args provided, so if they are in the correct order, e.g. if we used set for clients, we can

    (def clients (atom #{:a :b :c :d}))
    (swap! clients disj :a)