Someone on github sent me this interesting one-liner that often causes
Ruby 1.8.7 with the MBARI patchset to either abort with an inappropriate
error message or segfault:

ruby  -rthread -e '
  q = Queue.new; Thread.new { q.pop }; pid = fork;
  if pid.nil?; q = nil; GC.start; else; Process.wait(pid); end'

I tracked down the problem to the fastthread library's policy of killing
any threads waiting on a queue when it is finalized.  In the test case
above, the Thread.new {q.pop} is destroyed immediately in the child
process.  The subsequent GC marks the thread for finalization, setting
its object type to T_DEFFERRED.  When the same GC pass finalizes the
Queue, its finalizer tries to kill those threads again, but, because
they have already been destroy and are marked for finalization
themselves, rb_thead_check fails on them (as it should).

In this simple case, you'll see:

-e:1:in `start': wrong argument type Object (expected Thread)
(TypeError)

However, I'm told that more complex cases will segfault.
(which I find quite believable)

When both a Queue and one or more threads waiting on it fail to be
marked on the same gc marking pass they are finalized and discarded in
no particular order.

Note that, if I compile my ruby with optimization turned off, the
failure does not occur.  It also does not occur on unpatched MRI.
In both these cases, the 'C' stack will retain references to either the
thread or the queue that prevent GC of both on the same pass.

My first patch for fixing this was to add, in kill_waiting_threads(), a
test to ensure each thread's object type == T_DATA before calling
rb_thread_kill().  However, on further reflection, I question whether
there is any point in trying to gracefully kill such threads.

My reasoning for this is:

Any thread waiting on an object must hold a reference to that object.
That reference should prevent the synchronization object (Queue, Mutex,
etc.) itself from being garbage collected.  So, if a synchronization
object is garbage collected, this implies that any threads on its
waiting lists must already be dead.  (i.e. their stack has already been
freed)

If no one sees any holes on this logic,
I'd propose eliminating the kill_waiting_threads() calls in thread.c

- brent

-- 
Posted via http://www.ruby-forum.com/.