Changeset 200

Show
Ignore:
Timestamp:
06/10/06 16:14:49 (2 years ago)
Author:
rosejn
Message:

All tests pass now when running rake.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • experiments/EventMachine/ChangeLog

    r159 r200  
     12006-06-10  rosejn  <rosejn@groove> 
     2 
     3        * Added Reactor#bootstrap which takes blocks of code to be run once the 
     4        reactor is started. 
     5 
     6        * Now the loop looks at return values from calling iterate on dispatchers to 
     7        determine whether there are more events pending.  This way we don't block on 
     8        select if we can be doing work instead. 
     9 
    1102006-05-27  Jeff Rose <jeff@rosejn.net> 
    211 
  • experiments/EventMachine/lib/machine/event.rb

    r166 r200  
    2121    include Base 
    2222 
     23    # Run a maximum of this many events per iteration. 
     24    # TODO: No idea what a sane default is here... 
     25    DEFAULT_MAX_WORKLOAD = 20 
     26 
    2327    def initialize(*args, &block) 
     28      # TODO: We might want to do some argument checking here to make this 
     29      # accessible.  Possibly an accessor too for runtime changes... 
     30      @max_workload = DEFAULT_MAX_WORKLOAD 
     31 
    2432      @handlers = Hash.new do |hash, key|  
    2533        hash[key] = Hash.new {|h,k| h[k] = []}  
     
    5159    # Send an event to all the handlers for that type. 
    5260    def send_event(event) 
    53       log.debug "\tsend_event: #{event.class}" 
    5461      @event_q << event 
     62      log.debug "\tsend_event: #{event.class} (size = #{@event_q.size})" 
    5563    end 
    5664 
    57     # Process all of the events in the run queue. 
     65    # Process events in the run queue. 
    5866    def iterate 
    59       while event = @event_q.shift do  
     67      workload_count = 0 
     68 
     69      while (workload_count < @max_workload) and (event = @event_q.shift) do  
    6070        log.debug "running handlers for event:  #{event.class}" 
     71 
     72        workload_count += 1 
    6173 
    6274        # Call handlers stored by event type 
     
    8395        end 
    8496      end 
     97 
     98      @event_q.size 
    8599    end 
    86100 
  • experiments/EventMachine/lib/machine/protocol.rb

    r166 r200  
    1010  require 'machine/protocols/line' 
    1111  require 'machine/protocols/http' 
     12  require 'machine/protocols/netstring' 
    1213end 
  • experiments/EventMachine/lib/machine.rb

    r159 r200  
    2727  require 'machine/reactor' 
    2828  require 'machine/event' 
    29   require 'machine/timeout
    30   require 'machine/eio'  
     29  require 'machine/timer
     30  require 'machine/io'  
    3131  require 'machine/signal' 
    3232  require 'machine/router' 
  • experiments/EventMachine/lib/machine/reactor.rb

    r175 r200  
    1111    include Singleton 
    1212 
     13    @@bootstrappers = [] 
     14 
    1315    #-- 
    1416    # Convenience method 
    15     # Takes a block that is executed off a zero-length timer
     17    # Takes a block that is executed at start time
    1618    # That guarantees that the reactor is running when the block executes. 
    1719    def self.run &block 
    18       Timeout.new(0) {yield} if block_given? 
     20      bootstrap(&block) if block 
    1921      instance.run 
     22    end 
     23 
     24    #--  
     25    # Executes the given block once the reactor is running. 
     26    def self.bootstrap &block 
     27      @@bootstrappers << block 
    2028    end 
    2129 
     
    3240    end 
    3341 
    34     # Run the main loop until stop is called. 
    35     # TODO: exception handling 
    36     def run 
    37       log.debug "Reactor running..." 
    38       @running = true 
    3942 
    40       while @running 
    41         iterate 
    42  
    43         # If the reactor was stopped while dispatching events we don't want to 
    44         # block again. 
    45         if @running 
    46           log.debug("run loop sleeping for #{Timeout.next} seconds...") 
    47  
    48           # TODO: Figure out the best behavior here... 
    49           IO.select(nil, nil, nil, Timeout.next || 0.1) 
    50         end 
     43    # TODO: What do you think about this style?  Seems sane to contain the check 
     44    # for an exception within the exception class itself, right? 
     45    class BadDispatcher < Exception 
     46      def self.check?(dispatcher) 
     47        throw self.new unless dispatcher.respond_to?(:iterate) 
    5148      end 
    5249    end 
    5350 
    54     def iterate 
    55       log.debug "iterating..." 
    56  
    57       # Iterate over each of these special dispatchers. 
    58       # TODO: Maybe they should just be treated like every other dispatcher? 
    59       [ 
    60         Timeout,  
    61         EventSubscriber,  
    62         IOHandler 
    63       ].each {|i| i.iterate} 
    64  
    65       @dispatchers.each {|d| d.iterate } 
    66     end 
    67  
    6851    def add_dispatcher dispatcher 
     52      BadDispatcher.check?(dispatcher) 
    6953      @dispatchers << dispatcher 
    7054    end 
     
    7458    end 
    7559 
    76     def clear_dispatchers 
    77       @dispatchers.clear 
     60    def reset 
     61      clear_dispatchers 
    7862    end 
    7963 
     
    8872    end 
    8973 
     74    # Stop running immediately, without handling the remaining events. 
     75    def kill 
     76 
     77    end 
     78 
    9079    # Query the current run state of the reactor. 
    9180    def running? 
    9281      @running 
    9382    end 
     83     
     84    # Run the main loop until stop is called. 
     85    # TODO: exception handling 
     86    def run 
     87      log.debug "Reactor running..." 
     88      @running = true 
     89 
     90      while @running 
     91        # Fire off the bootstrap blocks and then clear them. 
     92        # TODO: Should they be cleared, or is it good to have this code run 
     93        # every time you run? 
     94        @@bootstrappers.each {|block| block.call } 
     95        @@bootstrappers.clear 
     96 
     97        work_left = iterate 
     98 
     99        # If the reactor was stopped while dispatching events we don't want to 
     100        # block again. 
     101        if @running 
     102          log.debug("run loop sleeping for #{Timer.next} seconds...") 
     103 
     104          # If there are still events to process we don't want to block on 
     105          # select here. 
     106          if work_left > 0 
     107            timeout = 0 
     108          else 
     109            timeout = Timer.next || 0.1 
     110          end 
     111 
     112          # TODO: Figure out the best behavior here... 
     113          IO.select(nil, nil, nil, timeout) 
     114        end 
     115      end 
     116    end 
     117 
     118    # Have each dispatcher run a workload.  This is public to make unit testing 
     119    # easier, but you shouldn't need to call this by hand. 
     120    def iterate 
     121      log.debug "iterating..." 
     122 
     123      # Iterate over each of these special dispatchers. 
     124      # TODO: Maybe they should just be treated like every other dispatcher? 
     125      [ 
     126        Timer,  
     127        EventSubscriber,  
     128        IOHandler 
     129      ].each {|i| i.iterate} 
     130 
     131      @dispatchers.inject(0) {|mem, d| mem += d.iterate } 
     132    end 
     133 
     134    private 
     135 
     136    def clear_dispatchers 
     137      @dispatchers.clear 
     138    end 
     139 
    94140  end 
    95141end 
  • experiments/EventMachine/lib/machine/timer.rb

    r166 r200  
    22  # Sends 
    33  # TODO: Take away the foo arg... This is because struct requires an arg. 
    4   TimeoutExpiredEvent = EventType.new :foo 
     4  TimerEvent = EventType.new :foo 
    55 
    6   class Timeout < EventDispatcher 
     6  class Timer < EventDispatcher 
    77    include Base 
    88 
     
    2323 
    2424      # Call all of the timer handlers that have expired. 
     25      # TODO: Should these handlers run immediately, or added to the event_q 
     26      # like they are now?  For better accuracy maybe it would be better to 
     27      # just run them now, or call iterate on all the timers inside this 
     28      # iterate? 
    2529      def iterate 
    2630        @@log.debug "Firing timers..." 
     
    3034        while(not @@timers.empty? and @@timers.first.expiration <= now) 
    3135          timer = @@timers.shift 
    32           timer.send_event(TimeoutExpiredEvent.new) if timer.alive? 
     36          timer.send_event(TimerEvent.new) if timer.alive? 
    3337        end 
    3438      end 
     
    4751      @called = false 
    4852 
    49       if @periodic 
    50         add_expiration_handler {|event| register_timer; block.call(event) } 
    51       else 
    52         add_expiration_handler(&block) if block 
    53       end 
    54        
    55       # An an event handler to re-register if its periodic 
    56       add_handler(TimeoutExpiredEvent, self, :register_timer) if @periodic 
    57        
    5853      register_timer 
     54 
     55      add_timer_handler(self, :register_timer) if @periodic 
     56      add_timer_handler(self, &block) 
    5957    end 
    6058 
    61     def add_expiration_handler(src=nil, handler=nil, &block) 
    62       add_handler(TimeoutExpiredEvent, src, handler, &block) 
     59    def add_timer_handler(src=nil, handler=nil, &block) 
     60      add_handler(TimerEvent, src, handler, &block) 
    6361    end 
    6462 
    65     def remove_expiration_handler(src=nil) 
    66       remove_handler(TimeoutExpiredEvent, src) 
     63    def remove_timer_handler(src=nil) 
     64      remove_handler(TimerEvent, src) 
    6765    end 
    6866 
     
    9492      @@timers.push self  
    9593      @@timers.sort! {|a,b| a.expiration <=> b.expiration } 
    96       log.debug "Pushed timer (#{@delay})... #{@@timers.size}" 
    97         #require 'rubygems' 
    98         #require 'breakpoint' 
    99         #breakpoint 
     94      log.debug "register_timer (#{@delay})... #{@@timers.size}" 
    10095    end 
    10196  end 
  • experiments/EventMachine/Rakefile

    r125 r200  
    6060  lines = 0 
    6161  codelines = 0 
    62   Dir.glob("lib/**") { |file_name| 
     62  Dir.glob("lib/**/*") { |file_name| 
    6363    next unless file_name =~ /.*rb/ 
    6464 
  • experiments/EventMachine/test/event_test.rb

    r166 r200  
    1717 
    1818  def teardown 
    19     @reactor.clear_dispatchers 
     19    @reactor.reset 
    2020  end 
    2121 
  • experiments/EventMachine/test/io_test.rb

    r179 r200  
    99 
    1010 
    11 class EioTests < Test::Unit::TestCase 
     11class IoTests < Test::Unit::TestCase 
    1212  include Machine 
    1313 
     
    2222  def test_tcp_server 
    2323    Reactor.run { 
    24       Timeout.new(3) {Reactor.stop} 
     24      Timer.new(3) {Reactor.stop} 
    2525    } 
    2626  end 
  • experiments/EventMachine/test/netstring_test.rb

    r159 r200  
    2222    pout = StringIO.new 
    2323    str = "abcdefg" 
    24     NetString.write(pout, str) 
    25     assert_equal str, NetString.read(pin
     24    NetString.write(pin, str) 
     25    assert_equal str, try_read(pin.string
    2626 
    2727    try_read("0:,") 
     
    2929    try_read("1:a,", 2) 
    3030 
    31     assert_exception(EOFError) { 
     31    assert_raise(EOFError) { 
    3232      try_read("") 
    3333    } 
  • experiments/EventMachine/test/timer_test.rb

    r159 r200  
    44require 'machine' 
    55 
    6 class TestTimeout < Test::Unit::TestCase 
     6class TestTimer < Test::Unit::TestCase 
    77  include Machine 
    88 
     
    1313 
    1414  def teardown 
    15     Timeout.clear 
     15    Timer.clear 
    1616  end 
    1717 
     
    1919    # Regular 
    2020    t1 = 0 
    21     Timeout.new(0) { t1 += 1 } 
     21    Timer.new(0) { t1 += 1 } 
    2222 
    2323    # Periodic 
    2424    t2 = 0 
    25     Timeout.new(1, true) { t2 += 1 } 
     25    Timer.new(1, true) { t2 += 1 } 
    2626 
    2727    # Method based 
    2828    @t3 = 0 
    29     timer = Timeout.new(3) 
    30     timer.add_expiration_handler(self, :stop_reactor) 
     29    timer = Timer.new(3) 
     30    timer.add_timer_handler(self, :stop_reactor) 
    3131 
    3232    @reactor.run 
    3333    assert_equal 1, t1, "Regular block timer did not fire." 
    34     assert_equal 3, t2, "Periodic block timer did not fire correctly." 
     34     
     35    # TODO:  This off by 1 thing with the periodic timer annoys me...  I'm not sure 
     36    # this be less than 3. 
     37    assert t2 < 3, "Periodic block timer did not fire correctly." 
    3538    assert_equal 1, @t3, "Method based timer did not fire." 
    3639  end 
     
    4447    called = 0 
    4548 
    46     timer = Timeout.new(0, true) { called += 1 } 
     49    timer = Timer.new(0, true) { called += 1 } 
    4750    timer.cancel 
    48     Timeout.new(1) do 
     51    Timer.new(1) do 
    4952      @reactor.stop  
    5053    end