summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/clj/clojure/core.clj39
-rw-r--r--src/jvm/clojure/lang/LockingTransaction.java5
-rw-r--r--src/jvm/clojure/lang/Ref.java44
3 files changed, 85 insertions, 3 deletions
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index ba674763..38c58daf 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -1268,14 +1268,30 @@
:validator validate-fn
+ :min-history (default 0)
+ :max-history (default 10)
+
If metadata-map is supplied, it will be come the metadata on the
ref. validate-fn must be nil or a side-effect-free fn of one
argument, which will be passed the intended new state on any state
change. If the new state is unacceptable, the validate-fn should
return false or throw an exception. validate-fn will be called on
- transaction commit, when all refs have their final values."
+ transaction commit, when all refs have their final values.
+
+ Normally refs accumulate history dynamically as needed to deal with
+ read demands. If you know in advance you will need history you can
+ set :min-history to ensure it will be available when first needed (instead
+ of after a read fault). History is limited, and the limit can be set
+ with :max-history."
([x] (new clojure.lang.Ref x))
- ([x & options] (setup-reference (ref x) options)))
+ ([x & options]
+ (let [r #^clojure.lang.Ref (setup-reference (ref x) options)
+ opts (apply hash-map options)]
+ (when (:max-history opts)
+ (.setMaxHistory r (:max-history opts)))
+ (when (:min-history opts)
+ (.setMinHistory r (:min-history opts)))
+ r)))
(defn deref
"Also reader macro: @ref/@agent/@var/@atom/@delay/@future. Within a transaction,
@@ -1383,6 +1399,25 @@
[#^clojure.lang.Ref ref val]
(. ref (set val)))
+(defn ref-history-count
+ "Returns the history count of a ref"
+ [#^clojure.lang.Ref ref]
+ (.getHistoryCount ref))
+
+(defn ref-min-history
+ "Gets the min-history of a ref, or sets it and returns the ref"
+ ([#^clojure.lang.Ref ref]
+ (.getMinHistory ref))
+ ([#^clojure.lang.Ref ref n]
+ (.setMinHistory ref n)))
+
+(defn ref-max-history
+ "Gets the max-history of a ref, or sets it and returns the ref"
+ ([#^clojure.lang.Ref ref]
+ (.getMaxHistory ref))
+ ([#^clojure.lang.Ref ref n]
+ (.setMaxHistory ref n)))
+
(defn ensure
"Must be called in a transaction. Protects the ref from modification
by other transactions. Returns the in-transaction-value of
diff --git a/src/jvm/clojure/lang/LockingTransaction.java b/src/jvm/clojure/lang/LockingTransaction.java
index 66a262e7..4976d169 100644
--- a/src/jvm/clojure/lang/LockingTransaction.java
+++ b/src/jvm/clojure/lang/LockingTransaction.java
@@ -281,11 +281,14 @@ Object run(Callable fn) throws Exception{
Ref ref = e.getKey();
Object oldval = ref.tvals == null ? null : ref.tvals.val;
Object newval = e.getValue();
+ int hcount = ref.histCount();
+
if(ref.tvals == null)
{
ref.tvals = new Ref.TVal(newval, commitPoint, msecs);
}
- else if(ref.faults.get() > 0)
+ else if((ref.faults.get() > 0 && hcount < ref.maxHistory)
+ || hcount < ref.minHistory)
{
ref.tvals = new Ref.TVal(newval, commitPoint, msecs, ref.tvals);
ref.faults.set(0);
diff --git a/src/jvm/clojure/lang/Ref.java b/src/jvm/clojure/lang/Ref.java
index 90faedcc..fef7c439 100644
--- a/src/jvm/clojure/lang/Ref.java
+++ b/src/jvm/clojure/lang/Ref.java
@@ -26,6 +26,24 @@ public class Ref extends ARef implements IFn, Comparable<Ref>, IRef{
return 1;
}
+public int getMinHistory(){
+ return minHistory;
+}
+
+public Ref setMinHistory(int minHistory){
+ this.minHistory = minHistory;
+ return this;
+}
+
+public int getMaxHistory(){
+ return maxHistory;
+}
+
+public Ref setMaxHistory(int maxHistory){
+ this.maxHistory = maxHistory;
+ return this;
+}
+
public static class TVal{
Object val;
long point;
@@ -60,6 +78,9 @@ LockingTransaction.Info tinfo;
//IFn validator;
final long id;
+volatile int minHistory = 0;
+volatile int maxHistory = 10;
+
static final AtomicLong ids = new AtomicLong();
public Ref(Object initVal) throws Exception{
@@ -187,6 +208,29 @@ public void trimHistory(){
}
}
+public int getHistoryCount(){
+ try
+ {
+ lock.writeLock().lock();
+ return histCount();
+ }
+ finally
+ {
+ lock.writeLock().unlock();
+ }
+}
+
+int histCount(){
+ if(tvals == null)
+ return 0;
+ else
+ {
+ int count = 0;
+ for(TVal tv = tvals.next;tv != tvals;tv = tv.next)
+ count++;
+ return count;
+ }
+}
final public IFn fn(){
return (IFn) deref();