sockets and threads

Discussion in 'Ruby' started by Dominik Werder, Mar 31, 2006.

  1. Hello,

    I observed that even if every thread in a program creates it's own
    Socket, but all to the same Server, then this is not thread-safe.

    It is the same situation as if every thread shared the same Socket
    without any mutexes. I observed this with TCPSockets, UNIXSockets ans

    I run Kernel, ruby 1.8.4 (2005-12-24) [i686-linux]

    The short source code below produces the following output on the server.
    If I enabled the mutex (the 3 lines commented out) it would work better.

    Got: thread[16] says 65
    Got: thread[19] says 68thread[17] says 20
    Got: thread[24] says 97
    Got: thread[8] says 64thread[26] says 17thread[13] says 43
    Got: thread[7] says 84
    Got: thread[0] says 78thread[18] says 37
    Got: thread[27] says 98
    Got: thread[18] says 13
    Got: thread[11] says 19
    Got: thread[1] says 91thread[26] says 18
    Got: thread[1] says 53
    Got: thread[19] says 42

    # First start the server with:
    # ruby tcpsockets.rb server
    # Then start the client which spawns several threads:
    # ruby tcpsockets.rb

    require 'socket'
    require 'monitor'

    if ARGV[0] == 'server'
    threads = []
    server = 'localhost', 8889
    putslock =
    while socket = server.accept
    t = do |sock|
    while str = sock.gets
    putslock.synchronize do
    puts "Got: #{str}"
    threads << t
    threads.each do |t| t.join end
    Thread.abort_on_exception = true
    #socketlock =
    threads = []
    30.times do |i|
    t = do |ii|
    count = 0
    socket = 'localhost', 8889
    puts "Using socket #{socket.object_id}"
    while count < 10
    #socketlock.synchronize do
    socket.puts "thread[#{ii}] says #{Kernel.rand 100}"
    sleep Kernel.rand * 0.3
    count += 1
    threads << t
    threads.each do |t| t.join end

    Is this known/intended behavior?

    thanks and a good day!
    Dominik Werder, Mar 31, 2006
    1. Advertisements

  2. Hello,

    I found the bug: In the client code the variable "socket" was already
    known to the interpreter, but anyway not in the same scope, so every
    thread overwrote the socket and in the end all used the same connection..

    I didn't know that

    if false
    yo = 123

    would output "NilClass"..

    Dominik Werder, Mar 31, 2006
    1. Advertisements

  3. I'm getting more and more defensive about this kind of bug. I usually
    just do { |...| some_method(...)}

    and then I can code some_method without worrying about local variable

    Another example is when threads are created in a loop. Even if the
    thread code is very small, local vars with shared binding can be

    a = []
    i = 0
    (0...100).map do {sleep 0.001; a << i}
    i += 1
    sleep 1
    p a.uniq.size

    Output is unpredictable, 74, 86, etc. (YMMV)

    Obviously, this could be solved by making i local to the block, or
    passing it to Without the sleep 0.001, I don't seem to get
    this behavior (the output is 100), but I wouldn't rely on that: the ruby
    thread scheduler could still reasonably schedule threads so that two or
    more of them saw the same value of i. So it's still best to pass
    arguments to
    Joel VanderWerf, Mar 31, 2006
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.