about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <e@yhbt.net>2010-09-27 00:24:50 +0000
committerEric Wong <e@yhbt.net>2010-09-27 00:31:56 +0000
commit87cf3ce6185b9138032a5af53cecae98f8c93564 (patch)
treea6c2290df504027fa9d72ddd3eec6a4450bf9929
parent6fbde1518578dd1b828efcecaf2caf893bddc110 (diff)
downloadkgio-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.c19
-rw-r--r--test/test_connect_fd_leak.rb23
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