From c118f04a07a932d9d9bf81da8cf8f8383be077bb Mon Sep 17 00:00:00 2001 From: Jeremy Thurgood Date: Sat, 20 Jul 2013 14:52:56 +0200 Subject: [PATCH 1/4] Add recursion_guard_outer() for use in hashing mutable objects. --- lib-topaz/thread.rb | 25 +++++++++++++++++++++++++ tests/objects/test_threadobject.py | 15 +++++++++++++++ topaz/objects/threadobject.py | 8 ++++++++ 3 files changed, 48 insertions(+) diff --git a/lib-topaz/thread.rb b/lib-topaz/thread.rb index 95f62d5de..3cf1e78ec 100644 --- a/lib-topaz/thread.rb +++ b/lib-topaz/thread.rb @@ -16,4 +16,29 @@ def abort_on_exception=(value) def abort_on_exception @abort_on_exception ||= (Thread.abort_on_exception || false) end + + def recursion_guard_outer(identifier, obj, &block) + # We want to throw something less likely to be caught accidentally outside + # our own code than the recursion identifier. Ideally this should be an + # object that is unique to this particular recursion guard. Since doing + # that properly requires pushing extra state all the way up into + # ExecutionContext, we do this instead. + throw_symbol = "__recursion_guard_#{identifier}".to_sym + + if self.in_recursion_guard?(identifier) + self.recursion_guard(identifier, obj) do + yield + return false + end + throw(throw_symbol) + else + self.recursion_guard(identifier, obj) do + catch(throw_symbol) do + yield + return false + end + return true + end + end + end end diff --git a/tests/objects/test_threadobject.py b/tests/objects/test_threadobject.py index 64724c196..1179bb167 100644 --- a/tests/objects/test_threadobject.py +++ b/tests/objects/test_threadobject.py @@ -56,3 +56,18 @@ def bar(objs, depth) w_depth, w_symbol = space.listview(w_res) assert space.int_w(w_depth) == 5 assert space.symbol_w(w_symbol) == "a" + + def test_recursion_guard_outer(self, space): + w_res = space.execute(""" + def foo(objs, depth = 0) + obj = objs.shift + Thread.current.recursion_guard_outer(:foo, obj) do + return foo(objs, depth + 1) + end + return [depth, obj] + end + return foo([:a, :b, :c, :a, :d]) + """) + w_depth, w_symbol = space.listview(w_res) + assert space.int_w(w_depth) == 0 + assert space.symbol_w(w_symbol) == "a" diff --git a/topaz/objects/threadobject.py b/topaz/objects/threadobject.py index ed51cd74e..46de258e3 100644 --- a/topaz/objects/threadobject.py +++ b/topaz/objects/threadobject.py @@ -42,3 +42,11 @@ def method_recursion_guard(self, space, w_identifier, w_obj, block): if not in_recursion: space.invoke_block(block, []) return space.newbool(in_recursion) + + @classdef.method("in_recursion_guard?") + def method_in_recursion_guardp(self, space, w_identifier): + ec = space.getexecutioncontext() + identifier = space.str_w(w_identifier) + if identifier in ec.recursive_calls: + return space.w_true + return space.w_false From 7e49d2c05580fcec04f00a702d4f4ed5d1096f9b Mon Sep 17 00:00:00 2001 From: Jeremy Thurgood Date: Sun, 21 Jul 2013 22:43:02 +0200 Subject: [PATCH 2/4] This should be symbol_w, not str_w. --- topaz/objects/threadobject.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/topaz/objects/threadobject.py b/topaz/objects/threadobject.py index 46de258e3..6d9cafaa6 100644 --- a/topaz/objects/threadobject.py +++ b/topaz/objects/threadobject.py @@ -46,7 +46,7 @@ def method_recursion_guard(self, space, w_identifier, w_obj, block): @classdef.method("in_recursion_guard?") def method_in_recursion_guardp(self, space, w_identifier): ec = space.getexecutioncontext() - identifier = space.str_w(w_identifier) + identifier = space.symbol_w(w_identifier) if identifier in ec.recursive_calls: return space.w_true return space.w_false From 6329af76f9919e42687a955917a172b22666d84a Mon Sep 17 00:00:00 2001 From: Jeremy Thurgood Date: Sun, 21 Jul 2013 23:38:38 +0200 Subject: [PATCH 3/4] Move recursion_guard_outer() to Topaz::Thread. --- lib-topaz/thread.rb | 25 ------------------------- lib-topaz/topaz.rb | 1 + lib-topaz/topaz/thread.rb | 26 ++++++++++++++++++++++++++ tests/objects/test_threadobject.py | 2 +- 4 files changed, 28 insertions(+), 26 deletions(-) create mode 100644 lib-topaz/topaz/thread.rb diff --git a/lib-topaz/thread.rb b/lib-topaz/thread.rb index 3cf1e78ec..95f62d5de 100644 --- a/lib-topaz/thread.rb +++ b/lib-topaz/thread.rb @@ -16,29 +16,4 @@ def abort_on_exception=(value) def abort_on_exception @abort_on_exception ||= (Thread.abort_on_exception || false) end - - def recursion_guard_outer(identifier, obj, &block) - # We want to throw something less likely to be caught accidentally outside - # our own code than the recursion identifier. Ideally this should be an - # object that is unique to this particular recursion guard. Since doing - # that properly requires pushing extra state all the way up into - # ExecutionContext, we do this instead. - throw_symbol = "__recursion_guard_#{identifier}".to_sym - - if self.in_recursion_guard?(identifier) - self.recursion_guard(identifier, obj) do - yield - return false - end - throw(throw_symbol) - else - self.recursion_guard(identifier, obj) do - catch(throw_symbol) do - yield - return false - end - return true - end - end - end end diff --git a/lib-topaz/topaz.rb b/lib-topaz/topaz.rb index e915a862e..0875ef128 100644 --- a/lib-topaz/topaz.rb +++ b/lib-topaz/topaz.rb @@ -7,3 +7,4 @@ module Topaz end load_bootstrap.call("array.rb") +load_bootstrap.call("thread.rb") diff --git a/lib-topaz/topaz/thread.rb b/lib-topaz/topaz/thread.rb new file mode 100644 index 000000000..1c6caa830 --- /dev/null +++ b/lib-topaz/topaz/thread.rb @@ -0,0 +1,26 @@ +class Topaz::Thread + def self.recursion_guard_outer(identifier, obj, &block) + # We want to throw something less likely to be caught accidentally outside + # our own code than the recursion identifier. Ideally this should be an + # object that is unique to this particular recursion guard. Since doing + # that properly requires pushing extra state all the way up into + # ExecutionContext, we do this instead. + throw_symbol = "__recursion_guard_#{identifier}".to_sym + + if Thread.current.in_recursion_guard?(identifier) + Thread.current.recursion_guard(identifier, obj) do + yield + return false + end + throw(throw_symbol) + else + Thread.current.recursion_guard(identifier, obj) do + catch(throw_symbol) do + yield + return false + end + return true + end + end + end +end diff --git a/tests/objects/test_threadobject.py b/tests/objects/test_threadobject.py index 1179bb167..ca4c8b5a0 100644 --- a/tests/objects/test_threadobject.py +++ b/tests/objects/test_threadobject.py @@ -61,7 +61,7 @@ def test_recursion_guard_outer(self, space): w_res = space.execute(""" def foo(objs, depth = 0) obj = objs.shift - Thread.current.recursion_guard_outer(:foo, obj) do + Topaz::Thread.recursion_guard_outer(:foo, obj) do return foo(objs, depth + 1) end return [depth, obj] From e80bb2c59ef2fe5de4daf6aa29d5b7b29526fab9 Mon Sep 17 00:00:00 2001 From: Jeremy Thurgood Date: Sun, 21 Jul 2013 23:49:05 +0200 Subject: [PATCH 4/4] Just Topaz, not Topaz::Thread. --- lib-topaz/topaz.rb | 25 ++++++++++++- lib-topaz/topaz/thread.rb | 26 -------------- tests/objects/test_threadobject.py | 56 ------------------------------ tests/test_recursion_guard.py | 56 ++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 83 deletions(-) delete mode 100644 lib-topaz/topaz/thread.rb create mode 100644 tests/test_recursion_guard.py diff --git a/lib-topaz/topaz.rb b/lib-topaz/topaz.rb index 0875ef128..691e71050 100644 --- a/lib-topaz/topaz.rb +++ b/lib-topaz/topaz.rb @@ -1,4 +1,28 @@ module Topaz + def self.recursion_guard_outer(identifier, obj, &block) + # We want to throw something less likely to be caught accidentally outside + # our own code than the recursion identifier. Ideally this should be an + # object that is unique to this particular recursion guard. Since doing + # that properly requires pushing extra state all the way up into + # ExecutionContext, we do this instead. + throw_symbol = "__recursion_guard_#{identifier}".to_sym + + if Thread.current.in_recursion_guard?(identifier) + Thread.current.recursion_guard(identifier, obj) do + yield + return false + end + throw(throw_symbol) + else + Thread.current.recursion_guard(identifier, obj) do + catch(throw_symbol) do + yield + return false + end + return true + end + end + end end lib_topaz = File.join(File.dirname(__FILE__), 'topaz') @@ -7,4 +31,3 @@ module Topaz end load_bootstrap.call("array.rb") -load_bootstrap.call("thread.rb") diff --git a/lib-topaz/topaz/thread.rb b/lib-topaz/topaz/thread.rb deleted file mode 100644 index 1c6caa830..000000000 --- a/lib-topaz/topaz/thread.rb +++ /dev/null @@ -1,26 +0,0 @@ -class Topaz::Thread - def self.recursion_guard_outer(identifier, obj, &block) - # We want to throw something less likely to be caught accidentally outside - # our own code than the recursion identifier. Ideally this should be an - # object that is unique to this particular recursion guard. Since doing - # that properly requires pushing extra state all the way up into - # ExecutionContext, we do this instead. - throw_symbol = "__recursion_guard_#{identifier}".to_sym - - if Thread.current.in_recursion_guard?(identifier) - Thread.current.recursion_guard(identifier, obj) do - yield - return false - end - throw(throw_symbol) - else - Thread.current.recursion_guard(identifier, obj) do - catch(throw_symbol) do - yield - return false - end - return true - end - end - end -end diff --git a/tests/objects/test_threadobject.py b/tests/objects/test_threadobject.py index ca4c8b5a0..158b61736 100644 --- a/tests/objects/test_threadobject.py +++ b/tests/objects/test_threadobject.py @@ -15,59 +15,3 @@ def test_thread_local_storage(self, space): return Thread.current[:a] """) assert space.int_w(w_res) == 1 - - def test_recursion_guard(self, space): - w_res = space.execute(""" - def foo(objs, depth = 0) - obj = objs.shift - recursion = Thread.current.recursion_guard(:foo, obj) do - return foo(objs, depth + 1) - end - if recursion - return [depth, obj] - end - end - return foo([:a, :b, :c, :a, :d]) - """) - w_depth, w_symbol = space.listview(w_res) - assert space.int_w(w_depth) == 3 - assert space.symbol_w(w_symbol) == "a" - - def test_recursion_guard_nested(self, space): - w_res = space.execute(""" - def foo(objs, depth = 0) - obj = objs.shift - Thread.current.recursion_guard(:foo, obj) do - return bar(objs, depth + 1) - end - return [depth, obj] - end - - def bar(objs, depth) - obj = objs.shift - Thread.current.recursion_guard(:bar, obj) do - return foo(objs, depth + 1) - end - return [depth, obj] - end - - return foo([:a, :a, :b, :b, :c, :a, :d, :d]) - """) - w_depth, w_symbol = space.listview(w_res) - assert space.int_w(w_depth) == 5 - assert space.symbol_w(w_symbol) == "a" - - def test_recursion_guard_outer(self, space): - w_res = space.execute(""" - def foo(objs, depth = 0) - obj = objs.shift - Topaz::Thread.recursion_guard_outer(:foo, obj) do - return foo(objs, depth + 1) - end - return [depth, obj] - end - return foo([:a, :b, :c, :a, :d]) - """) - w_depth, w_symbol = space.listview(w_res) - assert space.int_w(w_depth) == 0 - assert space.symbol_w(w_symbol) == "a" diff --git a/tests/test_recursion_guard.py b/tests/test_recursion_guard.py new file mode 100644 index 000000000..410072e6f --- /dev/null +++ b/tests/test_recursion_guard.py @@ -0,0 +1,56 @@ +class TestRecursionGuard(object): + def test_recursion_guard(self, space): + w_res = space.execute(""" + def foo(objs, depth = 0) + obj = objs.shift + recursion = Thread.current.recursion_guard(:foo, obj) do + return foo(objs, depth + 1) + end + if recursion + return [depth, obj] + end + end + return foo([:a, :b, :c, :a, :d]) + """) + w_depth, w_symbol = space.listview(w_res) + assert space.int_w(w_depth) == 3 + assert space.symbol_w(w_symbol) == "a" + + def test_recursion_guard_nested(self, space): + w_res = space.execute(""" + def foo(objs, depth = 0) + obj = objs.shift + Thread.current.recursion_guard(:foo, obj) do + return bar(objs, depth + 1) + end + return [depth, obj] + end + + def bar(objs, depth) + obj = objs.shift + Thread.current.recursion_guard(:bar, obj) do + return foo(objs, depth + 1) + end + return [depth, obj] + end + + return foo([:a, :a, :b, :b, :c, :a, :d, :d]) + """) + w_depth, w_symbol = space.listview(w_res) + assert space.int_w(w_depth) == 5 + assert space.symbol_w(w_symbol) == "a" + + def test_recursion_guard_outer(self, space): + w_res = space.execute(""" + def foo(objs, depth = 0) + obj = objs.shift + Topaz.recursion_guard_outer(:foo, obj) do + return foo(objs, depth + 1) + end + return [depth, obj] + end + return foo([:a, :b, :c, :a, :d]) + """) + w_depth, w_symbol = space.listview(w_res) + assert space.int_w(w_depth) == 0 + assert space.symbol_w(w_symbol) == "a"