about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2011-01-21 16:49:50 -0800
committerEric Wong <normalperson@yhbt.net>2011-01-21 16:50:21 -0800
commit6ba38eee337821cebcf9a7b15fcbf4622a06b78d (patch)
treefeff9e3ac780f292defaeaa3276243871dd0598e
parent6c39a46da8be194ca5069cd66ad9153f2ba09139 (diff)
downloadsleepy_penguin-6ba38eee337821cebcf9a7b15fcbf4622a06b78d.tar.gz
Proxied objects may not be IOs at all, so have type-dependent
checks to ensure they're closed.
-rw-r--r--ext/sleepy_penguin/epoll.c7
-rw-r--r--ext/sleepy_penguin/sleepy_penguin.h23
-rw-r--r--test/test_epoll_optimizations.rb24
3 files changed, 51 insertions, 3 deletions
diff --git a/ext/sleepy_penguin/epoll.c b/ext/sleepy_penguin/epoll.c
index aa2f5e2..986ecc9 100644
--- a/ext/sleepy_penguin/epoll.c
+++ b/ext/sleepy_penguin/epoll.c
@@ -289,11 +289,15 @@ fallback_add:
 static VALUE delete(VALUE self, VALUE io)
 {
         struct rb_epoll *ep = ep_get(self);
-        int fd = my_fileno(io);
+        int fd;
         int rv;
         VALUE cur_io;
 
         ep_check(ep);
+        if (my_io_closed(io))
+                goto out;
+
+        fd = my_fileno(io);
         cur_io = rb_ary_entry(ep->marks, fd);
         if (NIL_P(cur_io) || my_io_closed(cur_io))
                 return Qnil;
@@ -308,6 +312,7 @@ static VALUE delete(VALUE self, VALUE io)
                         rb_sys_fail("epoll_ctl - del");
                 }
         }
+out:
         rb_ary_store(ep->marks, fd, Qnil);
         rb_ary_store(ep->flag_cache, fd, Qnil);
 
diff --git a/ext/sleepy_penguin/sleepy_penguin.h b/ext/sleepy_penguin/sleepy_penguin.h
index 2c016d5..50f6272 100644
--- a/ext/sleepy_penguin/sleepy_penguin.h
+++ b/ext/sleepy_penguin/sleepy_penguin.h
@@ -26,18 +26,37 @@
 #  endif
 #endif
 
+static int fixint_closed_p(VALUE io)
+{
+        return (fcntl(FIX2INT(io), F_GETFD) == -1 && errno == EBADF);
+}
+
 #if defined(RFILE) && defined(HAVE_ST_FD)
-static int my_io_closed(VALUE io)
+static int my_rb_io_closed(VALUE io)
 {
         return RFILE(io)->fptr->fd < 0;
 }
 #else
-static int my_io_closed(VALUE io)
+static int my_rb_io_closed(VALUE io)
 {
         return rb_funcall(io, rb_intern("closed?"), 0) == Qtrue;
 }
 #endif
 
+static int my_io_closed(VALUE io)
+{
+        switch (TYPE(io)) {
+        case T_FIXNUM:
+                return fixint_closed_p(io);
+        case T_FILE:
+                break;
+        default:
+                io = rb_convert_type(io, T_FILE, "IO", "to_io");
+        }
+
+        return my_rb_io_closed(io);
+}
+
 static int my_fileno(VALUE io)
 {
         rb_io_t *fptr;
diff --git a/test/test_epoll_optimizations.rb b/test/test_epoll_optimizations.rb
index c8df6f3..b4280d8 100644
--- a/test/test_epoll_optimizations.rb
+++ b/test/test_epoll_optimizations.rb
@@ -83,6 +83,30 @@ class TestEpollOptimizations < Test::Unit::TestCase
     assert_equal 0, lines.grep(/^epoll_ctl/).size
   end
 
+  def test_delete_closed_proxy
+    obj = Struct.new(:to_io).new(@wr)
+    rv = nil
+    @ep.add(obj, Epoll::OUT)
+    @wr.close
+    io, err = Strace.me { rv = @ep.delete(obj) }
+    lines = io.readlines; io.close
+    assert_nil err
+    assert_equal obj, rv
+    assert_equal 0, lines.grep(/^epoll_ctl/).size
+  end
+
+  def test_delete_closed_fileno
+    fileno = @wr.fileno
+    @ep.add(fileno, Epoll::OUT)
+    @wr.close
+    rv = nil
+    io, err = Strace.me { rv = @ep.delete(fileno) }
+    lines = io.readlines; io.close
+    assert_nil err
+    assert_equal fileno, rv
+    assert_equal 0, lines.grep(/^epoll_ctl/).size
+  end
+
   def test_delete_aliased_a
     tmp = IO.for_fd @wr.fileno
     IO_PURGATORY << tmp