possible bug

7 views
Skip to first unread message

doc

unread,
Sep 12, 2010, 3:58:27 PM9/12/10
to Clojure
Hello,

I am relatively new to clojure and lispy languages in general. I
wrote a small program that manipulated thread vars using the binding
macro and I am seeing what seems to be strange behavior.

To illustrate, here is another small program that reproduces what I am
seeing (without all the distraction of the original). There is a
reason for the nested bindings in the original code where it seems a
bit contrived here. Regardless, if it is something I am doing wrong,
please let me know.

------------

(declare x y)

(def rnd1 (java.util.Random.))
(def rnd2 (java.util.Random.))

(defn throw-random-exception [c]
(let [value (.nextInt rnd1 100)]
(if (= value 50)
(throw (Exception. (str "Random Exception after count = "
c))))))

(defn change-value-of-thread-local-var []
(set! x (conj x (.nextInt rnd2 100))))

(defn test-binding []
(binding [x [0 1 2 3 4 5 6]]
(try
(loop [c 0]
(do
(change-value-of-thread-local-var)
(throw-random-exception c))
(recur (inc c)))
(catch Exception e (println (str x "\n" (.getMessage e)))))))

(defn test-nested-binding []
(binding [x [0 1 2 3 4 5 6]]
(try
(loop [c 0]
(binding [y [7 8 9]]
(do
(change-value-of-thread-local-var)
(throw-random-exception c))
(recur (inc c))))
(catch Exception e (println (str x "\n" (.getMessage e)))))))


------------

If I load this as a file at the REPL and then run "(test-binding)" the
value of x is unbound when the function returns. However if you run
"(test-nested-binding)" then the value of BOTH x and y have a bound
value when the function returns. Is this a bug? If so, where do I
report it? If not, what am I doing wrong? I have also moved the
binding of x inside the "try" and it has the same behavior.

thanks,

take care,

Mike

Chouser

unread,
Sep 13, 2010, 3:01:37 AM9/13/10
to clo...@googlegroups.com

For me, x and y are unbound after running either function on a recent
master branch of Clojure. What version of Clojure are you using?

user=> (clojure-version)
"1.3.0-master-SNAPSHOT"

If you're using something earlier than the 1.2 release, I'd suggest
you upgrade to 1.2 and try again.

--Chouser
http://joyofclojure.com/

doc

unread,
Sep 13, 2010, 11:37:50 AM9/13/10
to Clojure
it was on 1.2.0-master but I will upgrade to 1.3.0-master

thanks! :-)

On Sep 13, 3:01 am, Chouser <chou...@gmail.com> wrote:

doc

unread,
Sep 13, 2010, 8:24:14 PM9/13/10
to Clojure
I sent a reply earlier but I don't think it went through. I tried it
again with 1.3.0-master-SNAPSHOT and it still had x and y bound after
the test-nested-binding function ran. Not sure what I am doing wrong
or have misconfigured? Any insight would be appreciated.

take care,

Mike

doc

unread,
Sep 13, 2010, 12:48:02 PM9/13/10
to Clojure
OK, I upgraded to 1.3.0-master-SNAPSHOT but I am still getting a bound
x and y after running the test-nested-binding function. There must be
something else not quite right.

for my REPL I am using:

ubuntu linux 10.04
java sdk 1.6.0_21
clojure 1.3.0-master-SNAPSHOT
clojure-contrib 1.3.0-SNAPSHOT
jLine 0.9.95-SNAPSHOT

here is a cut/paste of what I did at the repl:
---------------
Clojure 1.3.0-master-SNAPSHOT
user=> (load-file "TestBinding.clj")
#'user/test-nested-binding
user=> (test-binding)
[0 1 2 3 4 5 6 40 80 19 72 31 73 40 61 99 25 13 14 91 81 22 94 87 25
33 11 60 3 6 14 13 77 94 77 83 16 88 38 7 59 79 74 5 98 57 95 90 86 14
64 63 2 34 52 30 86 72 14 25 7 54 83 53 86 66 57 86 55 52 97 8 20 3 21
7 6 66 73 48 16 43 3 43 91 42 21 49 95 25 31 71 4 21 18 78 79 1 57 55
76 42 82 99 49 54 41 3 34 27 26 65 33 37 68 18 88 64 23 81 13 99 55 14
94 61 35 36 32 54 97 26 34 8 26 60 7 97 38 18 84 24 74 57 95 67 4 18
25 75 42 21 75 87 21 94 82 64 80 56 28 34 88 42 3 13 20 28 97 87 68 76
46 33 38 33 22 9 97 37 63 26 13 9 55 52 91 55 74 12 99 69 4 96 0 9 54
60 41 82 4 0 99 24 38 30 74]
Random Exception after count = 199
nil
user=> x
IllegalStateException Var user/x is unbound. clojure.lang.Var.deref
(Var.java:142)
user=> (test-nested-binding)
[0 1 2 3 4 5 6 52 76 96 80 15 52 35 56 66 78 59 72 36 80 59 52 15 56
65 92 41 68 59 64 42 40 77 78 95 34 51 28 23 99 40 45 2 82 21 11 6 52
28 28 98 19 76 90 79 87 65 71 35 19 29 1 52 96 94 51 49 69 24 54 76 87
88 52 48 64 56 71 64 72 86 22 37 63 10 85 20]
Random Exception after count = 80
nil
user=> x
[0 1 2 3 4 5 6 52 76 96 80 15 52 35 56 66 78 59 72 36 80 59 52 15 56
65 92 41 68 59 64 42 40 77 78 95 34 51 28 23 99 40 45 2 82 21 11 6 52
28 28 98 19 76 90 79 87 65 71 35 19 29 1 52 96 94 51 49 69 24 54 76 87
88 52 48 64 56 71 64 72 86 22 37 63 10 85 20]
user=> (clojure-version)
"1.3.0-master-SNAPSHOT"
user=>
---------------

(I also ran this with just a plain clojure repl (no contrib or jline
jars in the classpath))

thanks for any help

take care,

Mike

On Sep 13, 11:37 am, doc <mdpendergr...@gmail.com> wrote:

Meikel Brandmeyer

unread,
Sep 14, 2010, 1:38:46 AM9/14/10
to Clojure
Hi,

I can reproduce it with 1.2 and 1.3.0-master-20100911.130147-5. And I
think I know, what the problem is: You must not recur out of a
binding. The binding implicitely contains a try with cleanup code in
the finally clause. It seems that recur bypasses the finally and
executes again the binding. This pushes another frame of the bindings
onto the stack, but the old frame is not popped. When the exception is
thrown, the finally part gets executed and one frame is popped, but
the additional frames remain. The stack of bindings is now out of
sync. Hence your result. Throwing the exception in the first iteration
will leave the bindings stack intact.

user=> (binding [x [1 2 3]] (loop [c 0] (binding [y [4 5 6]] (throw
(Exception. "BOOOM")))))
Exception BOOOM user/eval13 (NO_SOURCE_FILE:9)
user=> x
IllegalStateException Var user/x is unbound. clojure.lang.Var.deref
(Var.java:142)
user=> y
IllegalStateException Var user/y is unbound. clojure.lang.Var.deref
(Var.java:142)

Please find the following code, which works as expected:

(defn test-fixed-nested-binding
[]
(binding [x [0 1 2 3 4 5 6]]
(try
(binding [y [7 8 9]]
(loop [c 0]
(change-value-of-thread-local-var)
(throw-random-exception c)
(recur (inc c))))
(catch Exception e (println (str x "\n" (.getMessage e)))))))

Since recur is local, I think "don't do it" is a valid short- to mid-
term fix.

Sincerely
Meikel

doc

unread,
Sep 14, 2010, 9:17:50 AM9/14/10
to Clojure
That works. Thanks!

take care,

Mike
Reply all
Reply to author
Forward
0 new messages