Skip to content

Instantly share code, notes, and snippets.

@tobias
Created November 22, 2013 16:03
Show Gist options
  • Select an option

  • Save tobias/7602270 to your computer and use it in GitHub Desktop.

Select an option

Save tobias/7602270 to your computer and use it in GitHub Desktop.
commit 7d4a8f4724ea8f82709550d8a55798c87bfab802 (HEAD, refs/heads/clj-1299-no-nil)
Author: Toby Crawley <toby@tcrawley.org>
Date: Fri Nov 22 09:49:44 2013 -0500
Prevent a conveyed binding frame from being removed by a popThreadBindings call [CLJ-1299]
This introduces a TOP frame that is used to mark the top of a
push/pop stack. It also removes unnecessary nil checks around dvals.
Modified src/jvm/clojure/lang/Var.java
diff --git a/src/jvm/clojure/lang/Var.java b/src/jvm/clojure/lang/Var.java
index 7463337..af942f6 100644
--- a/src/jvm/clojure/lang/Var.java
+++ b/src/jvm/clojure/lang/Var.java
@@ -45,17 +45,13 @@ static public class Unbound extends AFn{
}
static class Frame{
+ final static Frame TOP = new Frame(PersistentHashMap.EMPTY, null);
//Var->TBox
Associative bindings;
//Var->val
// Associative frameBindings;
Frame prev;
-
- public Frame(){
- this(PersistentHashMap.EMPTY, null);
- }
-
public Frame(Associative bindings, Frame prev){
// this.frameBindings = frameBindings;
this.bindings = bindings;
@@ -63,9 +59,7 @@ static class Frame{
}
protected Object clone() {
- Frame f = new Frame();
- f.bindings = this.bindings;
- return f;
+ return new Frame(this.bindings, null);
}
}
@@ -73,7 +67,7 @@ static class Frame{
static final ThreadLocal<Frame> dvals = new ThreadLocal<Frame>(){
protected Frame initialValue(){
- return new Frame();
+ return Frame.TOP;
}
};
@@ -96,17 +90,11 @@ public final Namespace ns;
//IPersistentMap _meta;
public static Object getThreadBindingFrame(){
- Frame f = dvals.get();
- if(f != null)
- return f;
- return new Frame();
+ return dvals.get();
}
public static Object cloneThreadBindingFrame(){
- Frame f = dvals.get();
- if(f != null)
- return f.clone();
- return new Frame();
+ return dvals.get().clone();
}
public static void resetThreadBindingFrame(Object frame){
@@ -359,11 +347,10 @@ public static void pushThreadBindings(Associative bindings){
}
public static void popThreadBindings(){
- Frame f = dvals.get();
- if(f.prev == null)
+ Frame f = dvals.get().prev;
+ if (f == null) {
throw new IllegalStateException("Pop without matching push");
- f = f.prev;
- if (f.prev == null) {
+ } else if (f == Frame.TOP) {
dvals.remove();
} else {
dvals.set(f);
Modified test/clojure/test_clojure/parallel.clj
diff --git a/test/clojure/test_clojure/parallel.clj b/test/clojure/test_clojure/parallel.clj
index fb98d60..357e961 100644
--- a/test/clojure/test_clojure/parallel.clj
+++ b/test/clojure/test_clojure/parallel.clj
@@ -27,3 +27,14 @@
;; regression fixed in r1218; was OutOfMemoryError
(is (= '(1) (pmap inc [0]))))
+
+(def ^:dynamic *test-value* 1)
+
+(deftest future-fn-properly-retains-conveyed-bindings
+ (let [a (atom [])]
+ (binding [*test-value* 2]
+ @(future (dotimes [_ 3]
+ ;; we need some binding to trigger binding pop
+ (binding [*print-dup* false]
+ (swap! a conj *test-value*))))
+ (is (= [2 2 2] @a)))))
[back]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment