Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recursive methods crash in Fiber/Thread due to stack size #12

evnu opened this issue Oct 25, 2012 · 3 comments

Recursive methods crash in Fiber/Thread due to stack size #12

evnu opened this issue Oct 25, 2012 · 3 comments


Copy link

evnu commented Oct 25, 2012

RubyTree uses recursion for methods such as each and detached_subtree_copy. This is ok for smaller well-balanced trees, but fails for unbalanced trees when Fiber or Threads are involved.

Constructing and Walking a Linear Tree

See the following example method to construct a linear tree:

require "tree"

def run_test(depth=100)
    tree ="/")
    current = tree
    depth.times do |i|
        new_node ="#{i}")
        current << new_node
        current = new_node
    tree.each{|n| nil}

This runs fine when executed in the main thread...

puts "Run test in the main thread"

...but fails when a Fiber or Thread is involved:

puts "Run test in a fiber"
begin do
rescue SystemStackError
    puts "Fiber died."

puts "Run test in a thread"
    Thread.abort_on_exception = true do
        run_test(600) # 600 is not chosen arbitrary: this is the first number where it fails here.
rescue SystemStackError
    puts "Thread died."

Note that the recursion depth between Fiber and Thread differs.

Demo Output

The example script above produces the following output with my machine:

% ./example.rb
Run test in the main thread
Run test in a fiber
Fiber died.
Run test in a thread
Thread died.

Possible Solution

This should be resolvable by replacing the recursion in each, detached_subtree_copy and elsewhere with the respective iterative representation. each could possibly be fixed like this:

module Tree
    class TreeNode
        def each(&blk)
            expand_nodes = [self]
            while expand_nodes != []
                current = expand_nodes.shift
                yield current
                expand_nodes = current.children.concat(expand_nodes)
@ghost ghost assigned evolve75 Oct 26, 2012
Copy link


Thank you for highlighting this. I was actually planning to replace recursions at some point of time. I will review the issue and put in a few test cases to start with.

Just out of curiosity, would you be able to share the use case where you need this large volume of nodes?


Copy link

evnu commented Oct 28, 2012

We are using rubytree to display different kinds of taxonomies. The graphs are built using user input and sometimes users create very long branches (probably by accident). We will eventually filter those branches, but for that we have to find them first. Note that the example above is quite artificial; if the node names are longer, it should crash with a smaller depth. We had one case where it crashed for a tree of depth 40.

evolve75 added a commit that referenced this issue Dec 28, 2013
This change fixes bug #12, where the recursion for deep trees was leading to out
of stack memory errors.
Copy link

Fix is working fine, and will be available in R0.9.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
None yet

No branches or pull requests

2 participants