From: Bill Kelly Date: 2004-08-27T05:18:27+09:00 Subject: Re: [BUG?] erroneous deadlock? (was: apparent hang...) Hi yet again, :) I think my script is simple enough to post now... After running it for a few minutes, I get: w{-}{}{}S+w{-}{}{}S+w{-}{}{}S+w{-}{ deadlock 0x2a74888: sleep:- - c:/dev/ruby-dbg/lib/ruby/1.8/thread.rb:195 deadlock 0x2a85628: sleep:- (main) - c:/dev/ruby-dbg/lib/ruby/1.8/thread.rb:195 c:/dev/ruby-dbg/lib/ruby/1.8/thread.rb:195:in `stop': Thread(0x2a85628): deadlock (fatal) from c:/dev/ruby-dbg/lib/ruby/1.8/thread.rb:195:in `wait' from c:/dev/ruby-dbg/lib/ruby/1.8/thread.rb:193:in `exclusive_unlock' from c:/dev/ruby-dbg/lib/ruby/1.8/thread.rb:142:in `exclusive' from c:/dev/ruby-dbg/lib/ruby/1.8/thread.rb:142:in `exclusive_unlock' from c:/dev/ruby-dbg/lib/ruby/1.8/thread.rb:193:in `wait' from buffered-io-test4d.rb:46:in `global_wait' from buffered-io-test4d.rb:44:in `synchronize' from buffered-io-test4d.rb:44:in `global_wait' from buffered-io-test4d.rb:66 from buffered-io-test4d.rb:60:in `loop' from buffered-io-test4d.rb:60 I believe this script is simple enough that based on inspection we should be able to say there is no way it could deadlock. I'll intersperse some comments to try to support this assertion: 01: 02: require 'socket' 03: require 'thread' 04: require 'timeout' 05: 06: $frame = 0 07: 08: $global_mutex = Mutex.new 09: $global_signal = ConditionVariable.new 10: 11: $background_mutex = Mutex.new 12: $background_signal = ConditionVariable.new 13: 14: $sock = TCPSocket.new("curmudgeon", 27999) # "curmudgeon:27999" is a server that will merely # send back a prompt then wait for us to respond. # We are not going to respond - so after we've read # the initial prompt, we expect line 18 to wait # forever in the select. This is by intention, as # it seems to be important to have a thread doing # a time-unlimited select to get these problems to # occur. # # (Note that we can replace line 14 with: # $server = TCPServer.new(12345) # $sock = TCPSocket.new("localhost", 12345) # and get similar erroneous behavior. But for # whatever reason the localhost version exhibits # the "hanging" behavior, while the "curmudgeon:27999" # version prefers to deadlock.) # So - once background_read has read the initial # data it receives from the server, we expect it # to just wait forever on line 18. 15: 16: def background_read 17: loop do 18: if select([$sock], nil, nil, nil) 19: print "r" 20: dat = $bg_rd_sock.recv(65536) 21: $global_signal.signal 22: end 23: end 24: end 25: # background_write is the only code owning the # $background_mutex... so there should never be # any deadlock contention there. So all we do # is repeatedly wait for the main thread to # signal us. Then print chars to stdout and go # back to sleep. 26: def background_write 27: loop do 28: $background_mutex.synchronize { 29: print "-" 30: $background_signal.wait($background_mutex) 31: print "+" 32: } 33: 34: print "w" 35: end 36: end 37: # global_wait creates a secondary alarm thread to # awaken the primary thread doing the wait after # the timeout elapses. Line 44 is the only code # owning the $global_mutex, so there should not be # any deadlock contention there. # Line 46 is where the deadlocks and hangs always # occur. 38: def global_wait(timeout_secs) 39: begin 40: alarm_th = Thread.start { 41: sleep timeout_secs 42: $global_signal.broadcast # %%WAS: .signal 43: } 44: $global_mutex.synchronize { 45: print "{" 46: $global_signal.wait($global_mutex) 47: print "}" 48: } 49: ensure 50: alarm_th.kill if alarm_th and alarm_th.alive? 51: end 52: end 53: 54: 55: bg_read_th = Thread.new { background_read } 56: bg_write_th = Thread.new { background_write } 57: 58: $stdout.sync = true 59: # Our main loop does nothing but periodically signal # the background_write thread, and sleep in global_wait. 60: loop do 61: $frame += 1 62: if ($frame % 3) == 0 63: print "S" 64: $background_signal.signal 65: end 66: global_wait(0.003) 67: end 68: This script will either deadlock, or hang, on both Windows (1.8.2preview2) and Linux (1.8.0). I believe it is simple enough to claim that by inspection it should never deadlock or hang. (Unless I am just being dense.) I will begin delving into rb_thread_shedule to try to determine what is going awry. Any tips from the experts would be most welcome. :) Regards, Bill