diff options
-rw-r--r-- | ext/sleepy_penguin/epoll.c | 8 | ||||
-rw-r--r-- | test/test_epoll_gc.rb | 47 |
2 files changed, 55 insertions, 0 deletions
diff --git a/ext/sleepy_penguin/epoll.c b/ext/sleepy_penguin/epoll.c index d06ece1..26995de 100644 --- a/ext/sleepy_penguin/epoll.c +++ b/ext/sleepy_penguin/epoll.c @@ -50,6 +50,7 @@ struct rb_epoll { int capa; struct epoll_event *events; VALUE io; + VALUE marks; int flags; }; @@ -93,6 +94,7 @@ static void gcmark(void *ptr) struct rb_epoll *ep = ptr; rb_gc_mark(ep->io); + rb_gc_mark(ep->marks); } static void gcfree(void *ptr) @@ -122,6 +124,7 @@ static VALUE alloc(VALUE klass) self = Data_Make_Struct(klass, struct rb_epoll, gcmark, gcfree, ep); ep->fd = -1; ep->io = Qnil; + ep->marks = rb_ary_new(); ep->capa = step; ep->flags = EPOLL_CLOEXEC; ep->events = xmalloc(sizeof(struct epoll_event) * ep->capa); @@ -201,6 +204,8 @@ static VALUE ctl(VALUE self, VALUE io, VALUE flags, int op) if (rv == -1) rb_sys_fail("epoll_ctl"); } + if (op == EPOLL_CTL_ADD) + rb_ary_store(ep->marks, fd, io); return INT2NUM(rv); } @@ -226,6 +231,8 @@ static VALUE set(VALUE self, VALUE io, VALUE flags) rv = epoll_ctl(ep->fd, EPOLL_CTL_ADD, fd, &event); if (rv == -1) rb_sys_fail("epoll_ctl - add"); + + rb_ary_store(ep->marks, fd, io); return INT2NUM(rv); } rb_sys_fail("epoll_ctl - mod"); @@ -514,6 +521,7 @@ static VALUE init_copy(VALUE copy, VALUE orig) NIL_P(b->io) && "Ruby broken?"); ep_check(a); + b->marks = a->marks; b->flags = a->flags; b->fd = cloexec_dup(a); if (b->fd == -1) { diff --git a/test/test_epoll_gc.rb b/test/test_epoll_gc.rb new file mode 100644 index 0000000..4a7c115 --- /dev/null +++ b/test/test_epoll_gc.rb @@ -0,0 +1,47 @@ +require 'test/unit' +$-w = true + +require 'sleepy_penguin' + +class TestEpollGC < Test::Unit::TestCase + include SleepyPenguin + + def setup + GC.stress = true if GC.respond_to?(:stress=) + @ep = Epoll.new + end + + def teardown + GC.stress = false if GC.respond_to?(:stress=) + end + + def add_pipe_no_tailcall(m, depth) + add_pipe(m, depth += 1) + end + + def add_pipe(m, depth = 0) + if depth > 6000 + rd, wr = IO.pipe + warn "wr: #{wr.fileno}" + @ep.__send__(m, wr, Epoll::OUT) + else + add_pipe_no_tailcall(m, depth + 1) + end + end + + def test_gc_safety + done = false + begin + if done + x = nil + @ep.wait(nil, 10) { |flags, obj| p [ flags, x = obj ] } + assert x, "#{x.inspect}" + break + else + add_pipe(:add) + 2048.times { IO.pipe; File.open(__FILE__)} + done = true + end + end while true + end +end |