Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib-topaz/bootstrap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
26 changes: 26 additions & 0 deletions lib-topaz/thread.rb
Original file line number Diff line number Diff line change
@@ -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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't used yet, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, but I need it for Array#hash in #456.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is becoming my official theme song, but let's split this up :D, so this just has the identifier for recursion guards.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it have Thread.in_recursion_guard? as well? That's only used by the outer guard.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, just what's needed. :)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should have been more precise, just what's needed for the recursion_guard() changes.

15 changes: 15 additions & 0 deletions tests/objects/test_threadobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
8 changes: 8 additions & 0 deletions topaz/objects/threadobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -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