summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/jvm/clojure/lang/LockingTransaction.java9
-rw-r--r--src/jvm/clojure/lang/TransactionalHashMap.java192
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;
+}
+
+}