From: Bill Kelly Date: 2006-05-09T06:24:56+09:00 Subject: Re: Considering Ruby For a Networking Application ------=_NextPart_000_0149_01C672AA.E18C4170 Content-Type: text/plain; format=flowed; charset="iso-8859-1"; reply-type=response Content-Transfer-Encoding: 7bit From: "James Edward Gray II" > > On May 8, 2006, at 12:12 PM, Francis Cianfrocca wrote: > >> If your performance and scalability requirements are low, then the >> easiest thing is >> probably to use blocking i/o with a thread per socket. If you use >> blocking >> i/o, you're better off multiplexing the i/o with a select loop. > > But then a large write could still block the entire process, since > Ruby's threads aren't native. Right? Judging from the results of my test program on Linux and Windows, it appears nonblocking writes are supported in 1.8.4. ruby 1.8.4 (2005-12-24) [i686-linux] ruby 1.8.4 (2005-12-24) [i386-mswin32] Interestingly, while the Linux version behaved as expected, displaying blocking characteristics until I explicitly set nonblock=true, the windows version appears to sport some sort of always-on nonblocking buffered implementation behind the scenes now. On Windows, even if I never set nonblock=true, I could syswrite 40 megs to the socket and #syswrite would return immediately! (Subsequent syswrites would then appear to block until the data from the previous huge syswrite was consumed; but just the ruby thread performing the syswrite was blocked, never the whole process. So anyway there's some kind of dynamic buffering going on behind the scenes in the windows implementation--but in any case, it never blocked the whole process. Excellent.) In my test program (attached) I'm catching just about every exception under the sun, including EAGAIN, which I saw being done in lib/openssl/buffering.rb. However, the only exception I actually saw raised was EOFError. (I never tried to force an EPIPE condition on the write.) Anyway, for what it's worth, it appears ruby 1.8.4 now definitely supports nonblocking socket I/O on both Linux and Windows!! Yay! I'll still be looking at EventMachine though. :) Regards, Bill ------=_NextPart_000_0149_01C672AA.E18C4170 Content-Type: application/octet-stream; name="nonblock_test.rb" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="nonblock_test.rb" require 'socket'=0A= require 'io/nonblock'=0A= =0A= # hmm, io/nonblock refuses to work on windows because=0A= # of missing GETFL :( Just fake it...=0A= if RUBY_PLATFORM =3D~ /mswin32/=0A= class IO=0A= def nonblock=3D(nb)=0A= fcntl(Fcntl::F_SETFL, nb ? Fcntl::O_NONBLOCK : 0)=0A= end=0A= end=0A= end=0A= =0A= sv =3D TCPServer.new 12345=0A= cl =3D TCPSocket.new "localhost", 12345=0A= sv_cl =3D sv.accept=0A= =0A= # NOTE: setting nonblock seems to be completely OPTIONAL on windows!=0A= # There's some kind of buffered nonblocking implementation=0A= # going on behind the scenes in 1.8.4 win32, different from=0A= # 1.8.4 linux.=0A= cl.nonblock =3D true=0A= sv_cl.nonblock =3D true=0A= =0A= # add a background print thread to the mix, just for fun=0A= bg_print_th =3D Thread.new do=0A= loop do=0A= print "."=0A= sleep 0.5=0A= end=0A= end=0A= =0A= bg_write_th =3D Thread.new(cl) do |sock|=0A= 5.times do=0A= data =3D "foo" * 1000000=0A= begin=0A= res =3D sock.syswrite(data)=0A= rescue Errno::EAGAIN=0A= print "(write:EAGAIN)"=0A= retry=0A= rescue EOFError=0A= print "(write:EOF)"=0A= rescue IOError, SystemCallError, SocketError =3D> ex=0A= print "(write:#{ex})"=0A= end=0A= puts " wrote #{res} bytes."=0A= end=0A= puts " write thread exit."=0A= end=0A= =0A= bg_read_th =3D Thread.new(sv_cl) do |sock|=0A= # be deliberately slow about reading=0A= total =3D 0=0A= begin=0A= loop do=0A= sleep 0.1 if rand(50) =3D=3D 0=0A= begin=0A= data =3D sock.readpartial(4096)=0A= rescue Errno::EAGAIN=0A= print "(read:EAGAIN)"=0A= retry=0A= end=0A= total +=3D data.length=0A= puts " read #{data.length} bytes (#{data[0..6]}...) (#{total} = total.)"=0A= end=0A= rescue EOFError=0A= print "(read:EOF)"=0A= rescue IOError, SystemCallError, SocketError =3D> ex=0A= print "(read:#{ex})"=0A= end=0A= puts " read thread exit."=0A= end=0A= =0A= bg_write_th.join=0A= cl.close=0A= bg_read_th.join=0A= sv_cl.close=0A= sv.close=0A= bg_print_th.kill=0A= =0A= =0A= ------=_NextPart_000_0149_01C672AA.E18C4170--