diff --git a/stm-lib/src/main/groovy/gvystm/STM.groovy b/stm-lib/src/main/groovy/gvystm/STM.groovy index c15dab0..dcd0e00 100755 --- a/stm-lib/src/main/groovy/gvystm/STM.groovy +++ b/stm-lib/src/main/groovy/gvystm/STM.groovy @@ -3,7 +3,10 @@ package gvystm import clojure.lang.LockingTransaction import clojure.lang.Atom import clojure.lang.Ref +import clojure.lang.Var import clojure.lang.IRef +import clojure.lang.PersistentHashMap +import clojure.lang.Associative import java.util.concurrent.Callable class STM { @@ -32,6 +35,10 @@ class STM { r.deref() } + static Object deref(IRef r) { + r.deref(); + } + /** Adds a callback that will get invoked when the value of the specified reference/atom/agent/etc. is changed. * See clojure add-watch function. * The specified closure should take 4 arguments - key, reference, old value and new value @@ -48,4 +55,40 @@ class STM { static Object swap(Atom a, Closure c) { return a.swap(new SwapClosureFn(c)) } + + // TODO: add agent support + /** Sets the bindings into the threadlocal scope */ + static void binding(Map bindings, Closure c) { + Map varMap = new HashMap(); + bindings.every { key, value -> + Var v = Var.create(key) + v.setDynamic() + varMap.put(v, value) + } + + Var.pushThreadBindings(PersistentHashMap.create(varMap)) + c() + // TODO: need some finally + Var.popThreadBindings() + } + + /** Calls the specified closure with the current thread bindings */ + static void withCurrentBindings(Closure c) { + //Associative bindings = Var.getThreadBindings(); + c(Var.getThreadBindings()); + } + + // TODO: is there a better way in the clojure collections? + /*private static Map associativeToMap(Associative a) { + IPersistentMap ret = PersistentHashMap.EMPTY; + for(ISeq bs = a.seq(); bs != null; bs = bs.next()) + { + IMapEntry e = (IMapEntry) bs.first(); + Var v = (Var) e.key(); + TBox b = (TBox) e.val(); + ret = ret.assoc(v, b.val); + } + return ret; + + }*/ } diff --git a/stm-lib/src/test/groovy/gvystm/STMTest.groovy b/stm-lib/src/test/groovy/gvystm/STMTest.groovy index 42fe709..73f55a7 100755 --- a/stm-lib/src/test/groovy/gvystm/STMTest.groovy +++ b/stm-lib/src/test/groovy/gvystm/STMTest.groovy @@ -8,6 +8,9 @@ import static org.junit.Assert.assertEquals import static org.junit.Assert.fail import static gvystm.STM.doSync import static gvystm.STM.alter +import static gvystm.STM.binding +import static gvystm.STM.withCurrentBindings +import static gvystm.STM.deref import static gvystm.STM.ensure import static gvystm.STM.addWatch import static gvystm.STM.removeWatch @@ -21,7 +24,7 @@ class STMTest { doSync { alter(valueMapRef) { m -> m.assoc(1,100) } } - assertEquals 100, valueMapRef.deref().get(1); + assertEquals 100, deref(valueMapRef).get(1); } @Test @@ -47,7 +50,7 @@ class STMTest { doSync { ensure(r) Thread.sleep(1000); - alter(r2) { v -> r.deref() * 2 } + alter(r2) { v -> deref(r) * 2 } } } @@ -58,8 +61,8 @@ class STMTest { } Thread.sleep(2000) - assertEquals r.deref(), -1 - assertEquals r2.deref(), 200 + assertEquals deref(r), -1 + assertEquals deref(r2), 200 } @Test @@ -101,7 +104,7 @@ class STMTest { void testAtomSwap() { Atom a = new Atom(100); swap(a) { v -> v * 2 } - assertEquals a.deref(), 200 + assertEquals deref(a), 200 } @Test @@ -116,6 +119,29 @@ class STMTest { } swap(a) { v -> v + 1 } - assertEquals a.deref(), 1 + assertEquals deref(a), 1 + } + + @Test + void testBindings() { + + Thread.start { + binding([name: "Jim", id: 1]) { + testBindingValues("Jim", 1) + } + } + + Thread.start { + binding([name: "Jeff", id: 2]) { + testBindingValues("Jeff", 1) + } + } + } + + void TestBindingValues(String name, int id) { + withCurrentBindings { m -> + assertEquals m.name, name + assertEquals m.id, id + } } }