dumping ground for random patches and texts
 help / color / mirror / Atom feed
* [PATCH] socket: support accept `sock_nonblock: (true|false)'
@ 2015-07-02  1:36 Eric Wong
  0 siblings, 0 replies; only message in thread
From: Eric Wong @ 2015-07-02  1:36 UTC (permalink / raw)
  To: spew

[Feature ##11139]

An application wanting to do non-blocking accept may want to
create a blocking accepted socket, allow it with a kwarg while
preserving default behavior.

This is analogous to the SOCK_NONBLOCK flag in the Linux `accept4'
syscall.

While this has little obvious effect for Ruby API users (which
can emulate blocking behavior) this will reduce syscalls made
internally by Ruby.  Forcing blocking will preserve "wake-one"
behavior in the OS kernel to avoid a "thundering herd" problem.

In all cases, existing Ruby 2.2 behavior is preserved by default
to maximize compatibility, especially when sharing sockets with
non-Ruby processes:

`accept' and `sysaccept' calls will create sockets which are
blocking by default.

`accept_nonblock', calls will create sockets which are non-blocking
by default.
---
 ext/socket/ancdata.c         |  4 +--
 ext/socket/init.c            | 41 ++++++++++++++++------
 ext/socket/rubysocket.h      |  6 ++--
 ext/socket/socket.c          | 16 ++++-----
 ext/socket/tcpserver.c       | 13 +++----
 ext/socket/unixserver.c      | 13 +++----
 test/socket/test_nonblock.rb | 83 ++++++++++++++++++++++++++++++++++++++++++--
 test/socket/test_tcp.rb      | 53 ++++++++++++++++++++++++++++
 test/socket/test_unix.rb     | 67 +++++++++++++++++++++++++++++++++++
 9 files changed, 258 insertions(+), 38 deletions(-)

diff --git a/ext/socket/ancdata.c b/ext/socket/ancdata.c
index d0290ba..fddcfe7 100644
--- a/ext/socket/ancdata.c
+++ b/ext/socket/ancdata.c
@@ -1285,7 +1285,7 @@ bsock_sendmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
             goto retry;
         }
         if (nonblock && (errno == EWOULDBLOCK || errno == EAGAIN)) {
-	    if (rsock_opt_false_p(opts, sym_exception)) {
+	    if (rsock_opt_eq(opts, sym_exception, Qfalse)) {
 		return sym_wait_writable;
 	    }
 	    rb_readwrite_sys_fail(RB_IO_WAIT_WRITABLE,
@@ -1602,7 +1602,7 @@ bsock_recvmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
             goto retry;
         }
         if (nonblock && (errno == EWOULDBLOCK || errno == EAGAIN)) {
-            if (rsock_opt_false_p(vopts, sym_exception)) {
+            if (rsock_opt_eq(vopts, sym_exception, Qfalse)) {
                 return sym_wait_readable;
             }
             rb_readwrite_sys_fail(RB_IO_WAIT_READABLE, "recvmsg(2) would block");
diff --git a/ext/socket/init.c b/ext/socket/init.c
index 34f6a11..de3b63d 100644
--- a/ext/socket/init.c
+++ b/ext/socket/init.c
@@ -29,7 +29,7 @@ VALUE rb_cSOCKSSocket;
 #endif
 
 int rsock_do_not_reverse_lookup = 1;
-static VALUE sym_exception, sym_wait_readable;
+static VALUE sym_exception, sym_wait_readable, sym_sock_nonblock;
 
 void
 rsock_raise_socket_error(const char *reason, int error)
@@ -247,7 +247,7 @@ rsock_s_recvfrom_nonblock(VALUE sock, int argc, VALUE *argv, enum sock_recv_type
 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
 	  case EWOULDBLOCK:
 #endif
-            if (rsock_opt_false_p(opts, sym_exception))
+            if (rsock_opt_eq(opts, sym_exception, Qfalse))
 		return sym_wait_readable;
             rb_readwrite_sys_fail(RB_IO_WAIT_READABLE, "recvfrom(2) would block");
 	}
@@ -498,7 +498,7 @@ make_fd_nonblock(int fd)
 
 static int
 cloexec_accept(int socket, struct sockaddr *address, socklen_t *address_len,
-	       int nonblock)
+	       int sock_nonblock)
 {
     int ret;
     socklen_t len0 = 0;
@@ -513,7 +513,7 @@ cloexec_accept(int socket, struct sockaddr *address, socklen_t *address_len,
         flags |= SOCK_CLOEXEC;
 #endif
 #ifdef SOCK_NONBLOCK
-        if (nonblock) {
+        if (sock_nonblock) {
             flags |= SOCK_NONBLOCK;
         }
 #endif
@@ -523,7 +523,7 @@ cloexec_accept(int socket, struct sockaddr *address, socklen_t *address_len,
             if (ret <= 2)
                 rb_maygvl_fd_fix_cloexec(ret);
 #ifndef SOCK_NONBLOCK
-            if (nonblock) {
+            if (sock_nonblock) {
                 make_fd_nonblock(ret);
             }
 #endif
@@ -540,7 +540,7 @@ cloexec_accept(int socket, struct sockaddr *address, socklen_t *address_len,
     if (ret == -1) return -1;
     if (address_len && len0 < *address_len) *address_len = len0;
     rb_maygvl_fd_fix_cloexec(ret);
-    if (nonblock) {
+    if (sock_nonblock) {
         make_fd_nonblock(ret);
     }
     return ret;
@@ -551,12 +551,17 @@ rsock_s_accept_nonblock(int argc, VALUE *argv, VALUE klass, rb_io_t *fptr,
 			struct sockaddr *sockaddr, socklen_t *len)
 {
     int fd2;
-    VALUE opts = Qnil;
+    VALUE opts;
+    int sock_nonblock = 1;
 
     rb_scan_args(argc, argv, "0:", &opts);
 
+    if (rsock_opt_eq(opts, sym_sock_nonblock, Qfalse))
+        sock_nonblock = 0;
+
     rb_io_set_nonblock(fptr);
-    fd2 = cloexec_accept(fptr->fd, (struct sockaddr*)sockaddr, len, 1);
+    fd2 = cloexec_accept(fptr->fd, (struct sockaddr*)sockaddr, len,
+                         sock_nonblock);
     if (fd2 < 0) {
 	switch (errno) {
 	  case EAGAIN:
@@ -567,7 +572,7 @@ rsock_s_accept_nonblock(int argc, VALUE *argv, VALUE klass, rb_io_t *fptr,
 #if defined EPROTO
 	  case EPROTO:
 #endif
-            if (rsock_opt_false_p(opts, sym_exception))
+            if (rsock_opt_eq(opts, sym_exception, Qfalse))
 		return sym_wait_readable;
             rb_readwrite_sys_fail(RB_IO_WAIT_READABLE, "accept(2) would block");
 	}
@@ -579,6 +584,7 @@ rsock_s_accept_nonblock(int argc, VALUE *argv, VALUE klass, rb_io_t *fptr,
 
 struct accept_arg {
     int fd;
+    int sock_nonblock;
     struct sockaddr *sockaddr;
     socklen_t *len;
 };
@@ -587,19 +593,31 @@ static VALUE
 accept_blocking(void *data)
 {
     struct accept_arg *arg = data;
-    return (VALUE)cloexec_accept(arg->fd, arg->sockaddr, arg->len, 0);
+    return (VALUE)cloexec_accept(arg->fd, arg->sockaddr, arg->len,
+				 arg->sock_nonblock);
 }
 
 VALUE
-rsock_s_accept(VALUE klass, int fd, struct sockaddr *sockaddr, socklen_t *len)
+rsock_s_accept(int argc, VALUE *argv, VALUE klass, int fd,
+               struct sockaddr *sockaddr, socklen_t *len)
 {
     int fd2;
     int retry = 0;
     struct accept_arg arg;
+    VALUE opts;
 
     arg.fd = fd;
+    arg.sock_nonblock = 0;
     arg.sockaddr = sockaddr;
     arg.len = len;
+
+    rb_scan_args(argc, argv, "0:", &opts);
+
+    if (!NIL_P(opts) &&
+		rb_hash_lookup2(opts, sym_sock_nonblock, Qundef) == Qtrue) {
+	arg.sock_nonblock = 1;
+    }
+
   retry:
     rsock_maybe_wait_fd(fd);
     fd2 = (int)BLOCKING_REGION_FD(accept_blocking, &arg);
@@ -659,4 +677,5 @@ rsock_init_socket_init(void)
 #undef rb_intern
     sym_exception = ID2SYM(rb_intern("exception"));
     sym_wait_readable = ID2SYM(rb_intern("wait_readable"));
+    sym_sock_nonblock = ID2SYM(rb_intern("sock_nonblock"));
 }
diff --git a/ext/socket/rubysocket.h b/ext/socket/rubysocket.h
index d03b1c5..11d5a78 100644
--- a/ext/socket/rubysocket.h
+++ b/ext/socket/rubysocket.h
@@ -342,7 +342,7 @@ VALUE rsock_s_recvfrom(VALUE sock, int argc, VALUE *argv, enum sock_recv_type fr
 
 int rsock_connect(int fd, const struct sockaddr *sockaddr, int len, int socks);
 
-VALUE rsock_s_accept(VALUE klass, int fd, struct sockaddr *sockaddr, socklen_t *len);
+VALUE rsock_s_accept(int argc, VALUE *argv, VALUE klass, int fd, struct sockaddr *sockaddr, socklen_t *len);
 VALUE rsock_s_accept_nonblock(int argc, VALUE *argv, VALUE klass, rb_io_t *fptr, struct sockaddr *sockaddr, socklen_t *len);
 VALUE rsock_sock_listen(VALUE sock, VALUE log);
 
@@ -424,9 +424,9 @@ static inline void rsock_maybe_wait_fd(int fd) { }
 #endif
 
 static inline int
-rsock_opt_false_p(VALUE opt, VALUE sym)
+rsock_opt_eq(VALUE opt, VALUE sym, VALUE exp)
 {
-    if (!NIL_P(opt) && Qfalse == rb_hash_lookup2(opt, sym, Qundef))
+    if (!NIL_P(opt) && exp == rb_hash_lookup2(opt, sym, Qundef))
 	return 1;
     return 0;
 }
diff --git a/ext/socket/socket.c b/ext/socket/socket.c
index 2cda4cb..73ebac4 100644
--- a/ext/socket/socket.c
+++ b/ext/socket/socket.c
@@ -502,13 +502,13 @@ sock_connect_nonblock(int argc, VALUE *argv, VALUE sock)
     n = connect(fptr->fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr));
     if (n < 0) {
         if (errno == EINPROGRESS) {
-           if (rsock_opt_false_p(opts, sym_exception)) {
+           if (rsock_opt_eq(opts, sym_exception, Qfalse)) {
                 return sym_wait_writable;
             }
             rb_readwrite_sys_fail(RB_IO_WAIT_WRITABLE, "connect(2) would block");
 	}
 	if (errno == EISCONN) {
-           if (rsock_opt_false_p(opts, sym_exception)) {
+           if (rsock_opt_eq(opts, sym_exception, Qfalse)) {
                 return INT2FIX(0);
             }
 	}
@@ -896,7 +896,7 @@ sock_recvfrom_nonblock(int argc, VALUE *argv, VALUE sock)
  *
  */
 static VALUE
-sock_accept(VALUE sock)
+sock_accept(int argc, VALUE *argv, VALUE sock)
 {
     rb_io_t *fptr;
     VALUE sock2;
@@ -904,7 +904,7 @@ sock_accept(VALUE sock)
     socklen_t len = (socklen_t)sizeof buf;
 
     GetOpenFile(sock, fptr);
-    sock2 = rsock_s_accept(rb_cSocket,fptr->fd,&buf.addr,&len);
+    sock2 = rsock_s_accept(argc, argv, rb_cSocket, fptr->fd, &buf.addr, &len);
 
     return rb_assoc_new(sock2, rsock_io_socket_addrinfo(sock2, &buf.addr, len));
 }
@@ -1020,7 +1020,7 @@ sock_accept_nonblock(int argc, VALUE *argv, VALUE sock)
  * * Socket#accept
  */
 static VALUE
-sock_sysaccept(VALUE sock)
+sock_sysaccept(int argc, VALUE *argv, VALUE sock)
 {
     rb_io_t *fptr;
     VALUE sock2;
@@ -1028,7 +1028,7 @@ sock_sysaccept(VALUE sock)
     socklen_t len = (socklen_t)sizeof buf;
 
     GetOpenFile(sock, fptr);
-    sock2 = rsock_s_accept(0,fptr->fd,&buf.addr,&len);
+    sock2 = rsock_s_accept(argc, argv, 0, fptr->fd, &buf.addr, &len);
 
     return rb_assoc_new(sock2, rsock_io_socket_addrinfo(sock2, &buf.addr, len));
 }
@@ -2175,9 +2175,9 @@ Init_socket(void)
     rb_define_method(rb_cSocket, "connect_nonblock", sock_connect_nonblock, -1);
     rb_define_method(rb_cSocket, "bind", sock_bind, 1);
     rb_define_method(rb_cSocket, "listen", rsock_sock_listen, 1);
-    rb_define_method(rb_cSocket, "accept", sock_accept, 0);
+    rb_define_method(rb_cSocket, "accept", sock_accept, -1);
     rb_define_method(rb_cSocket, "accept_nonblock", sock_accept_nonblock, -1);
-    rb_define_method(rb_cSocket, "sysaccept", sock_sysaccept, 0);
+    rb_define_method(rb_cSocket, "sysaccept", sock_sysaccept, -1);
 
     rb_define_method(rb_cSocket, "recvfrom", sock_recvfrom, -1);
     rb_define_method(rb_cSocket, "recvfrom_nonblock", sock_recvfrom_nonblock, -1);
diff --git a/ext/socket/tcpserver.c b/ext/socket/tcpserver.c
index 578377b..a46eac5 100644
--- a/ext/socket/tcpserver.c
+++ b/ext/socket/tcpserver.c
@@ -53,7 +53,7 @@ tcp_svr_init(int argc, VALUE *argv, VALUE sock)
  *
  */
 static VALUE
-tcp_accept(VALUE sock)
+tcp_accept(int argc, VALUE *argv, VALUE sock)
 {
     rb_io_t *fptr;
     union_sockaddr from;
@@ -61,7 +61,8 @@ tcp_accept(VALUE sock)
 
     GetOpenFile(sock, fptr);
     fromlen = (socklen_t)sizeof(from);
-    return rsock_s_accept(rb_cTCPSocket, fptr->fd, &from.addr, &fromlen);
+    return rsock_s_accept(argc, argv, rb_cTCPSocket,
+                          fptr->fd, &from.addr, &fromlen);
 }
 
 /*
@@ -128,7 +129,7 @@ tcp_accept_nonblock(int argc, VALUE *argv, VALUE sock)
  *
  */
 static VALUE
-tcp_sysaccept(VALUE sock)
+tcp_sysaccept(int argc, VALUE *argv, VALUE sock)
 {
     rb_io_t *fptr;
     union_sockaddr from;
@@ -136,7 +137,7 @@ tcp_sysaccept(VALUE sock)
 
     GetOpenFile(sock, fptr);
     fromlen = (socklen_t)sizeof(from);
-    return rsock_s_accept(0, fptr->fd, &from.addr, &fromlen);
+    return rsock_s_accept(argc, argv, 0, fptr->fd, &from.addr, &fromlen);
 }
 
 void
@@ -174,9 +175,9 @@ rsock_init_tcpserver(void)
      *
      */
     rb_cTCPServer = rb_define_class("TCPServer", rb_cTCPSocket);
-    rb_define_method(rb_cTCPServer, "accept", tcp_accept, 0);
+    rb_define_method(rb_cTCPServer, "accept", tcp_accept, -1);
     rb_define_method(rb_cTCPServer, "accept_nonblock", tcp_accept_nonblock, -1);
-    rb_define_method(rb_cTCPServer, "sysaccept", tcp_sysaccept, 0);
+    rb_define_method(rb_cTCPServer, "sysaccept", tcp_sysaccept, -1);
     rb_define_method(rb_cTCPServer, "initialize", tcp_svr_init, -1);
     rb_define_method(rb_cTCPServer, "listen", rsock_sock_listen, 1); /* in socket.c */
 }
diff --git a/ext/socket/unixserver.c b/ext/socket/unixserver.c
index a38d0cb..cc936fe 100644
--- a/ext/socket/unixserver.c
+++ b/ext/socket/unixserver.c
@@ -45,7 +45,7 @@ unix_svr_init(VALUE sock, VALUE path)
  *
  */
 static VALUE
-unix_accept(VALUE sock)
+unix_accept(int argc, VALUE *argv, VALUE sock)
 {
     rb_io_t *fptr;
     struct sockaddr_un from;
@@ -53,7 +53,7 @@ unix_accept(VALUE sock)
 
     GetOpenFile(sock, fptr);
     fromlen = (socklen_t)sizeof(struct sockaddr_un);
-    return rsock_s_accept(rb_cUNIXSocket, fptr->fd,
+    return rsock_s_accept(argc, argv, rb_cUNIXSocket, fptr->fd,
 		          (struct sockaddr*)&from, &fromlen);
 }
 
@@ -126,7 +126,7 @@ unix_accept_nonblock(int argc, VALUE *argv, VALUE sock)
  *
  */
 static VALUE
-unix_sysaccept(VALUE sock)
+unix_sysaccept(int argc, VALUE *argv, VALUE sock)
 {
     rb_io_t *fptr;
     struct sockaddr_un from;
@@ -134,7 +134,8 @@ unix_sysaccept(VALUE sock)
 
     GetOpenFile(sock, fptr);
     fromlen = (socklen_t)sizeof(struct sockaddr_un);
-    return rsock_s_accept(0, fptr->fd, (struct sockaddr*)&from, &fromlen);
+    return rsock_s_accept(argc, argv, 0, fptr->fd,
+                          (struct sockaddr*)&from, &fromlen);
 }
 
 #endif
@@ -151,9 +152,9 @@ rsock_init_unixserver(void)
      */
     rb_cUNIXServer = rb_define_class("UNIXServer", rb_cUNIXSocket);
     rb_define_method(rb_cUNIXServer, "initialize", unix_svr_init, 1);
-    rb_define_method(rb_cUNIXServer, "accept", unix_accept, 0);
+    rb_define_method(rb_cUNIXServer, "accept", unix_accept, -1);
     rb_define_method(rb_cUNIXServer, "accept_nonblock", unix_accept_nonblock, -1);
-    rb_define_method(rb_cUNIXServer, "sysaccept", unix_sysaccept, 0);
+    rb_define_method(rb_cUNIXServer, "sysaccept", unix_sysaccept, -1);
     rb_define_method(rb_cUNIXServer, "listen", rsock_sock_listen, 1); /* in socket.c */
 #endif
 }
diff --git a/test/socket/test_nonblock.rb b/test/socket/test_nonblock.rb
index 0853a15..0c47b39 100644
--- a/test/socket/test_nonblock.rb
+++ b/test/socket/test_nonblock.rb
@@ -26,8 +26,20 @@ class TestSocketNonblock < Test::Unit::TestCase
       s, sockaddr = serv.accept_nonblock
     end
     assert_equal(Socket.unpack_sockaddr_in(c.getsockname), Socket.unpack_sockaddr_in(sockaddr))
-    if s.respond_to?(:nonblock?)
-      assert_predicate(s, :nonblock?, 'accepted socket is non-blocking')
+
+    assert_predicate(s, :nonblock?, 'default behavior should be non-blocking')
+
+    [ true, false ].each do |nb|
+      begin
+        b = Socket.new(:INET, :STREAM)
+        b.connect(serv.getsockname)
+        serv.wait_readable
+        a, _ = serv.accept_nonblock(sock_nonblock: nb)
+        assert_equal nb, a.nonblock?
+      ensure
+        a.close if a
+        b.close if b
+      end
     end
   ensure
     serv.close if serv
@@ -392,4 +404,71 @@ class TestSocketNonblock < Test::Unit::TestCase
     s.close if s && !s.closed?
   end
 
+  def test_accept_sock_nonblock
+    serv = Socket.new(:INET, :STREAM)
+    serv.bind(Socket.sockaddr_in(0, "127.0.0.1"))
+    serv.listen(5)
+    begin
+      s, _ = serv.accept_nonblock
+    rescue Errno::EWOULDBLOCK
+      assert_kind_of(IO::WaitReadable, $!)
+    end
+  ensure
+    serv.close if serv && !serv.closed?
+    s.close if s && !s.closed?
+  end
+
+  def test_accept_blocking_sock_nonblock
+    serv = Socket.new(:INET, :STREAM)
+    serv.bind(Socket.sockaddr_in(0, "127.0.0.1"))
+    serv.listen(5)
+
+    begin
+      b = Socket.new(:INET, :STREAM)
+      b.connect(serv.getsockname)
+      a, _ = serv.accept
+      refute_predicate a, :nonblock?
+    ensure
+      a.close
+      b.close
+    end
+
+    begin
+      b = Socket.new(:INET, :STREAM)
+      b.connect(serv.getsockname)
+      a, _ = serv.sysaccept
+      a = Socket.for_fd(a)
+      refute_predicate a, :nonblock?
+    ensure
+      a.close
+      b.close
+    end
+
+    [ true, false ].each do |nb|
+      begin
+        b = Socket.new(:INET, :STREAM)
+        b.connect(serv.getsockname)
+        serv.wait_readable
+        a, _ = serv.accept(sock_nonblock: nb)
+        assert_equal nb, a.nonblock?
+      ensure
+        a.close if a
+        b.close if b
+      end
+
+      begin
+        b = Socket.new(:INET, :STREAM)
+        b.connect(serv.getsockname)
+        serv.wait_readable
+        a, _ = serv.sysaccept(sock_nonblock: nb)
+        a = Socket.for_fd(a)
+        assert_equal nb, a.nonblock?
+      ensure
+        a.close if a
+        b.close if b
+      end
+    end
+  ensure
+    serv.close
+  end
 end if defined?(Socket)
diff --git a/test/socket/test_tcp.rb b/test/socket/test_tcp.rb
index 5e3528e..357f441 100644
--- a/test/socket/test_tcp.rb
+++ b/test/socket/test_tcp.rb
@@ -4,6 +4,8 @@ begin
 rescue LoadError
 end
 
+require "io/wait"
+require "io/nonblock"
 
 class TestSocket_TCPSocket < Test::Unit::TestCase
   def test_initialize_failure
@@ -82,6 +84,57 @@ class TestSocket_TCPSocket < Test::Unit::TestCase
       assert_raise(IO::WaitReadable) { svr.accept_nonblock }
       assert_equal :wait_readable, svr.accept_nonblock(exception: false)
       assert_raise(IO::WaitReadable) { svr.accept_nonblock(exception: true) }
+      addr = svr.addr
+      host, port = addr[3], addr[1]
+
+      begin
+        c = TCPSocket.new(host, port)
+        svr.wait_readable
+        a = svr.accept_nonblock
+        assert_predicate a, :nonblock?, 'default behavior'
+      ensure
+        c.close if c
+        a.close if a
+      end
+
+      [ true, false ].each do |nb|
+        begin
+          c = TCPSocket.new(host, port)
+          svr.wait_readable
+          a = svr.accept_nonblock(sock_nonblock: nb)
+          assert_equal nb, a.nonblock?
+        ensure
+          c.close if c
+          a.close if a
+        end
+      end
     }
   end
+
+  def test_accept_sock_nonblock
+    TCPServer.open("localhost", 0) do |svr|
+      addr = svr.addr
+      host, port = addr[3], addr[1]
+
+      begin
+        c = TCPSocket.new(host, port)
+        a = svr.accept
+        refute_predicate a, :nonblock?, 'default behavior'
+      ensure
+        c.close if c
+        a.close if a
+      end
+
+      [ true, false ].each do |nb|
+        begin
+          c = TCPSocket.new(host, port)
+          a = svr.accept(sock_nonblock: nb)
+          assert_equal nb, a.nonblock?
+        ensure
+          c.close if c
+          a.close if a
+        end
+      end
+    end
+  end
 end if defined?(TCPSocket)
diff --git a/test/socket/test_unix.rb b/test/socket/test_unix.rb
index 9ca97d2..2ae0f9a 100644
--- a/test/socket/test_unix.rb
+++ b/test/socket/test_unix.rb
@@ -9,6 +9,7 @@ require "timeout"
 require "tmpdir"
 require "thread"
 require "io/nonblock"
+require "io/wait"
 
 class TestSocket_UNIXSocket < Test::Unit::TestCase
   def test_fd_passing
@@ -675,6 +676,72 @@ class TestSocket_UNIXSocket < Test::Unit::TestCase
       assert_raise(IO::WaitReadable) { serv.accept_nonblock }
       assert_raise(IO::WaitReadable) { serv.accept_nonblock(exception: true) }
       assert_equal :wait_readable, serv.accept_nonblock(exception: false)
+
+      begin
+        c = UNIXSocket.new(path)
+        serv.wait_readable
+        a = serv.accept_nonblock
+        assert_predicate a, :nonblock?, 'default behavior'
+      ensure
+        c.close if c
+        a.close if a
+      end
+
+      [ true, false ].each do |nb|
+        begin
+          c = UNIXSocket.new(path)
+          serv.wait_readable
+          a = serv.accept_nonblock(sock_nonblock: nb)
+          assert_equal nb, a.nonblock?
+        ensure
+          c.close if c
+          a.close if a
+        end
+      end
     }
   end
+
+  def test_accept_sock_nonblock
+    bound_unix_socket(UNIXServer) do |serv, path|
+      begin
+        c = UNIXSocket.new(path)
+        a = serv.accept
+        refute_predicate a, :nonblock?, 'default behavior'
+      ensure
+        c.close if c
+        a.close if a
+      end
+
+      begin
+        c = UNIXSocket.new(path)
+        a = serv.sysaccept
+        a = UNIXSocket.for_fd(a)
+        refute_predicate a, :nonblock?, 'default behavior'
+      ensure
+        c.close if c
+        a.close if a
+      end
+
+      [ true, false ].each do |nb|
+        begin
+          c = UNIXSocket.new(path)
+          a = serv.accept(sock_nonblock: nb)
+          assert_equal nb, a.nonblock?
+        ensure
+          c.close if c
+          a.close if a
+        end
+
+        begin
+          c = UNIXSocket.new(path)
+          a = serv.sysaccept(sock_nonblock: nb)
+          a = UNIXSocket.for_fd(a)
+          assert_equal nb, a.nonblock?
+        ensure
+          c.close if c
+          a.close if a
+        end
+      end
+    end
+  end
 end if defined?(UNIXSocket) && /cygwin/ !~ RUBY_PLATFORM
-- 
EW


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2015-07-02  1:36 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-07-02  1:36 [PATCH] socket: support accept `sock_nonblock: (true|false)' Eric Wong

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).