Hi,

I would like to say that without applying this patch, my ruby interpreter, embedded in a pthread, would cause a segmentation fault as soon as GC was invoked. I would like to see this applied to 1.9.1 as well as http://redmine.ruby-lang.org/issues/show/2279 . Without these, it's hardly possible to have ruby 1.9.1 embedded in a useful way.

Thanks,
--Roman 


----- Original Message ----
From: Suraj Kurapati <redmine / ruby-lang.org>
To: ruby-core / ruby-lang.org
Sent: Thu, November 5, 2009 9:27:17 AM
Subject: [ruby-core:26550] [Feature #2294] [PATCH] ruby_bind_stack() to embed Ruby in coroutine

Issue #2294 has been updated by Suraj Kurapati.


Hi,

According to Matz's suggestion in [ruby-core:25139], I wrote
a detailed explanation of the problem this patch solves. I 
hope this explanation is helpful.  Please do not hesitate
to ask for clarifications or to correct any misunderstandings.

Thanks for your thoughtful consideration.

== Introduction

The patch adds a ruby_bind_stack() function to the Ruby C API.
This function allows the person who is embedding Ruby to
tell the Ruby GC about the stack boundaries of the embedded
environment:

  void ruby_bind_stack(VALUE *lower_bound, VALUE *upper_bound);

In order to understand why this function is important, please
consider the following two modes of operation: normal & embedded.


== Normal operation: Ruby runs in a C program's main()

Initially, Ruby assumes that the stack of Ruby's main
thread exists in a high memory address range, like this:

  (high memory address)
  
  0xc1bff1f0    Ruby's stack upper boundary
  
  0xbffff1f0    Ruby's stack lower boundary
                
  (low memory address)

As Ruby runs, the lower boundary is adjusted (by the
SET_STACK_END macro) to reflect the machine stack pointer:

  (high memory address)
  
  0xc1bff1f0    Ruby's stack upper boundary (not changed)
  
  0xc0ff1e80    Ruby's stack lower boundary
                (after update by SET_STACK_END)

  (low memory address)


== Embedded operation: Ruby runs inside a C coroutine

Initially, Ruby assumes that the stack of Ruby's main
thread exists in a high memory address range, like this:

  (high memory address)
  
  0xc1bff1f0    Ruby's stack upper boundary
  
  0xbffff1f0    Ruby's stack lower boundary
              
  (low memory address)

However, the stack of the C coroutine (which runs Ruby)
exists at a low memory address range, because it is
statically allocated:

  (high memory address)
  
  0xc1bff1f0    Ruby's stack upper boundary
  
  0xbffff1f0    Ruby's stack lower boundary

  0x086032a0    System V context's stack upper boundary
                
  0x082032a0    System V context's stack lower boundary
                
  (low memory address)

As Ruby runs, the lower boundary is adjusted (by the
SET_STACK_END macro) to reflect the machine stack pointer:

  (high memory address)
  
  0xc1bff1f0    Ruby's stack upper boundary
  
  0x086032a0    System V context's stack upper boundary
  
  0x08601680    Ruby's stack lower boundary
                (after update by SET_STACK_END)
                
  0x082032a0    System V context's stack lower boundary
  
  (low memory address)


See the problem?  Ruby's stack and the C coroutine stack
do not agree.  They overlap!

This situation becomes worse (and causes a segfault) when
the Ruby GC runs: it marks VALUEs in the Ruby stack, which
currently contains all of the heap memory!  Somewhere in
the vast heap memory, it finds and dereferences a NULL value
and BOOM! a segfault occurs.  :-)

To solve this problem, the ruby_bind_stack() function corrects
Ruby's stack to reflect the stack boundaries of the C coroutine:

  (high memory address)
  
  0x086032a0    Ruby's stack upper boundary and also
                System V context's stack upper boundary
  
  0x08601680    Ruby's stack lower boundary
                (after update by SET_STACK_END)
                
  0x082032a0    System V context's stack lower boundary
  
  (low memory address)

Now, when the Ruby GC runs, it marks VALUEs in the correct
memory region.  It does not travel into heap memory and
cause a segfault.

That is all.  Thanks for reading!
----------------------------------------
http://redmine.ruby-lang.org/issues/show/2294

----------------------------------------
http://redmine.ruby-lang.org