network-programmingclojureclojureclr

Clojure: alternative to using a mutex/lock and a counter


Scenario: I have a server listening to six active TCP/IP connections. When a "ready" message comes in, an event will be raised on its own thread. When the server has received a "ready" message from each connection, it needs to run the "start" function.

My object oriented solution would likely involve using a mutex and a counter. Something like:

int _countDown= 6;
object _lock;
void ReadyMessageReceivedForTheFirstTimeFromAConnection() {
    lock(_lock) {
      --_countDown; // 
       if (_countDown==0) Start();
    }
}

How could this problem be solved in Clojure without resorting to locks/mutexes?


Solution

  • When you prefer a pure clojure version, you can use a promise to give your futures a go.

    Every time you receive message you increment the conn-count the watch checks if the treshold is reached and delivers :go to the barrier promise.

    (def wait-barrier (promise))
    (def conn-count (atom 0))
    
    (add-watch conn-count :barrier-watch
               (fn [key ref old-state new-state]
                 (when (== new-state 6)
                   (deliver wait-barrier :go))))  
    

    dummy-example:

    (def wait-barrier (promise))
    (def conn-count (atom 0))
    (defn worker-dummy []
      (when (= @wait-barrier :go)
        (println "I'm a worker")))
    
    (defn dummy-receive-msg []
      (doall (repeatedly 6,
                         (fn []
                           (println "received msg")
                           (swap! conn-count inc)))))
    
    (let [workers (doall (repeatedly 6 (fn [] (future (worker-dummy)))))]
      (add-watch conn-count :barrier-watch
                 (fn [key ref old-state new-state]
                   (when (== new-state 6)
                     (deliver wait-barrier :go))))
      (dummy-receive-msg)
      (doall (map deref workers)))