Thead.parent, revisited. Or: Building a call stack.

Discussion in 'Ruby' started by Erik Veenstra, Mar 5, 2007.

  1. In order to build a diagram of all method calls in an
    application, I created a set_trace_func function. This function
    builds a call stack. Each thread in each process should have
    its own call stack, initialized to the call stack of the parent
    process or the parent thread.

    The call stacks are stored in the array stack_per_thread. The
    initial call stack is an empty array:

    stack_per_thread = []
    stack_per_thread[[Process.pid, Thread.current]] = []

    Creating a new call stack after a fork (both process fork and
    thread fork) is done like this:

    stack = (stack_per_thread[[Process.pid, Thread.current]] ||=
    stack.dup)

    Process forks are not a problem, since the last call stack
    before the process fork is the one with which the child process
    has to continue and does continue.

    However, threads are a bit of a problem. Threads don't have a
    parent, which means that it's not possible to clone the call
    stack of the parent thread. I currently fake the concept
    "parent thread" by assuming that the last running thread is the
    parent thread.

    If Parent.thread existed, everything would be perfectly fine:

    stack = (stack_per_thread[[Process.pid, Thread.current]] ||=
    stack_per_thread[[Process.pid, Thread.parent]].dup)

    I observed a couple of traces and noticed, empirically, that a
    new thread always gets a couple of CPU cycles (and hence calls
    to set_trace_func). If this is always true, my assumption ("the
    last thread is the parent thread") is no problem. If the parent
    process can spawn many threads before any of them gets some CPU
    cycles, well, then we do have a problem... :{

    Ideas? Related work? Anybody able to understand the C code?
    Matz?... :}

    gegroet,
    Erik V. - http://www.erikveen.dds.nl/
     
    Erik Veenstra, Mar 5, 2007
    #1
    1. Advertisements

  2. Erik Veenstra

    ara.t.howard Guest

    class Thread
    class << self
    alias_method "__new__", "new"
    def new *a, &b
    child '__new__', *a, &b
    end
    alias_method "__start__", "start"
    def start *a, &b
    child '__start__', *a, &b
    end
    private
    def child as, *a, &b
    parent = Thread.current
    send(as, *a) do |*a|
    Thread.current.parent = parent
    b.call *a
    end
    end
    end
    def parent
    self['parent']
    end
    def parent= parent
    self['parent'] = parent
    end
    def ancestors
    return self['ancestors'] if self['ancestors']
    list = [t = self]
    while((t = t.parent))
    list << t
    end
    self['ancestors'] = list
    end
    end


    -a
     
    ara.t.howard, Mar 5, 2007
    #2
    1. Advertisements

  3. Yep, I know. That's a "work around". But, as we've discussed
    before [1], it's not water tight.

    "Thread#start doesn't reuse Thread.new. Neither does
    rb_thread_create(), which is used in e.g. TK."

    Thanks.

    gegroet,
    Erik V. - http://www.erikveen.dds.nl/

    [1] http://tinyurl.com/38823g
     
    Erik Veenstra, Mar 5, 2007
    #3
  4. Erik Veenstra

    ara.t.howard Guest

    oh right. forgot.

    check out ThreadGroup - i think it's properties may allow someone to hack what
    you're after. just looking at that today - but out of time now...

    -a
     
    ara.t.howard, Mar 5, 2007
    #4
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.