about summary refs log tree commit homepage
diff options
context:
space:
mode:
-rw-r--r--ext/sleepy_penguin/epoll.c8
-rw-r--r--test/test_epoll_gc.rb47
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