diff options
author | Eric Wong <e@yhbt.net> | 2010-09-27 00:24:50 +0000 |
---|---|---|
committer | Eric Wong <e@yhbt.net> | 2010-09-27 00:31:56 +0000 |
commit | 87cf3ce6185b9138032a5af53cecae98f8c93564 (patch) | |
tree | a6c2290df504027fa9d72ddd3eec6a4450bf9929 | |
parent | 6fbde1518578dd1b828efcecaf2caf893bddc110 (diff) | |
download | kgio-87cf3ce6185b9138032a5af53cecae98f8c93564.tar.gz |
We cannot raise exceptions and expect GC to clean up after us until we've created an actual IO object.
-rw-r--r-- | ext/kgio/kgio_ext.c | 19 | ||||
-rw-r--r-- | test/test_connect_fd_leak.rb | 23 |
2 files changed, 37 insertions, 5 deletions
diff --git a/ext/kgio/kgio_ext.c b/ext/kgio/kgio_ext.c index eac04a5..977273d 100644 --- a/ext/kgio/kgio_ext.c +++ b/ext/kgio/kgio_ext.c @@ -447,10 +447,17 @@ static VALUE set_nonblock(VALUE mod, VALUE boolean) return Qnil; } +static void close_fail(int fd, const char *msg) +{ + int saved_errno = errno; + (void)close(fd); + errno = saved_errno; + rb_sys_fail(msg); +} + static VALUE my_connect(VALUE klass, int io_wait, int domain, void *addr, socklen_t addrlen) { - int rc; int fd = socket(domain, SOCK_STREAM, 0); if (fd == -1) { @@ -467,9 +474,11 @@ my_connect(VALUE klass, int io_wait, int domain, void *addr, socklen_t addrlen) if (fd == -1) rb_sys_fail("socket"); } - set_nonblocking(fd); - rc = connect(fd, addr, addrlen); - if (rc == -1) { + + if (fcntl(fd, F_SETFL, O_RDWR | O_NONBLOCK) == -1) + close_fail(fd, "fcntl(F_SETFL, O_RDWR | O_NONBLOCK)"); + + if (connect(fd, addr, addrlen) == -1) { if (errno == EINPROGRESS) { VALUE io = sock_for_fd(klass, fd); @@ -479,7 +488,7 @@ my_connect(VALUE klass, int io_wait, int domain, void *addr, socklen_t addrlen) } return io; } - rb_sys_fail("connect"); + close_fail(fd, "connect"); } return sock_for_fd(klass, fd); } diff --git a/test/test_connect_fd_leak.rb b/test/test_connect_fd_leak.rb new file mode 100644 index 0000000..5889e3a --- /dev/null +++ b/test/test_connect_fd_leak.rb @@ -0,0 +1,23 @@ +require 'test/unit' +require 'io/nonblock' +$-w = true +require 'kgio' + +class TestConnectFDLeak < Test::Unit::TestCase + + def teardown + Kgio.wait_readable = Kgio.wait_writable = nil + end + + def test_unix_socket + nr = 0 + path = "/non/existent/path" + assert(! File.exist?(path), "#{path} should not exist") + assert_nothing_raised do + begin + sock = Kgio::UNIXSocket.new(path) + rescue Errno::ENOENT + end while (nr += 1) < 10000 + end + end +end |