edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ThreadOps.cs;C695420 File: ThreadOps.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ThreadOps.cs;C695420 (server) 1/4/2009 11:36 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ThreadOps.cs;hangs @@ -159,6 +159,7 @@ internal Exception Exception { get; set; } internal object Result { get; set; } internal bool CreatedFromRuby { get; set; } + private bool IsSleeping { get; set; } internal bool AbortOnException { get { @@ -188,11 +189,18 @@ /// lock which can be signalled from another thread. /// internal void Sleep() { - _runSignal.WaitOne(); + try { + IsSleeping = true; + _runSignal.WaitOne(); + } finally { + IsSleeping = false; + } } internal void Run() { - _runSignal.Set(); + if (IsSleeping) { + _runSignal.Set(); + } } } @@ -490,6 +498,9 @@ } if ((state & ThreadState.WaitSleepJoin) == ThreadState.WaitSleepJoin) { + // We will report a thread to be sleeping more often than in CRuby. This is because any "lock" statement + // can potentially cause ThreadState.WaitSleepJoin. Also, "Thread.pass" does System.Threading.Thread.Sleep(0) + // which also briefly changes the state to ThreadState.WaitSleepJoin return RubyThreadStatus.Sleeping; } =================================================================== edit: $/Merlin_External/Languages/IronRuby/mspec/ironruby-tags/critical_tags.txt;C691193 File: critical_tags.txt =================================================================== --- $/Merlin_External/Languages/IronRuby/mspec/ironruby-tags/critical_tags.txt;C691193 (server) 1/6/2009 11:39 PM +++ Shelved Change: $/Merlin_External/Languages/IronRuby/mspec/ironruby-tags/critical_tags.txt;hangs @@ -37,6 +37,10 @@ core\string\hex_tags.txt:3:critical:String#hex returns 0 on error core\string\oct_tags.txt:3:critical:String#oct takes an optional sign core\string\oct_tags.txt:3:critical:String#oct returns 0 on error +core\thread\critical_tags.txt:0:unstable("Thread.pass uses System.Threading.Thread.Sleep(0) which sets ThreadState.WaitSleepJoin for a brief instant"):Thread.critical does not change status of other existing threads +core\thread\critical_tags.txt:critical("need to track which thread owns critical lock"):Thread.critical defers exit until Thread.pass +core\thread\critical_tags.txt:critical("need to track which thread owns critical lock"):Thread.critical can be mismatched +core\thread\critical_tags.txt:critical("unstable realy. It occasionally hangs"):Thread.critical schedules other threads on Thread.stop core\thread\raise_tags.txt:0:critical:Thread#raise on another thread re-raises active exception language\method_tags.txt:0:critical:Calling a method fails with both lambda and block argument library\mathn\rational\power2_tags.txt:0:critical:Rational#power2 when passed [Rational] returns Rational.new!(1, 1) when the passed argument is 0 =================================================================== edit: $/Merlin_External/Languages/IronRuby/mspec/ironruby-tags/1.8/core/thread/critical_tags.txt;C695420 File: critical_tags.txt =================================================================== --- $/Merlin_External/Languages/IronRuby/mspec/ironruby-tags/1.8/core/thread/critical_tags.txt;C695420 (server) 1/6/2009 4:05 PM +++ Shelved Change: $/Merlin_External/Languages/IronRuby/mspec/ironruby-tags/1.8/core/thread/critical_tags.txt;hangs @@ -1,4 +1,5 @@ -fails:Thread.critical defers exit -critical:Thread.critical defers exit until Thread.pass -fails:Thread.critical is not reset if the critical thread is killed -critical:Thread.critical can be mismatched \ No newline at end of file +unstable("Thread.pass uses System.Threading.Thread.Sleep(0) which sets ThreadState.WaitSleepJoin for a brief instant"):Thread.critical does not change status of other existing threads +fails("need to track which thread owns critical lock"):Thread.critical defers exit +critical("need to track which thread owns critical lock"):Thread.critical defers exit until Thread.pass +critical("need to track which thread owns critical lock"):Thread.critical can be mismatched +critical("unstable realy. It occasionally hangs"):Thread.critical schedules other threads on Thread.stop \ No newline at end of file =================================================================== edit: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/1.8/core/thread/critical_spec.rb;C695420 File: critical_spec.rb =================================================================== --- $/Merlin_External/Languages/IronRuby/mspec/rubyspec/1.8/core/thread/critical_spec.rb;C695420 (server) 12/31/2008 10:48 AM +++ Shelved Change: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/1.8/core/thread/critical_spec.rb;hangs @@ -62,12 +62,12 @@ end it "schedules other threads on sleep" do - ThreadSpecs.critical_thread_yields_to_main_thread { sleep } + ThreadSpecs.critical_thread_yields_to_main_thread(isThreadSleep=true) { sleep } end it "schedules other threads on Thread.stop" do # Note that Thread.Stop resets Thread.critical, whereas sleep does not - ThreadSpecs.critical_thread_yields_to_main_thread(true) { Thread.stop } + ThreadSpecs.critical_thread_yields_to_main_thread(isThreadStop=true) { Thread.stop } end it "defers exit" do @@ -84,7 +84,7 @@ ScratchPad.recorded.should == nil end - not_compliant_on(:ironruby) do # requires green threads + not_compliant_on(:ironruby) do # requires green threads so that another thread can be scheduled when the critical thread is killed it "is not reset if the critical thread is killed" do critical_thread = ThreadSpecs.create_and_kill_critical_thread(true) Thread.pass while critical_thread.status != false =================================================================== edit: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/1.8/core/thread/raise_spec.rb;C695420 File: raise_spec.rb =================================================================== --- $/Merlin_External/Languages/IronRuby/mspec/rubyspec/1.8/core/thread/raise_spec.rb;C695420 (server) 12/30/2008 10:42 PM +++ Shelved Change: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/1.8/core/thread/raise_spec.rb;hangs @@ -70,7 +70,9 @@ rescue RuntimeError => e $!.message.should == "Kill the thread" e.message.should == "Kill the thread" - e.backtrace.select {|b| b =~ /looping_method/ }.size().should == 1 + frame_count = e.backtrace.select {|b| b =~ /looping_method/ }.size() + # There will be two occurences if "loop" is yielding to the block + (frame_count == 1 or frame_count == 2).should == true end 102 end =================================================================== edit: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/1.8/core/thread/run_spec.rb;C544557 File: run_spec.rb =================================================================== --- $/Merlin_External/Languages/IronRuby/mspec/rubyspec/1.8/core/thread/run_spec.rb;C544557 (server) 1/6/2009 10:41 AM +++ Shelved Change: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/1.8/core/thread/run_spec.rb;hangs @@ -1,2 +1,9 @@ require File.dirname(__FILE__) + '/../../spec_helper' -require File.dirname(__FILE__) + '/fixtures/classes' \ No newline at end of file +require File.dirname(__FILE__) + '/fixtures/classes' + +require File.dirname(__FILE__) + '/shared/wakeup' + +describe "Thread#run" do + it_behaves_like :thread_wakeup, :run +end + =================================================================== edit: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/1.8/core/thread/wakeup_spec.rb;C682742 File: wakeup_spec.rb =================================================================== --- $/Merlin_External/Languages/IronRuby/mspec/rubyspec/1.8/core/thread/wakeup_spec.rb;C682742 (server) 1/6/2009 10:15 AM +++ Shelved Change: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/1.8/core/thread/wakeup_spec.rb;hangs @@ -1,38 +1,7 @@ require File.dirname(__FILE__) + '/../../spec_helper' require File.dirname(__FILE__) + '/fixtures/classes' +require File.dirname(__FILE__) + '/shared/wakeup' describe "Thread#wakeup" do - it "does not result in a deadlock" do - c = Channel.new - t1 = Thread.new do - loop do - c << t1 - c << t1 - Thread.stop - end - end - t2 = Thread.new do - loop do - c << t2 - Thread.stop - end - end - - count1 = 0 - count2 = 0 - while(count1 < 10) do - if c.size > 0 - case c.receive - when t1; count1+=1 - when t2; count2+=1 - end - end - t1.wakeup - t2.wakeup - end - count1.should > count2 - - t1.kill - t2.kill - end + it_behaves_like :thread_wakeup, :wakeup end =================================================================== edit: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/1.8/core/thread/fixtures/classes.rb;C695420 File: classes.rb =================================================================== --- $/Merlin_External/Languages/IronRuby/mspec/rubyspec/1.8/core/thread/fixtures/classes.rb;C695420 (server) 1/5/2009 2:10 PM +++ Shelved Change: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/1.8/core/thread/fixtures/classes.rb;hangs @@ -46,14 +46,18 @@ end end - def self.MainThread1(critical_thread, isThreadStop) - # Thread.stop reset Thread.critical. Also, with native threads, the Thread.Stop may not have executed yet + def self.MainThread1(critical_thread, isThreadSleep, isThreadStop) + # Thread.stop resets Thread.critical. Also, with native threads, the Thread.Stop may not have executed yet # since the main thread will race with the critical thread if not isThreadStop Thread.critical.should == true end critical_thread[:thread_specs] = 101 - critical_thread.wakeup + if isThreadSleep or isThreadStop + # Thread#wakeup calls are not queued up. So we need to ensure that the thread is sleeping before calling wakeup + Thread.pass while critical_thread.status != "sleep" + critical_thread.wakeup + end end def self.MainThread2(critical_thread) @@ -62,7 +66,7 @@ Thread.critical.should == false end - def self.critical_thread_yields_to_main_thread(isThreadStop=false) + def self.critical_thread_yields_to_main_thread(isThreadSleep=false, isThreadStop=false) @@after_first_sleep = false critical_thread = Thread.new do @@ -70,7 +74,7 @@ CriticalThread1() Thread.main.wakeup yield - Thread.pass while @@after_first_sleep != true # Implementations using native thread need to ensure that the next statement does not see the first sleep + Thread.pass while @@after_first_sleep != true # Need to ensure that the next statement does not see the first sleep itself Thread.pass while Thread.main.status != "sleep" CriticalThread2(isThreadStop) Thread.main.wakeup @@ -78,7 +82,7 @@ sleep @@after_first_sleep = true - MainThread1(critical_thread, isThreadStop) + MainThread1(critical_thread, isThreadSleep, isThreadStop) sleep MainThread2(critical_thread) end =================================================================== edit: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/1.8/core/thread/shared/exit.rb;C695420 File: exit.rb =================================================================== --- $/Merlin_External/Languages/IronRuby/mspec/rubyspec/1.8/core/thread/shared/exit.rb;C695420 (server) 1/5/2009 2:45 PM +++ Shelved Change: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/1.8/core/thread/shared/exit.rb;hangs @@ -6,7 +6,7 @@ ScratchPad.record :after_sleep end Thread.pass while sleeping_thread.status != "sleep" - sleeping_thread.kill + sleeping_thread.send(@method) sleeping_thread.join ScratchPad.recorded.should == nil end @@ -14,7 +14,7 @@ it "kills current thread" do ScratchPad.clear() thread = Thread.new do - Thread.current.kill + Thread.current.send(@method) ScratchPad.record :after_sleep end Thread.pass while thread.status != false =================================================================== add: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/1.8/core/thread/shared/wakeup.rb File: wakeup.rb =================================================================== --- [no source file] +++ Shelved Change: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/1.8/core/thread/shared/wakeup.rb;hangs @@ -1,0 +1,50 @@ +describe :thread_wakeup, :shared => true do + it "is not queued" do + exit_loop = false + after_sleep1 = false + after_sleep2 = false + t = Thread.new do + loop do + if exit_loop == true + break + end + end + + sleep + after_sleep1 = true + + sleep + after_sleep2 = true + end + + 10.times { t.send(@method); Thread.pass } # These will all get ignored because the thread is not sleeping yet + + exit_loop = true + + Thread.pass while t.status != "sleep" + after_sleep1.should == false # t should be blocked on the first sleep + t.send(@method) + + Thread.pass while after_sleep1 != true + Thread.pass while t.status != "sleep" + after_sleep2.should == false # t should be blocked on the second sleep + t.send(@method) + Thread.pass while after_sleep2 != true + + t.join + end + + it "does not result in a deadlock" do + c = Channel.new + t = Thread.new do + 1000.times {Thread.stop } + end + + while(t.status != false) do + t.send(@method) + Thread.pass + end + + 1.should == 1 # test succeeds if we reach here + end +end ===================================================================