In article <E8AB2CFA-FDAF-47B3-A0BE-4C089960C5CF / dragonascendant.com>,
  Christoffer Lern<lerno / dragonascendant.com> 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