Forums » Ruby-core » Nonblocking accept

Nonblocking accept
Posted by Francis Cianfrocca (Guest)
on 26.05.2006 14:30
Thanks to the Matz and colleagues for adding the *_nonblock functions. 
They
have been a huge help. I've already gotten some big performance 
improvements
in the EventMachine event-processing library. I thought it was a little
strange that accept_nonblock is not available for TCPServer (because it
inherits from IO rather than Socket) but it wasn't hard to work around 
that.
(Just create a Socket object and call bind and listen on it myself.) The
nonblock functions seem to work well with files and unix-domain sockets. 
I
haven't tested datagrams yet but will do so shortly.

Thanks again, team.
Re: Nonblocking accept
Posted by Tanaka Akira (Guest)
on 27.05.2006 03:58
In article 
<3a94cf510605260529x658e20e1q7d9754266675c3d5@mail.gmail.com>,
  "Francis Cianfrocca" <garbagecat10@gmail.com> writes:

> Thanks to the Matz and colleagues for adding the *_nonblock functions. They
> have been a huge help. I've already gotten some big performance improvements
> in the EventMachine event-processing library. I thought it was a little
> strange that accept_nonblock is not available for TCPServer (because it
> inherits from IO rather than Socket) but it wasn't hard to work around that.
> (Just create a Socket object and call bind and listen on it myself.) The
> nonblock functions seem to work well with files and unix-domain sockets. I
> haven't tested datagrams yet but will do so shortly.

Since accept is for connection oriented sockets, UDP doesn't need it.

Index: ext/socket/socket.c
===================================================================
RCS file: /src/ruby/ext/socket/socket.c,v
retrieving revision 1.164
diff -u -p -r1.164 socket.c
--- ext/socket/socket.c	25 May 2006 23:43:29 -0000	1.164
+++ ext/socket/socket.c	27 May 2006 01:52:19 -0000
@@ -1383,6 +1383,20 @@ tcp_svr_init(argc, argv, sock)
 }

 static VALUE
+s_accept_nonblock(VALUE klass, OpenFile *fptr, struct sockaddr 
*sockaddr, socklen_t *len)
+{
+    int fd2;
+
+    rb_secure(3);
+    rb_io_set_nonblock(fptr);
+    fd2 = accept(fptr->fd, (struct sockaddr*)sockaddr, len);
+    if (fd2 < 0) {
+        rb_sys_fail("accept(2)");
+    }
+    return init_sock(rb_obj_alloc(klass), fd2);
+}
+
+static VALUE
 s_accept(klass, fd, sockaddr, len)
     VALUE klass;
     int fd;
@@ -1435,6 +1449,49 @@ tcp_accept(sock)
 		    (struct sockaddr*)&from, &fromlen);
 }

+/*
+ * call-seq:
+ * 	tcpserver.accept_nonblock => tcpsocket
+ *
+ * Accepts an incoming connection using accept(2) after
+ * O_NONBLOCK is set for the underlying file descriptor.
+ * It returns an accepted TCPSocket for the incoming connection.
+ *
+ * === Example
+ * 	require 'socket'
+ * 	serv = TCPServer.new(2202)
+ * 	begin
+ * 	  sock = serv.accept_nonblock
+ * 	rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EPROTO, 
Errno::EINTR
+ * 	  IO.select([serv])
+ * 	  retry
+ * 	end
+ * 	# sock is an accepted socket.
+ *
+ * Refer to Socket#accept for the exceptions that may be thrown if the 
call
+ * to TCPServer#accept_nonblock fails.
+ *
+ * TCPServer#accept_nonblock may raise any error corresponding to 
accept(2) failure,
+ * including Errno::EAGAIN.
+ *
+ * === See
+ * * TCPServer#accept
+ * * Socket#accept
+ */
+static VALUE
+tcp_accept_nonblock(sock)
+    VALUE sock;
+{
+    OpenFile *fptr;
+    struct sockaddr_storage from;
+    socklen_t fromlen;
+
+    GetOpenFile(sock, fptr);
+    fromlen = sizeof(from);
+    return s_accept_nonblock(rb_cTCPSocket, fptr,
+                             (struct sockaddr *)&from, &fromlen);
+}
+
 static VALUE
 tcp_sysaccept(sock)
     VALUE sock;
@@ -1925,6 +1982,49 @@ unix_accept(sock)
 		    (struct sockaddr*)&from, &fromlen);
 }

+/*
+ * call-seq:
+ * 	unixserver.accept_nonblock => unixsocket
+ *
+ * Accepts an incoming connection using accept(2) after
+ * O_NONBLOCK is set for the underlying file descriptor.
+ * It returns an accepted UNIXSocket for the incoming connection.
+ *
+ * === Example
+ * 	require 'socket'
+ * 	serv = UNIXServer.new("/tmp/sock")
+ * 	begin
+ * 	  sock = serv.accept_nonblock
+ * 	rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EPROTO, 
Errno::EINTR
+ * 	  IO.select([serv])
+ * 	  retry
+ * 	end
+ * 	# sock is an accepted socket.
+ *
+ * Refer to Socket#accept for the exceptions that may be thrown if the 
call
+ * to UNIXServer#accept_nonblock fails.
+ *
+ * UNIXServer#accept_nonblock may raise any error corresponding to 
accept(2) failure,
+ * including Errno::EAGAIN.
+ *
+ * === See
+ * * UNIXServer#accept
+ * * Socket#accept
+ */
+static VALUE
+unix_accept_nonblock(sock)
+    VALUE sock;
+{
+    OpenFile *fptr;
+    struct sockaddr_storage from;
+    socklen_t fromlen;
+
+    GetOpenFile(sock, fptr);
+    fromlen = sizeof(from);
+    return s_accept_nonblock(rb_cUNIXSocket, fptr,
+                             (struct sockaddr *)&from, &fromlen);
+}
+
 static VALUE
 unix_sysaccept(sock)
     VALUE sock;
@@ -2784,19 +2884,12 @@ sock_accept_nonblock(sock)
     VALUE sock;
 {
     OpenFile *fptr;
-    int fd2;
     VALUE sock2;
     char buf[1024];
     socklen_t len = sizeof buf;

     GetOpenFile(sock, fptr);
-    rb_io_set_nonblock(fptr);
-    fd2 = accept(fptr->fd, (struct sockaddr*)buf, &len);
-    if (fd2 < 0) {
-        rb_sys_fail("accept(2)");
-    }
-    sock2 = init_sock(rb_obj_alloc(rb_cSocket), fd2);
-
+    sock2 = s_accept_nonblock(rb_cSocket, fptr, (struct sockaddr *)buf, 
&len);
     return rb_assoc_new(sock2, rb_str_new(buf, len));
 }

@@ -3414,6 +3507,7 @@ Init_socket()
     rb_cTCPServer = rb_define_class("TCPServer", rb_cTCPSocket);
     rb_define_global_const("TCPserver", rb_cTCPServer);
     rb_define_method(rb_cTCPServer, "accept", tcp_accept, 0);
+    rb_define_method(rb_cTCPServer, "accept_nonblock", 
tcp_accept_nonblock, 0);
     rb_define_method(rb_cTCPServer, "sysaccept", tcp_sysaccept, 0);
     rb_define_method(rb_cTCPServer, "initialize", tcp_svr_init, -1);
     rb_define_method(rb_cTCPServer, "listen", sock_listen, 1);
@@ -3442,6 +3536,7 @@ Init_socket()
     rb_define_global_const("UNIXserver", rb_cUNIXServer);
     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_nonblock", 
unix_accept_nonblock, 0);
     rb_define_method(rb_cUNIXServer, "sysaccept", unix_sysaccept, 0);
     rb_define_method(rb_cUNIXServer, "listen", sock_listen, 1);
 #endif
Re: Nonblocking accept
Posted by Francis Cianfrocca (Guest)
on 27.05.2006 04:31
Thanks for the patch, I'll test as soon as I can. In regard to 
datagrams,
what I meant was nonblocking recvfrom. I haven't tried that one yet.

Let me repeat- the performance boost from nonblocking connect is really
great. Thanks again.