From: yannick Date: 2004-03-27T17:30:51+09:00 Subject: Re: non blocking read and thread Thank you for the tip, using sysread does fix this problem. But after further tests, I found another problem. Apparently, as Ara.T.Howard pointed out before, Kernel#select and IO#sysread seems to be a bad mix. Every sockets Kernel#select returns seems to be buffered, as this code shows : require 'socket' Thread.new do sleep 2 client = TCPSocket.new('localhost', '1999') loop do sleep 1 client.syswrite "message" end end server = TCPServer.new("0.0.0.0","1999") thr = Thread.new do socks = [] loop do rs,wd,ed = select(socks+[server],nil,nil) rs.each do |sock| if sock == server n_sock = server.accept n_sock.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK) socks.push n_sock #This call will not raise an IOError #because this socket in not buffered yet. puts n_sock.sysread(100) elsif sock.closed? || sock.eof? socks.delete sock else #This call however will raise an IOError. puts sock.sysread(100) end end end end thr.join Is it a normal behavior? If so, does an option to allow Kernel#select to returns non-buffered IO exists? Yannick -----Original Message----- From: Tanaka Akira [mailto:akr@m17n.org] Sent: Saturday, March 27, 2004 2:26 AM To: ruby-talk ML Subject: Re: non blocking read and thread In article <000001c4134c$0cbe5920$0300a8c0@boulba>, "yannick" writes: > You are right, after further tests; it looks like a thread scheduling > problem, it could perhaps be solved by making the thread critical, but > then it only return the EAGAIN error. Well, it seems I'm stock here, > better find another way around :-) I recommend sysread instead of IO#read with nonblocking mode. However, your first script is interesting because it transit multithread to singlethread after sock.read(100) is started. When there are two or more threads, Ruby calls select(2) before each IO operation for thread scheduling. But Ruby doesn't call select(2) when there is only one thread. Your first script invokes following system calls. % strace -e read,write,select ruby tst1 ... select(0, [], [], [], {0, 0}) = 0 (Timeout) select(6, [5], [], [], {1, 999368}) = 0 (Timeout) select(6, [5], [], [], {0, 3115}) = 0 (Timeout) select(6, [5], [], [], {0, 0}) = 0 (Timeout) select(9, [5], [7], [], NULL) = 2 (in [5], out [7]) write(1, "try to read it...\n", 18) = 18 (*1) select(9, [8], [], [], {0, 0}) = 0 (Timeout) write(1, "send a message\n", 15) = 15 select(9, [8], [7], [], NULL) = 1 (out [7]) write(7, "message", 7) = 7 select(9, [8], [7], [], NULL) = 2 (in [8], out [7]) select(9, [8], [], [], {0, 0}) = 1 (in [8], left {0, 0}) write(7, "\n", 1) = 1 (*2) read(8, "message\n", 1024) = 8 (*3) read(8, 0x4001a000, 1024) = -1 EAGAIN (Resource temporarily unavailable) write(1, "done !\n", 7) = 7 System calls corresponding to sock.read(100) is (*1), (*2) and (*3) (*1) Check sock is readable or not. Since it is not readable yet, it blocks and switch to other thread. (*2) Read 8bytes from sock: "message\n". (*3) Occur EAGAIN. It terminates sock.read(100). What interesting is that select is not called before (*3). If select is called at (*3), it blocks forever. This means that there are no thread other than the main thread. Actually the client thread is finished before (*2). So sock.read(100) blocks at first(*1) by select because multithread. But it doesn't block at last(*3) because singlethread. I think this behavior is very confusing. -- Tanaka Akira