From: Leslie Viljoen Date: 2007-04-19T05:15:47+09:00 Subject: Re: Why does this code deadlock? On 4/18/07, Bill Kelly wrote: > > From: "Yukihiro Matsumoto" > > > > In message "Re: Why does this code deadlock?" > > on Wed, 18 Apr 2007 16:21:48 +0900, Nobuyoshi Nakada writes: > > > > |At Wed, 18 Apr 2007 08:34:03 +0900, > > |Bill Kelly wrote in [ruby-talk:248300]: > > |> require 'net/ftp' > > |> Thread.abort_on_exception = true > > |> ftp = Net::FTP.new('ftp.idsoftware.com') > > | th = Thread.new {$SAFE=4; ftp.login('anonymous', 'abc@def.com')} > > | Thread.critical = false > > | p th.value > > > > Who set Thread.critical? It should be fixed, I think. > > It seems to be something called by ftp.login: (I added some > sleeps to coordinate the two threads.) > > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > require 'fastthread' > require 'net/ftp' > Thread.abort_on_exception = true > puts "1. crit=#{Thread.critical}" > ftp = Net::FTP.new('ftp.idsoftware.com') > puts "2. crit=#{Thread.critical}" > th = Thread.new {sleep(2); $SAFE=4; sleep(2); ftp.login('anonymous', 'abc@def.com')} > puts "3. crit=#{Thread.critical}" > sleep(3) > puts "4. crit=#{Thread.critical}" > sleep(2) > puts "5. crit=#{Thread.critical}" > # Thread.critical = false > p th.value > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > > $ ruby -v ftp_safe_test.rb > ruby 1.8.6 (2007-03-16 patchlevel 3) [i686-linux] > 1. crit=false > 2. crit=false > 3. crit=false > 4. crit=false > 5. crit=true > ftp_safe_test.rb:14:in `value': Thread(0x525ad708): deadlock (fatal) > from ftp_safe_test.rb:14 > > > If I uncomment the `Thread.critical = false', it prints: > > $ ruby -v ftp_safe_test.rb > ruby 1.8.6 (2007-03-16 patchlevel 3) [i686-linux] > 1. crit=false > 2. crit=false > 3. crit=false > 4. crit=false > 5. crit=true > /opt/lib/ruby/1.8/monitor.rb:284:in `mon_acquire': Insecure: can't modify instance variable (SecurityError) > from ftp_safe_test.rb:14:in `value' > from ftp_safe_test.rb:14 > > > This is in MonitorMixin::ConditionVariable: > > def mon_acquire(queue) > while @mon_owner && @mon_owner != Thread.current > queue.push(Thread.current) > Thread.stop > Thread.critical = true > end > @mon_owner = Thread.current > end ftp.login calls synchronize in thread.rb, which calls lock in thread.rb, which sets critical: def lock while (Thread.critical = true; @locked) @waiting.push Thread.current Thread.stop end @locked = true Thread.critical = false self end Maybe I'm barking up the wrong tree.