diff options
author | Eric Wong <normalperson@yhbt.net> | 2011-01-21 16:49:50 -0800 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2011-01-21 16:50:21 -0800 |
commit | 6ba38eee337821cebcf9a7b15fcbf4622a06b78d (patch) | |
tree | feff9e3ac780f292defaeaa3276243871dd0598e | |
parent | 6c39a46da8be194ca5069cd66ad9153f2ba09139 (diff) | |
download | sleepy_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.c | 7 | ||||
-rw-r--r-- | ext/sleepy_penguin/sleepy_penguin.h | 23 | ||||
-rw-r--r-- | test/test_epoll_optimizations.rb | 24 |
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 |