From: Tanaka Akira Date: 2007-10-15T01:14:11+09:00 Subject: Re: write_nonblock is blocking? In article , Christoffer Lernö writes: > Could this be due to me running > > ruby 1.8.6 (2007-03-13 patchlevel 0) [i686-darwin8.10.1] ? > > The funny thing was that only the socket returned by accept_nonblock > had this behaviour, that is I think it is a Mac OS X problem. Your example works without problem on GNU/Linux and FreeBSD box but not on Mac OS X. % cat z1.rb require 'socket' require 'fcntl' a = TCPServer.new(5000) socket = TCPSocket.new("localhost", 5000) other = a.accept p other.fcntl(Fcntl::F_GETFL) & Fcntl::O_NONBLOCK puts "Start Write" p other.write_nonblock("X" * 300000) puts "End Write" % ./ruby -v z1.rb ruby 1.8.6 (2007-03-13 patchlevel 0) [powerpc-darwin8.3.0] 0 Start Write 81560 End Write The IO returned from accept is not nonblocking mode. So write_nonblock makes it nonblocking mode at first. % cat z2.rb require 'socket' require 'fcntl' a = TCPServer.new(5000) socket = TCPSocket.new("localhost", 5000) other = a.accept_nonblock p other.fcntl(Fcntl::F_GETFL) & Fcntl::O_NONBLOCK puts "Start Write" p other.write_nonblock("X" * 300000) puts "End Write" % ./ruby -v z2.rb ruby 1.8.6 (2007-03-13 patchlevel 0) [powerpc-darwin8.3.0] 4 Start Write ^Cz2.rb:8: Interrupt The IO returned from accept_nonblock seems nonblocking mode. So write_nonblock doesn't make it nonblocking mode. But actually it is not nonblocking mode, I think. Workaround: Index: ext/socket/socket.c =================================================================== --- ext/socket/socket.c (revision 13670) +++ ext/socket/socket.c (working copy) @@ -1464,6 +1464,24 @@ return init_inetsock(sock, Qnil, arg1, Qnil, Qnil, INET_SERVER); } +static void +make_fd_nonblock(int fd) +{ + int flags; +#ifdef F_GETFL + flags = fcntl(fd, F_GETFL); + if (flags == -1) { + rb_sys_fail(0); + } +#else + flags = 0; +#endif + flags |= O_NONBLOCK; + if (fcntl(fd, F_SETFL, flags) == -1) { + rb_sys_fail(0); + } +} + static VALUE s_accept_nonblock(VALUE klass, OpenFile *fptr, struct sockaddr *sockaddr, socklen_t *len) { @@ -1475,6 +1493,7 @@ if (fd2 < 0) { rb_sys_fail("accept(2)"); } + make_fd_nonblock(fd2); return init_sock(rb_obj_alloc(klass), fd2); } Anyway nonblocking flag of accepted fd is not portable. http://cr.yp.to/docs/unixport.html So setting it unconditionally is good thing for portability. -- Tanaka Akira