Bug #2127
closedFiber#resume - segfault inside C extension
Description
Hi,
I am using ruby 1.9.2dev (2009-09-14 trunk 24923) [i686-linux].
Both Fiber#resume and rb_fiber_resume() are causing a segfault
when called from inside a very simple C extension (see below).
Thanks for your consideration.
###############################################################
# ls
###############################################################
extconf.rb main.c
###############################################################
# cat extconf.rb
###############################################################
require 'mkmf'
have_library('ruby-static', 'ruby_init') ||
have_library('ruby', 'ruby_init')
create_makefile('main')
###############################################################
# cat main.c
###############################################################
#include <stdio.h>
#include <ruby.h>
VALUE body(VALUE arg)
{
printf("Fiber: Inside fiber body");
fflush(stdout);
return Qnil;
}
RUBY_GLOBAL_SETUP
int main(int argc, char** argv)
{
ruby_sysinit(&argc, &argv);
RUBY_INIT_STACK;
ruby_init();
printf("Main: Creating Ruby fiber...");
fflush(stdout);
VALUE fib = rb_fiber_new(body, Qnil);
VALUE dump = rb_inspect(fib);
rb_io_puts(1, &dump, rb_stdout);
printf("Main: Going to resume fiber...\n");
fflush(stdout);
rb_funcall(fib, rb_intern("resume"), 0); /* <== SEGFAULT */
printf("Main: Fiber resumed successfully.\n");
fflush(stdout);
return ruby_cleanup(0);
}
###############################################################
# ruby -v extconf.rb
###############################################################
ruby 1.9.2dev (2009-09-14 trunk 24923) [i686-linux]
checking for ruby_init() in -lruby-static... yes
creating Makefile
###############################################################
# sed -i 's,-shared,,g' Makefile ; make
###############################################################
gcc -I. -I/home/sun/app/ruby-1.9-git/include/ruby-1.9.1/i686-linux -I/home/sun/app/ruby-1.9-git/include/ruby-1.9.1/ruby/backward -I/home/sun/app/ruby-1.9-git/include/ruby-1.9.1 -I. -D_FILE_OFFSET_BITS=64 -fPIC -g -o main.o -c main.c
gcc -o main.so main.o -L. -L/home/sun/app/ruby-1.9-git/lib -Wl,-R/home/sun/app/ruby-1.9-git/lib -L. -rdynamic -Wl,-export-dynamic -lruby-static -lpthread -lrt -ldl -lcrypt -lm -lc
###############################################################
# ./main.so
###############################################################
Main: Creating Ruby fiber...#<Fiber:0x8a5938c>
Main: Going to resume fiber...
[BUG] Segmentation fault
ruby 1.9.2dev (2009-09-14 trunk 24923) [i686-linux]
-- control frame ----------
c:0001 p:---- s:0001 b:-001 l:000000 d:000000 ------
---------------------------
fish: Job 1, “./main.so” terminated by signal SIGSEGV (Address boundary error)
####################################################
# gdb main.so core
####################################################
GNU gdb 6.8
[...]
Core was generated by `./main.so'.
Program terminated with signal 11, Segmentation fault.
[New process 8635]
[New process 8636]
#0 0xb7c539be in uw_frame_state_for () from /usr/lib/libgcc_s.so.1
(gdb) bt full
#0 0xb7c539be in uw_frame_state_for () from /usr/lib/libgcc_s.so.1
No symbol table info available.
#1 0xb7c54011 in _Unwind_Backtrace () from /usr/lib/libgcc_s.so.1
No symbol table info available.
#2 0xb7ddf405 in backtrace () from /lib/libc.so.6
No symbol table info available.
#3 0x08141c33 in rb_vm_bugreport () at vm_dump.c:598
trace = {0x8141c33, 0x816fb60, 0x816fbba, 0x80e386e, 0xb7ee740c, 0x0 <repeats 1019 times>}
n = -1209027184
syms = (char **) 0xb7d4630f
i = -1209789088
#4 0x0816fb60 in report_bug (file=0x0, line=0, fmt=0x819a2e3 "Segmentation fault",
args=0x8aa97d4 "\236é\031k") at error.c:215
buf = '\0' <repeats 8176 times>, "\230\227ª\bcè\023\b\2102£\b\230~¬\b"
out = (FILE *) 0xb7e41560
len = 0
#5 0x0816fbba in rb_bug (fmt=0x819a2e3 "Segmentation fault") at error.c:232
args = 0x8aa97d4 "\236é\031k"
#6 0x080e386e in sigsegv (sig=11, info=0x8aa980c, ctx=0x8aa988c) at signal.c:616
th = (rb_thread_t *) 0x8a33288
#7 <signal handler called>
No symbol table info available.
#8 0x6b19e99e in ?? ()
No symbol table info available.
Updated by nobu (Nobuyoshi Nakada) over 14 years ago
- Status changed from Open to Closed
- % Done changed from 0 to 100
=begin
Applied in changeset r25014.
=end
Updated by nobu (Nobuyoshi Nakada) over 14 years ago
=begin
Hi,
At Mon, 21 Sep 2009 09:12:52 +0900,
Suraj Kurapati wrote in [ruby-core:25681]:
Both Fiber#resume and rb_fiber_resume() are causing a segfault
when called from inside a very simple C extension (see below).
Don't create a fiber inside a not-running thread.
$ cat bug-2127.c
#include <stdio.h>
#include <ruby.h>
VALUE
body(VALUE arg)
{
printf("Fiber: Inside fiber body\n");
fflush(stdout);
return Qnil;
}
VALUE
run_body(VALUE arg)
{
VALUE fib = rb_fiber_new(body, arg), dump;
printf("Main: Creating Ruby fiber...");
fflush(stdout);
dump = rb_inspect(fib);
rb_io_puts(1, &dump, rb_stdout);
printf("Main: Going to resume fiber...\n");
fflush(stdout);
rb_fiber_resume(fib, 0, 0); /* <== SEGFAULT */
printf("Main: Fiber resumed successfully.\n");
fflush(stdout);
return Qnil;
}
RUBY_GLOBAL_SETUP
int
main(int argc, char** argv)
{
ruby_sysinit(&argc, &argv);
{
RUBY_INIT_STACK;
ruby_init();
rb_protect(run_body, Qnil, 0);
}
return ruby_cleanup(0);
}
$ make MAINOBJ='$(PROGRAM).o' PROGRAM=bug-2127 program LIBRUBYARG='$(LIBRUBY_A)'
$ ./bug-2127
Main: Creating Ruby fiber...#<Fiber:0x2c365c>
Main: Going to resume fiber...
Fiber: Inside fiber body
Main: Fiber resumed successfully.
--
Nobu Nakada
=end
Updated by nobu (Nobuyoshi Nakada) over 14 years ago
- Status changed from Open to Rejected
=begin
=end
Updated by sunaku (Suraj Kurapati) over 14 years ago
=begin
Wow, thanks for the quick response! The corrections
you made to my C code are very helpful and instructive.
Thanks for your help! Cheers. :-)
=end
Updated by sunaku (Suraj Kurapati) over 14 years ago
=begin
I have one small question about the example code:
I want to pause the execution of run_body() and return
control to main(). Later, main() will resume run_body().
In other words, I want to make run_body() like a Fiber.
Is this possible?
Thanks for your consideration.
=end
Updated by sunaku (Suraj Kurapati) over 14 years ago
=begin
I found an answer to my question:
The trick is to always work with the Fiber inside the environment
(a running thread?) provided by rb_protect(). If you try to do
anything to the Fiber (other than #inspect) outside of rb_protect(),
a segfault occurs (see the NOTE comment in the example below).
Below is an updated example that shows the answer to my question.
Cheers.
###############################################################
cat main.c¶
###############################################################
#include <stdio.h>
#include <ruby.h>
VALUE
my_fiber_body(VALUE arg)
{
printf("Fiber: Entering fiber body\n");
int i;
for (i = 0; i < 5; i++)
{
printf("Fiber: going to yield=%d\n", i);
VALUE count = INT2FIX(i);
rb_fiber_yield(1, &count);
}
printf("Fiber: Exiting fiber body\n");
return Qtrue;
}
VALUE
create_my_fiber(VALUE arg)
{
VALUE fib = rb_fiber_new(my_fiber_body, arg), dump;
printf("create_my_fiber: Created my fiber=");
fflush(stdout);
dump = rb_inspect(fib);
rb_io_puts(1, &dump, rb_stdout);
return fib;
}
VALUE
resume_my_fiber(VALUE fib)
{
VALUE dump;
printf("resume_my_fiber: Going to resume fiber=");
fflush(stdout);
dump = rb_inspect(fib);
rb_io_puts(1, &dump, rb_stdout);
if (RTEST(rb_fiber_alive_p(fib)))
{
VALUE result = rb_fiber_resume(fib, 0, 0);
printf("resume_my_fiber: Fiber yielded value=");
fflush(stdout);
dump = rb_inspect(result);
rb_io_puts(1, &dump, rb_stdout);
return result;
}
else
{
printf("resume_my_fiber: Fiber is dead! cannot resume\n");
return Qfalse;
}
}
RUBY_GLOBAL_SETUP
int
main(int argc, char** argv)
{
ruby_sysinit(&argc, &argv);
{
RUBY_INIT_STACK;
ruby_init();
VALUE fib = rb_protect(create_my_fiber, Qnil, 0);
printf("Main: Outside rb_protect()\n");
/*
NOTE: I resume the fiber here (outside of rb_protect())
then a segfault occurs. Is this because the
rb_protect() function provides a running thread?
*/
printf("Main: Going to resume fiber many times...\n");
VALUE count;
do
{
count = rb_protect(resume_my_fiber, fib, 0);
}
while (RTEST(count));
printf("Main: Goodbye!\n");
}
return ruby_cleanup(0);
}
###############################################################
./main.so¶
###############################################################
create_my_fiber: Created my fiber=#Fiber:0x92d7414
Main: Outside rb_protect()
Main: Going to resume fiber many times...
resume_my_fiber: Going to resume fiber=#Fiber:0x92d7414
Fiber: Entering fiber body
Fiber: going to yield=0
resume_my_fiber: Fiber yielded value=0
resume_my_fiber: Going to resume fiber=#Fiber:0x92d7414
Fiber: going to yield=1
resume_my_fiber: Fiber yielded value=1
resume_my_fiber: Going to resume fiber=#Fiber:0x92d7414
Fiber: going to yield=2
resume_my_fiber: Fiber yielded value=2
resume_my_fiber: Going to resume fiber=#Fiber:0x92d7414
Fiber: going to yield=3
resume_my_fiber: Fiber yielded value=3
resume_my_fiber: Going to resume fiber=#Fiber:0x92d7414
Fiber: going to yield=4
resume_my_fiber: Fiber yielded value=4
resume_my_fiber: Going to resume fiber=#Fiber:0x92d7414
Fiber: Exiting fiber body
resume_my_fiber: Fiber yielded value=true
resume_my_fiber: Going to resume fiber=#Fiber:0x92d7414
resume_my_fiber: Fiber is dead! cannot resume
Main: Goodbye!
=end
Updated by nobu (Nobuyoshi Nakada) over 14 years ago
=begin
Hi,
At Mon, 21 Sep 2009 13:34:31 +0900,
Suraj Kurapati wrote in [ruby-core:25689]:
The trick is to always work with the Fiber inside the environment
(a running thread?) provided by rb_protect(). If you try to do
anything to the Fiber (other than #inspect) outside of rb_protect(),
a segfault occurs (see the NOTE comment in the example below).
Regardless of fiber, uncaught exception causes a segfault. And
I don't think it's guaranteed to resume a fiber in different
rb_protect scope.
--
Nobu Nakada
=end
Updated by sunaku (Suraj Kurapati) over 14 years ago
=begin
Hi,
Nobu Nakada wrote:
Regardless of fiber, uncaught exception causes a segfault.
Thanks, this is good to know.
I don't think it's guaranteed to resume a fiber in different
rb_protect scope.
Why is that? Does each rb_protect scope have a different running thread (with a different stack + heap)? Is there danger of a cross-thread violation?
Should I try to store the original rb_protect scope into a variable and reuse it when resuming my Fiber? What do you suggest?
Thanks for your consideration.
(Sorry for asking so many questions!)
=end
Updated by nobu (Nobuyoshi Nakada) over 14 years ago
=begin
Hi,
At Mon, 21 Sep 2009 16:26:17 +0900,
Suraj Kurapati wrote in [ruby-core:25691]:
I don't think it's guaranteed to resume a fiber in different
rb_protect scope.Why is that? Does each rb_protect scope have a different
running thread (with a different stack + heap)? Is there
danger of a cross-thread violation?
Maybe safe, maybe dangerous, noone has confirmed, nor
considered it yet. There is no cross-thread violation like
checks right now, but it might be figured out that the check is
necessary.
Should I try to store the original rb_protect scope into a
variable and reuse it when resuming my Fiber? What do you
suggest?
Create a new thread and use fibers within one rb_protect.
--
Nobu Nakada
=end
Updated by sunaku (Suraj Kurapati) over 14 years ago
=begin
Hi,
Nobu Nakada wrote:
Maybe safe, maybe dangerous, noone has confirmed, nor
considered it yet. There is no cross-thread violation like
checks right now, but it might be figured out that the check is
necessary.
You are correct. I got a #<FiberError: fiber called across trap>
error when I used a more complex Ruby script as the body of my Fiber.
Create a new thread and use fibers within one rb_protect.
Thanks, I will try this suggestion. Cheers!
=end
Updated by ioquatix (Samuel Williams) almost 3 years ago
- Description updated (diff)
Make description more readable.