diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/jvm/clojure/lang/LockingTransaction.java | 9 | ||||
-rw-r--r-- | src/jvm/clojure/lang/TransactionalHashMap.java | 192 |
2 files changed, 197 insertions, 4 deletions
diff --git a/src/jvm/clojure/lang/LockingTransaction.java b/src/jvm/clojure/lang/LockingTransaction.java index 3a8eaee2..80e1a1e7 100644 --- a/src/jvm/clojure/lang/LockingTransaction.java +++ b/src/jvm/clojure/lang/LockingTransaction.java @@ -14,6 +14,7 @@ package clojure.lang; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.Callable; @SuppressWarnings({"SynchronizeOnNonFinalField"}) public class LockingTransaction{ @@ -187,18 +188,18 @@ static LockingTransaction getRunning(){ return t; } -static public Object runInTransaction(IFn fn) throws Exception{ +static public Object runInTransaction(Callable fn) throws Exception{ LockingTransaction t = transaction.get(); if(t == null) transaction.set(t = new LockingTransaction()); if(t.info != null) - return fn.invoke(); + return fn.call(); return t.run(fn); } -Object run(IFn fn) throws Exception{ +Object run(Callable fn) throws Exception{ boolean done = false; Object ret = null; ArrayList<Ref> locked = new ArrayList<Ref>(); @@ -214,7 +215,7 @@ Object run(IFn fn) throws Exception{ startTime = System.nanoTime(); } info = new Info(RUNNING, startPoint); - ret = fn.invoke(); + ret = fn.call(); //make sure no one has killed us before this point, and can't from now on if(info.status.compareAndSet(RUNNING, COMMITTING)) { diff --git a/src/jvm/clojure/lang/TransactionalHashMap.java b/src/jvm/clojure/lang/TransactionalHashMap.java new file mode 100644 index 00000000..1bc3fc06 --- /dev/null +++ b/src/jvm/clojure/lang/TransactionalHashMap.java @@ -0,0 +1,192 @@ +/** + * Copyright (c) Rich Hickey. All rights reserved. + * The use and distribution terms for this software are covered by the + * Common Public License 1.0 (http://opensource.org/licenses/cpl.php) + * which can be found in the file CPL.TXT at the root of this distribution. + * By using this software in any fashion, you are agreeing to be bound by + * the terms of this license. + * You must not remove this notice, or any other, from this software. + **/ + +/* rich Jul 31, 2008 */ + +package clojure.lang; + +import java.util.concurrent.ConcurrentMap; +import java.util.*; + +public class TransactionalHashMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>{ +final Ref[] bins; + +IPersistentMap mapAt(int bin){ + return (IPersistentMap) bins[bin].get(); +} + +int binFor(Object k){ + return k.hashCode() % bins.length; +} + +Entry entryAt(Object k){ + return mapAt(binFor(k)).entryAt(k); +} + +public TransactionalHashMap() throws Exception{ + this(421); +} + +public TransactionalHashMap(int nBins) throws Exception{ + bins = new Ref[nBins]; + for(int i = 0; i < nBins; i++) + bins[i] = new Ref(PersistentHashMap.EMPTY); +} + +public TransactionalHashMap(Map<? extends K, ? extends V> m) throws Exception{ + this(m.size()); + putAll(m); +} + +public int size(){ + int n = 0; + for(int i = 0; i < bins.length; i++) + { + n += mapAt(i).count(); + } + return n; +} + +public boolean isEmpty(){ + return size() == 0; +} + +public boolean containsKey(Object k){ + return entryAt(k) != null; +} + +public V get(Object k){ + Entry e = entryAt(k); + if(e != null) + return (V) e.getValue(); + return null; +} + +public V put(K k, V v){ + Ref r = bins[binFor(k)]; + IPersistentMap map = (IPersistentMap) r.get(); + Object ret = map.valAt(k); + r.set(map.assoc(k, v)); + return (V) ret; +} + +public V remove(Object k){ + Ref r = bins[binFor(k)]; + IPersistentMap map = (IPersistentMap) r.get(); + Object ret = map.valAt(k); + //checked exceptions are a bad idea, especially in an interface + try + { + r.set(map.without(k)); + } + catch(Exception e) + { + throw new RuntimeException(e); + } + return (V) ret; +} + +public void putAll(Map<? extends K, ? extends V> map){ + for(Iterator i = map.entrySet().iterator(); i.hasNext();) + { + Entry<K, V> e = (Entry) i.next(); + put(e.getKey(), e.getValue()); + } +} + +public void clear(){ + for(int i = 0; i < bins.length; i++) + { + Ref r = bins[i]; + IPersistentMap map = (IPersistentMap) r.get(); + if(map.count() > 0) + { + r.set(PersistentHashMap.EMPTY); + } + } +} + +public Set<Entry<K, V>> entrySet(){ + final ArrayList<Map.Entry<K, V>> entries = new ArrayList(bins.length); + for(int i = 0; i < bins.length; i++) + { + IPersistentMap map = mapAt(i); + if(map.count() > 0) + entries.addAll((Collection) map); + } + return new AbstractSet<Entry<K, V>>(){ + public Iterator iterator(){ + return Collections.unmodifiableList(entries).iterator(); + } + + public int size(){ + return entries.size(); + } + }; +} + +public V putIfAbsent(K k, V v){ + Ref r = bins[binFor(k)]; + IPersistentMap map = (IPersistentMap) r.get(); + Entry e = map.entryAt(k); + if(e == null) + { + r.set(map.assoc(k, v)); + return null; + } + else + return (V) e.getValue(); +} + +public boolean remove(Object k, Object v){ + Ref r = bins[binFor(k)]; + IPersistentMap map = (IPersistentMap) r.get(); + Entry e = map.entryAt(k); + if(e != null && e.getValue().equals(v)) + { + //checked exceptions are a bad idea, especially in an interface + try + { + r.set(map.without(k)); + } + catch(Exception ex) + { + throw new RuntimeException(ex); + } + return true; + } + return false; +} + +public boolean replace(K k, V oldv, V newv){ + Ref r = bins[binFor(k)]; + IPersistentMap map = (IPersistentMap) r.get(); + Entry e = map.entryAt(k); + if(e != null && e.getValue().equals(oldv)) + { + r.set(map.assoc(k, newv)); + return true; + } + return false; +} + +public V replace(K k, V v){ + Ref r = bins[binFor(k)]; + IPersistentMap map = (IPersistentMap) r.get(); + Entry e = map.entryAt(k); + if(e != null) + { + r.set(map.assoc(k, v)); + return (V) e.getValue(); + } + return null; +} + +} |