diff --git a/lib-topaz/bootstrap.rb b/lib-topaz/bootstrap.rb index 7601773d1..f38819736 100644 --- a/lib-topaz/bootstrap.rb +++ b/lib-topaz/bootstrap.rb @@ -5,6 +5,7 @@ load(File.join(lib_topaz, file)) end +load_bootstrap.call("thread.rb") load_bootstrap.call("array.rb") load_bootstrap.call("class.rb") load_bootstrap.call("comparable.rb") diff --git a/lib-topaz/thread.rb b/lib-topaz/thread.rb new file mode 100644 index 000000000..81a99eefe --- /dev/null +++ b/lib-topaz/thread.rb @@ -0,0 +1,26 @@ +class Thread + 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 d7e3301cb..45e89ff73 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