On 10/19/2009 02:51 PM, Daniel Bush wrote:
> Hi,
> I think I'm running up against ruby 1.8.6's not so
> stellar threading system.  Was hoping someone
> could confirm or otherwise point out some flaws.
> 
> Note: I get reasonable performance when running on
> ruby 1.9 it's just 1.8.6 that hangs like a
> deadlock when I start using too many threads in
> one of my test scripts.  (My focus is actually
> on 1.9 and jruby anyway).
> 
> Give you an idea:
> 
> I might get a pool of 10 acceptor threads to run
> something like the following (each has their own
> version of this code):
> 
>     client, client_sockaddr = @socket.accept
>       # Threads block on #accept.
>     data = client.recvfrom( 40 )[0].chomp
>     @mutex.synchronize do
>       puts "#{Thread.current} received #{data}... "
>     end
>     client.close
> 
> on @socket which was set up like this:
> 
>     @socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
>     @sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost')
>     @socket.bind( @sockaddr )
>     @socket.listen( 100 )

This won't work.  You can have only 1 acceptor thread per server socket. 
  Typically you dispatch processing *after* the accept to a thread 
(either newly created or taken from a pool).

I have no idea what the interpreter is going to do if you have multiple 
threads trying to accept from the same socket.  In the best case #accept 
is synchronized and only one thread gets to enter it.  In worse 
scenarios anything bad may happen.

> I wanted to create a barrage of requests so next I
> create a pool of requester threads which each run
> something like this:
> 
>   socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
>   sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
>   socket.connect( sockaddr )
>   socket.puts "request #{i}"
>   socket.close

Btw, why don't you use TCPServer and TCPSocket?

> All of this in one script.  If I have so much as
> 2 requester threads in addition to the 10
> acceptors waiting to receive their requests, 1.8.6
> just seizes up before processing anything.  If I
> use 2 acceptors and 2 requesters, it works. If I
> use 10 acceptors, 1 requester it works.  When it
> does work however, it doesn't appear to schedule
> threads too well; it just seems to use one all the
> time - although this seems to happen only when
> using sockets as opposed to a more general job
> queue.

See above.

> I haven't submitted the full code because it uses
> a threadpool library I'm still building/reviewing.

I would rather do something like this (sketeched):

require 'thread'
queue = Queue.new
workers = (1..10).map do
   Thread.new queue do |q|
     until (cl = q.deq).equal? q
       # process data from / for client cl
       begin
         data = cl.gets.chomp
         @mutex.synchronize do
           puts "#{Thread.current} received #{data}..."
         end
       ensure
         cl.close
       end
     end
   end
end

server = TCPServer.new ...

while client = server.accept
   queue.enq client
end

# elsewhere


TCPSocket.open do |sock|
    sock.puts "request"
end

Kind regards

	robert


-- 
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/