Search code examples
clojurestm

clojure sendoff println function


I am trying to send-off function println in the transaction.

(ns com.lapots.functional.clojure.transact
    (:gen-class))

(defn transfer [from to amount]
    (alter
        (.balance from) - amount)
     (alter
        (.balance from) + amount))

(defrecord Account [balance])

(defn -main [& args]
    (def account1 (Account. (ref 100)))
    (def account2 (Account. (ref 100)))
    (def trx-agent (agent 0))

    (future
        (dosync
            (send-off trx-agent println "T2 transfer")
            (Thread/sleep 5000)
            (transfer account1 account2 10)))

    (dosync
        (println "T1 transfer")
        (transfer account1 account2 10))

    (shutdown-agents)
)

If I do like this

(println "T2 transfer")
(Thread/sleep 5000)

it displays message twice as the transaction retries. So I decided to use agents to make side-effecting operation println run only once.

But when I do like this

(send-off trx-agent println "T2 transfer")

It does not print T2 transfer message at all. What is the problem?


Solution

  • You are using shutdown-agents too soon.

    (defn transfer [from to amount]
      (println :transfer-enter amount)
      (alter
        (.balance from) - amount)
      (alter
        (.balance from) + amount)
      (println :transfer-exit  amount)
    )
    
    (defrecord Account [balance])
    
    (def account1 (Account. (ref 100)))
    (def account2 (Account. (ref 100)))
    (def trx-agent (agent 0))
    
    (defn -main [& args]
      (println :main-enter )
    
      (future
        (dosync
          (println :t2-enter)
          (send-off trx-agent println "agent: T2 transfer")
          (Thread/sleep 500)
          (transfer account1 account2 20)
          (println :t2-exit)
        ))
    
      (dosync
        (println :t1-enter)
        (send-off trx-agent println "agent: T1 transfer")
        (transfer account1 account2 10)
        (println :t1-exit))
    
      (Thread/sleep 2000)
      (shutdown-agents)
      (println :main-exit )
    )
    

    with result:

    :main-enter
    :t2-enter
    :t1-enter
    :transfer-enter 10
    :transfer-exit 10
    :t1-exit
    0 agent: T1 transfer
    :transfer-enter 20
    :t2-enter
    :transfer-enter 20
    :transfer-exit 20
    :t2-exit
    nil agent: T2 transfer
    :main-exit
    

    So T2 only waits 500 msec now, while T1 runs right away. We wait 2000 msec before calling shutdown-agents, which will kill all of the agent threads preventing the agent from running.