diff options
-rw-r--r-- | src/clj/clojure/core.clj | 12 | ||||
-rw-r--r-- | src/clj/clojure/zip.clj | 10 | ||||
-rw-r--r-- | src/jvm/clojure/lang/Keyword.java | 4 | ||||
-rw-r--r-- | src/jvm/clojure/lang/LispReader.java | 15 | ||||
-rw-r--r-- | src/jvm/clojure/lang/LockingTransaction.java | 108 | ||||
-rw-r--r-- | src/jvm/clojure/lang/Ref.java | 2 | ||||
-rw-r--r-- | src/jvm/clojure/lang/Var.java | 13 | ||||
-rw-r--r-- | test/clojure/test_clojure/numbers.clj | 20 | ||||
-rw-r--r-- | test/clojure/test_clojure/reader.clj | 13 |
9 files changed, 151 insertions, 46 deletions
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj index a2907aa8..ef5f2482 100644 --- a/src/clj/clojure/core.clj +++ b/src/clj/clojure/core.clj @@ -357,14 +357,14 @@ (defn symbol "Returns a Symbol with the given namespace and name." - ([name] (if (symbol? name) name (. clojure.lang.Symbol (intern name)))) - ([ns name] (. clojure.lang.Symbol (intern ns name)))) + ([name] (if (symbol? name) name (clojure.lang.Symbol/intern name))) + ([ns name] (clojure.lang.Symbol/intern ns name))) (defn keyword "Returns a Keyword with the given namespace and name. Do not use : in the keyword strings, it will be added automatically." - ([name] (if (keyword? name) name (. clojure.lang.Keyword (intern nil name)))) - ([ns name] (. clojure.lang.Keyword (intern ns name)))) + ([name] (if (keyword? name) name (clojure.lang.Keyword/intern name))) + ([ns name] (clojure.lang.Keyword/intern ns name))) (defn gensym "Returns a new symbol with a unique name. If a prefix string is @@ -4307,8 +4307,8 @@ (invoke [x] (locking d (if (pos? (.getCount d)) - (do (.countDown d) - (reset! v x) + (do (reset! v x) + (.countDown d) this) (throw (IllegalStateException. "Multiple deliver calls to a promise")))))))) diff --git a/src/clj/clojure/zip.clj b/src/clj/clojure/zip.clj index 81b09060..00cc3be5 100644 --- a/src/clj/clojure/zip.clj +++ b/src/clj/clojure/zip.clj @@ -31,12 +31,18 @@ (defn seq-zip "Returns a zipper for nested sequences, given a root sequence" [root] - (zipper seq? identity (fn [node children] children) root)) + (zipper seq? + identity + (fn [node children] (with-meta children (meta node))) + root)) (defn vector-zip "Returns a zipper for nested vectors, given a root vector" [root] - (zipper vector? seq (fn [node children] (apply vector children)) root)) + (zipper vector? + seq + (fn [node children] (with-meta (vec children) (meta node))) + root)) (defn xml-zip "Returns a zipper for xml elements (as from xml/parse), diff --git a/src/jvm/clojure/lang/Keyword.java b/src/jvm/clojure/lang/Keyword.java index fcb814f6..9b5aea9a 100644 --- a/src/jvm/clojure/lang/Keyword.java +++ b/src/jvm/clojure/lang/Keyword.java @@ -30,6 +30,10 @@ public static Keyword intern(String ns, String name){ return intern(Symbol.intern(ns, name)); } +public static Keyword intern(String nsname){ + return intern(Symbol.intern(nsname)); +} + private Keyword(Symbol sym){ this.sym = sym; } diff --git a/src/jvm/clojure/lang/LispReader.java b/src/jvm/clojure/lang/LispReader.java index 5bf22e66..368bc1fa 100644 --- a/src/jvm/clojure/lang/LispReader.java +++ b/src/jvm/clojure/lang/LispReader.java @@ -704,7 +704,20 @@ public static class SyntaxQuoteReader extends AFn{ // Simply quote method names. } else - sym = Compiler.resolveSymbol(sym); + { + Object maybeClass = null; + if(sym.ns != null) + maybeClass = Compiler.currentNS().getMapping( + Symbol.intern(null, sym.ns)); + if(maybeClass instanceof Class) + { + // Classname/foo -> package.qualified.Classname/foo + sym = Symbol.intern( + ((Class)maybeClass).getName(), sym.name); + } + else + sym = Compiler.resolveSymbol(sym); + } ret = RT.list(Compiler.QUOTE, sym); } else if(isUnquote(form)) diff --git a/src/jvm/clojure/lang/LockingTransaction.java b/src/jvm/clojure/lang/LockingTransaction.java index 4976d169..bb61e1b6 100644 --- a/src/jvm/clojure/lang/LockingTransaction.java +++ b/src/jvm/clojure/lang/LockingTransaction.java @@ -16,6 +16,7 @@ import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; @SuppressWarnings({"SynchronizeOnNonFinalField"}) public class LockingTransaction{ @@ -104,13 +105,32 @@ final HashMap<Ref, Object> vals = new HashMap<Ref, Object>(); final HashSet<Ref> sets = new HashSet<Ref>(); final TreeMap<Ref, ArrayList<CFn>> commutes = new TreeMap<Ref, ArrayList<CFn>>(); +final HashSet<Ref> ensures = new HashSet<Ref>(); //all hold readLock + + +void tryWriteLock(Ref ref){ + try + { + if(!ref.lock.writeLock().tryLock(LOCK_WAIT_MSECS, TimeUnit.MILLISECONDS)) + throw retryex; + } + catch(InterruptedException e) + { + throw retryex; + } +} //returns the most recent val Object lock(Ref ref){ - boolean unlocked = false; + //can't upgrade readLock, so release it + releaseIfEnsured(ref); + + boolean unlocked = true; try { - ref.lock.writeLock().lock(); + tryWriteLock(ref); + unlocked = false; + if(ref.tvals != null && ref.tvals.point > readPoint) throw retryex; Info refinfo = ref.tinfo; @@ -122,9 +142,23 @@ Object lock(Ref ref){ { ref.lock.writeLock().unlock(); unlocked = true; - //stop prior to blocking - stop(RETRY); - synchronized(refinfo) + return blockAndBail(refinfo); + } + } + ref.tinfo = info; + return ref.tvals == null ? null : ref.tvals.val; + } + finally + { + if(!unlocked) + ref.lock.writeLock().unlock(); + } +} + +private Object blockAndBail(Info refinfo){ +//stop prior to blocking + stop(RETRY); + synchronized(refinfo) { if(refinfo.running()) { @@ -133,20 +167,18 @@ Object lock(Ref ref){ refinfo.wait(LOCK_WAIT_MSECS); } catch(InterruptedException e) - { - } - } - } - throw retryex; + { } } - ref.tinfo = info; - return ref.tvals == null ? null : ref.tvals.val; } - finally + throw retryex; +} + +private void releaseIfEnsured(Ref ref){ + if(ensures.contains(ref)) { - if(!unlocked) - ref.lock.writeLock().unlock(); + ensures.remove(ref); + ref.lock.readLock().unlock(); } } @@ -240,8 +272,16 @@ Object run(Callable fn) throws Exception{ for(Map.Entry<Ref, ArrayList<CFn>> e : commutes.entrySet()) { Ref ref = e.getKey(); - ref.lock.writeLock().lock(); + if(sets.contains(ref)) continue; + + boolean wasEnsured = ensures.contains(ref); + //can't upgrade readLock, so release it + releaseIfEnsured(ref); + tryWriteLock(ref); locked.add(ref); + if(wasEnsured && ref.tvals != null && ref.tvals.point > readPoint) + throw retryex; + Info refinfo = ref.tinfo; if(refinfo != null && refinfo != info && refinfo.running()) { @@ -249,8 +289,7 @@ Object run(Callable fn) throws Exception{ throw retryex; } Object val = ref.tvals == null ? null : ref.tvals.val; - if(!sets.contains(ref)) - vals.put(ref, val); + vals.put(ref, val); for(CFn f : e.getValue()) { vals.put(ref, f.fn.applyTo(RT.cons(vals.get(ref), f.args))); @@ -258,11 +297,8 @@ Object run(Callable fn) throws Exception{ } for(Ref ref : sets) { - if(!commutes.containsKey(ref)) - { - ref.lock.writeLock().lock(); - locked.add(ref); - } + tryWriteLock(ref); + locked.add(ref); } //validate and enqueue notifications @@ -319,6 +355,11 @@ Object run(Callable fn) throws Exception{ locked.get(k).lock.writeLock().unlock(); } locked.clear(); + for(Ref r : ensures) + { + r.lock.readLock().unlock(); + } + ensures.clear(); stop(done ? COMMITTED : RETRY); try { @@ -391,10 +432,27 @@ Object doSet(Ref ref, Object val){ return val; } -void doTouch(Ref ref){ +void doEnsure(Ref ref){ if(!info.running()) throw retryex; - lock(ref); + if(ensures.contains(ref)) + return; + ref.lock.readLock().lock(); + + Info refinfo = ref.tinfo; + + //writer exists + if(refinfo != null && refinfo.running()) + { + ref.lock.readLock().unlock(); + + if(refinfo != info) //not us, ensure is doomed + { + blockAndBail(refinfo); + } + } + else + ensures.add(ref); } Object doCommute(Ref ref, IFn fn, ISeq args) throws Exception{ diff --git a/src/jvm/clojure/lang/Ref.java b/src/jvm/clojure/lang/Ref.java index fef7c439..a4626acb 100644 --- a/src/jvm/clojure/lang/Ref.java +++ b/src/jvm/clojure/lang/Ref.java @@ -175,7 +175,7 @@ public Object alter(IFn fn, ISeq args) throws Exception{ } public void touch(){ - LockingTransaction.getEx().doTouch(this); + LockingTransaction.getEx().doEnsure(this); } //*/ diff --git a/src/jvm/clojure/lang/Var.java b/src/jvm/clojure/lang/Var.java index 598a5785..e9286c36 100644 --- a/src/jvm/clojure/lang/Var.java +++ b/src/jvm/clojure/lang/Var.java @@ -309,6 +309,19 @@ public static void releaseThreadBindings(){ dvals.set(null); } +public static Associative getThreadBindings(){ + Frame f = dvals.get(); + IPersistentMap ret = PersistentHashMap.EMPTY; + for(ISeq bs = f.bindings.seq(); bs != null; bs = bs.next()) + { + IMapEntry e = (IMapEntry) bs.first(); + Var v = (Var) e.key(); + Box b = (Box) e.val(); + ret = ret.assoc(v, b.val); + } + return ret; +} + final Box getThreadBinding(){ if(count.get() > 0) { diff --git a/test/clojure/test_clojure/numbers.clj b/test/clojure/test_clojure/numbers.clj index 9f3cfdb2..597cedff 100644 --- a/test/clojure/test_clojure/numbers.clj +++ b/test/clojure/test_clojure/numbers.clj @@ -24,7 +24,7 @@ (deftest Coerced-Byte (let [v (byte 3)] - (are [x] + (are [x] (true? x) (instance? Byte v) (number? v) (integer? v) @@ -32,7 +32,7 @@ (deftest Coerced-Short (let [v (short 3)] - (are [x] + (are [x] (true? x) (instance? Short v) (number? v) (integer? v) @@ -40,7 +40,7 @@ (deftest Coerced-Integer (let [v (int 3)] - (are [x] + (are [x] (true? x) (instance? Integer v) (number? v) (integer? v) @@ -48,7 +48,7 @@ (deftest Coerced-Long (let [v (long 3)] - (are [x] + (are [x] (true? x) (instance? Long v) (number? v) (integer? v) @@ -56,7 +56,7 @@ (deftest Coerced-BigInteger (let [v (bigint 3)] - (are [x] + (are [x] (true? x) (instance? BigInteger v) (number? v) (integer? v) @@ -64,21 +64,21 @@ (deftest Coerced-Float (let [v (float 3)] - (are [x] + (are [x] (true? x) (instance? Float v) (number? v) (float? v)))) (deftest Coerced-Double (let [v (double 3)] - (are [x] + (are [x] (true? x) (instance? Double v) (number? v) (float? v)))) (deftest Coerced-BigDecimal (let [v (bigdec 3)] - (are [x] + (are [x] (true? x) (instance? BigDecimal v) (number? v) (decimal? v) @@ -370,7 +370,7 @@ ;; even? odd? (deftest test-even? - (are [x] + (are [x] (true? x) (even? -4) (not (even? -3)) (even? 0) @@ -380,7 +380,7 @@ (is (thrown? ArithmeticException (even? (double 10))))) (deftest test-odd? - (are [x] + (are [x] (true? x) (not (odd? -4)) (odd? -3) (not (odd? 0)) diff --git a/test/clojure/test_clojure/reader.clj b/test/clojure/test_clojure/reader.clj index b04543a9..20b1bfac 100644 --- a/test/clojure/test_clojure/reader.clj +++ b/test/clojure/test_clojure/reader.clj @@ -222,7 +222,18 @@ ;; Keywords -(deftest t-Keywords) +(deftest t-Keywords + (is (= :abc (keyword "abc"))) + (is (= :abc (keyword 'abc))) + (is (= :*+!-_? (keyword "*+!-_?"))) + (is (= :abc:def:ghi (keyword "abc:def:ghi"))) + (is (= :abc/def (keyword "abc" "def"))) + (is (= :abc/def (keyword 'abc/def))) + (is (= :abc.def/ghi (keyword "abc.def" "ghi"))) + (is (= :abc/def.ghi (keyword "abc" "def.ghi"))) + (is (= :abc:def/ghi:jkl.mno (keyword "abc:def" "ghi:jkl.mno"))) + (is (instance? clojure.lang.Keyword :alphabet)) + ) ;; Lists |