Programming generators with threads

Hi, in Rubytalk 55895 [
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/55895 ] Matz
wrote that generators can be written with Threads as well as with
Callcc. He then gave an example with Calcc. Works fine. Here is my
example with Threads. But I’m not sure whether it is good programming.
Is it free of race conditions, for example? Thanks.

module Math
def Math.divisible(number)
square_root = Math.sqrt(number).floor
for divisor in 2…square_root
return true if number % divisor==0
end
return false
end
end

class PrimeGenerator

def initialize
@thread = Thread.new { # create a separate thread
loop do # this thread is an infinite loop…

        Thread.stop # going to sleep, goodnight everybody

        # Uh oh ... Brrr.  Where am I?
        # I have been woken up and scheduled to run.
        # See (*)

        Thread.critical = true # take exclusive right to run
        # this right is necessary, because I must be kept awake
        # by scheduler until next prime is computed
        complex_computation # do something interesting..
        Thread.critical = false # release exclusive right
     end
  }
  @number = 1

end

def next
@thread.run
return @number
end

def complex_computation
@number += 1
@number += 1 while Math.divisible(@number)
end

end

p = PrimeGenerator.new
puts p.next #> 2
puts p.next #> 3
puts p.next #> 5
puts p.next #> 7

Hi, in Rubytalk 55895 [
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/55895 ] Matz
wrote that generators can be written with Threads as well as with
Callcc. He then gave an example with Calcc. Works fine. Here is my
example with Threads. But I’m not sure whether it is good programming.
Is it free of race conditions, for example? Thanks.

I tend to prefer using callcc for generators, because there are fewer
messy issues with garbage collection (ideally the thread should be
killed if the generator gets garbage collected).

class PrimeGenerator

def initialize
@thread = Thread.new { # create a separate thread

Probably a good idea to use Thread.current.abort_on_exception=true here,
in case an exception escapes from this thread.

Ideally, if an exception does escape, it should be passed from this
thread to the thread that called next(). That’s exactly what I did in:
RubyCollections download | SourceForge.net

     loop do # this thread is an infinite loop..

        Thread.stop # going to sleep, goodnight everybody

        # Uh oh ... Brrr.  Where am I?
        # I have been woken up and scheduled to run.
        # See (*)

        Thread.critical = true # take exclusive right to run

It’s a good idea to use Thread.exclusive instead of Thread.critical
here, in case complex_computation raises an exception:

Thread.exclusive do
complex_computation
end

The disadvantage of using Thread.critical/exclusive is that no other
threads can run. If complex_computation takes a long time to run, then
this is probably not good.

        # this right is necessary, because I must be kept awake
        # by scheduler until next prime is computed
        complex_computation # do something interesting..
        Thread.critical = false # release exclusive right
     end
  }
  @number = 1

end

def next
@thread.run
return @number

I think it’s possible (though unlikely) for return @number to occur
before complex_computation finishes. You probably want to use
Thread.stop right after @thread.run, and allow the thread to wake us up
to let us know the next number is ready to be returned.

end

def complex_computation
@number += 1
@number += 1 while Math.divisible(@number)
end

end

Paul

···

On Tue, Jun 03, 2003 at 01:06:48AM +0900, Gerard A.W. Vreeswijk wrote: