Skip to content

Instantly share code, notes, and snippets.

@tonsky
Last active August 29, 2015 13:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tonsky/10361119 to your computer and use it in GitHub Desktop.
Save tonsky/10361119 to your computer and use it in GitHub Desktop.
(defn opening? [sym]
(when (symbol? sym)
(.endsWith (name sym) ">")))
(defn closing? [sym]
(when (symbol? sym)
(.startsWith (name sym) "<")))
(defn split-closing [t exprs]
(split-with
(fn [[tid expr]]
(not
(and (= tid t)
(closing? expr))))
exprs))
(defn parse-sym [sym]
(->> (name sym)
(re-matches #"<?([^<>]+)>?")
second
symbol))
(defn print-expr [expr]
`(println ~(str (first expr) ": ") '~(second expr)))
(defmacro thread [& body]
`(doto
(Thread. (fn [] ~@body))
(.start)))
(defn expand-subtree [t exprs]
(loop [exprs exprs
acc []]
(let [[tid expr] (first exprs)
acc (conj acc `(Thread/sleep 100))]
(cond
(nil? tid)
(list* acc)
(not= tid t)
(recur (next exprs) acc)
(opening? expr)
(let [[sub-exprs cont-exprs] (split-closing t (next exprs))]
(recur
(next cont-exprs)
(conj acc
(print-expr [tid expr])
(list*
(parse-sym expr)
(expand-subtree t sub-exprs))
(print-expr (first cont-exprs)))))
:else
(recur (next exprs)
(conj acc `(do
~(print-expr [tid expr])
~expr)))))))
(defmacro in-threads [& body]
(let [exprs (partition 2 body)
threads (->> exprs (map first) set)]
`(do
~@(for [t threads]
`(thread
~@(expand-subtree t exprs))))))
(let [x (ref 0)]
(in-threads
1 dosync>
1 (ref-set x 1)
1 <dosync
2 dosync>
2 (ref-set x 2)
2 <dosync
3 (println "x =" @x)
))
;; Nothing special here, transactions do not affect each other
;;
;; 1: dosync>
;; 1: (ref-set x 1)
;; 1: <dosync
;; 2: dosync>
;; 2: (ref-set x 2)
;; 2: <dosync
;; 3: (println x = (clojure.core/deref x))
;; x = 2
(let [x (ref 0)]
(in-threads
1 dosync>
2 dosync>
2 (ref-set x 2)
2 <dosync
1 (ref-set x 1)
1 <dosync
3 (println "x =" @x)
))
;; Here we see that 1 started transaction, then 2 changed value, then 1 tries to change value and 1 gets restarted
;;
;; 1: dosync>
;; 2: dosync>
;; 2: (ref-set x 2)
;; 2: <dosync
;; 1: (ref-set x 1)
;; 3: (println x = (clojure.core/deref x))
;; x = 2
;; 1: (ref-set x 1)
;; 1: <dosync
(let [x (ref 0)]
(in-threads
1 dosync>
1 (ref-set x 1)
2 dosync>
2 (ref-set x 2)
2 <dosync
1 <dosync
3 (println "x =" @x)
))
;; Here we see that 1 started transaction and changed value, then 2 tries to change value and 2 cannot commit before 1 commits
;;
;; 1: dosync>
;; 1: (ref-set x 1)
;; 2: dosync>
;; 2: (ref-set x 2)
;; 1: <dosync
;; 2: (ref-set x 2)
;; 3: (println x = (clojure.core/deref x))
;; x = 1
;; 2: (ref-set x 2)
;; 2: <dosync
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment