You're going to like this one :)

The following program:


----

require "thread"                                
                                
N=4                    
                                                             
def sitter(n)                            
  a = 0                                
  while TRUE                                  
    if (a.remainder (5000) == 0)                                    
      printf ("thread %d at %d\n", n, a)         
    end                                              
  a += 1                                                  
  end  
end    

def sleeper    
  while TRUE
    sleep (0.01)
  end          
end

for i in 0..N-1  
  Thread.start{sitter(i)}            
end                                                   

Thread.start{sleeper}      
    
sleep                                    
    

-----

unexpectedly produces:

thread 0 at 0
thread 0 at 5000
thread 0 at 10000
thread 1 at 0
thread 1 at 5000
thread 1 at 10000
thread 0 at 15000
thread 2 at 0
thread 2 at 5000
thread 2 at 10000
thread 1 at 15000
thread 1 at 20000
thread 0 at 20000
thread 0 at 25000
thread 0 at 30000
thread 3 at 0
thread 3 at 5000
thread 3 at 10000
thread 2 at 15000
thread 2 at 20000
thread 1 at 25000
thread 1 at 30000
thread 0 at 35000
thread 0 at 40000
thread 3 at 15000
thread 3 at 20000
thread 3 at 25000
thread 3 at 30000
thread 3 at 35000
thread 3 at 40000
...

(continuing on thread 3 for the rest of the programs life)

I noticed this because the sleep () call is essentially what the gtk
bindings are using, and you can't run more than one extra thread for them
either.

I think the reason is in rb_thread_schedule ():

...

if (num_waiting_on_timer > 0) {
  now = timeofday();
  FOREACH_THREAD_FROM(curr, th) {
    if (th->wait_for & WAIT_TIME) {
      if (th->delay <= now) {
        th->delay = 0.0;
	th->wait_for &= ~WAIT_TIME;
	th->status = THREAD_RUNNABLE;
	num_waiting_on_timer--;
	next = th;
      } else if (th->delay < delay) {
	delay = th->delay;
      }
    }
  }
  END_FOREACH_FROM(curr, th);
}

...
					    
with the constant calling to sleep, it goes through this every call to
rb_thread_schedule ().  The thread that was sleeping, wakes up probably just
about every run through this function, so its set as the current thread.  It
then loops about, goes to sleep again, and somewhere in there the next
thread is run (thread 3 in our example), but then this one wakes up again
before any others have a chance to run, and the scheduling is reset.


I dunno what the perfect ruby-ish solution is so I'll leave that to you folk.


Ian