careo / fiber-examples

Stupid tricks with fibers

This URL has Read+Write access

fiber-examples / rpc / future_rpc.rb
100644 81 lines (67 sloc) 1.845 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
$:.unshift "#{File.dirname(__FILE__)}/../"
require 'require_me'
 
# Shamelessly stolen and (poorly) adapted from mentalguy's lazy.rb lib
class Future
  alias __class__ class
  alias __send__ send
  instance_methods.each { |m| undef_method m unless m =~ /^__/ }
 
  def initialize &blk
    @f = Fiber.new {
 
      @d = blk.call
      if @d.kind_of? EM::DefaultDeferrable
        @d.callback { |val|
         @f.resume val
        }
        @d.errback { |val|
         @f.resume val
        }
 
      else
        @result = d
      end
      @result = Fiber.yield
    }
    @f.resume
    self
  end
 
  def method_missing *args, &block
    # if we're not quite done yet... then resume the fiber.
    if @f.alive?
      f = Fiber.current
      @d.callback { |val|
        f.resume val
      }
      @result = Fiber.yield
      return @result.send(*args, &block)
    else
      @result
    end
  end
 
end
 
# nice simple asynchronous rpc. takes something to ask for (and eventually gives it back)
# and return a deferrable in case you want to do something else with the result.
def rpc val, &blk
  d = EventMachine::DefaultDeferrable.new
 
  EventMachine.add_timer(1) {
    d.succeed(val)
  }
 
  d
end
 
EventMachine.run {
  start_time = Time.now
  
  # Need to wrap this in a fiber so I can block the whole thing
  # in case the value of a future is demanded.
  Fiber.new {
    start = Time.now
    one = Future.new { rpc(1) }
    two = Future.new { rpc(2) }
 
    puts "at #{Time.now - start}, about to use futures"
    result = one + two
    puts "finished with futures at #{Time.now - start}"
    puts result
  }.resume
  
  # ensure we eventually do quit
  EventMachine.add_timer(5) {
    puts "Total Runtime: #{Time.now - start_time}"
    EventMachine.stop
  }
   
}