em-synchrony 0.3.0.beta.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ require 'active_record'
2
+ require 'em-synchrony/mysql2'
3
+
4
+ require 'em-synchrony/active_record/connection_adapters/em_mysql2_adapter'
5
+ require 'em-synchrony/active_record/patches'
@@ -48,7 +48,7 @@ module EventMachine
48
48
  def release(fiber)
49
49
  @available.push(@reserved.delete(fiber.object_id))
50
50
 
51
- if pending = @pending.pop
51
+ if pending = @pending.shift
52
52
  pending.resume
53
53
  end
54
54
  end
@@ -62,11 +62,11 @@ module EventMachine
62
62
  # once it is complete (assumption: fiber will yield until
63
63
  # data is available, or request is complete)
64
64
  #
65
- def method_missing(method, *args)
65
+ def method_missing(method, *args, &blk)
66
66
  async = (method[0,1] == "a")
67
67
 
68
68
  execute(async) do |conn|
69
- df = conn.send(method, *args)
69
+ df = conn.send(method, *args, &blk)
70
70
 
71
71
  if async
72
72
  fiber = Fiber.current
@@ -0,0 +1,24 @@
1
+ begin
2
+ require 'em-hiredis'
3
+ rescue LoadError => error
4
+ raise 'Missing EM-Synchrony dependency: gem install em-hiredis'
5
+ end
6
+
7
+ module EventMachine
8
+ module Hiredis
9
+ class Connection
10
+ attr_reader :connected
11
+
12
+ def self.connect(host = 'localhost', port = 6379)
13
+ conn = new(host, port)
14
+ EM::Synchrony.sync conn
15
+ conn
16
+ end
17
+
18
+ alias :old_method_missing :method_missing
19
+ def method_missing(sym, *args)
20
+ EM::Synchrony.sync old_method_missing(sym, *args)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,5 +1,5 @@
1
1
  begin
2
- require "em-http"
2
+ require "em-http-request"
3
3
  rescue LoadError => error
4
4
  raise "Missing EM-Synchrony dependency: gem install em-http-request"
5
5
  end
@@ -12,11 +12,15 @@ module EventMachine
12
12
  def #{type}(options = {}, &blk)
13
13
  f = Fiber.current
14
14
 
15
- conn = setup_request(:#{type}, options, &blk)
16
- conn.callback { f.resume(conn) }
17
- conn.errback { f.resume(conn) }
18
-
19
- Fiber.yield
15
+ conn = setup_request(:#{type}, options, &blk)
16
+ if conn.error.nil?
17
+ conn.callback { f.resume(conn) }
18
+ conn.errback { f.resume(conn) }
19
+
20
+ Fiber.yield
21
+ else
22
+ conn
23
+ end
20
24
  end
21
25
  ]
22
26
  end
@@ -22,24 +22,91 @@ module EM
22
22
 
23
23
  class Collection
24
24
 
25
- alias :afind :find
26
- def find(selector={}, opts={})
25
+ #
26
+ # The upcoming versions of EM-Mongo change Collection#find's interface: it
27
+ # now returns a deferrable cursor YAY. This breaks compatibility with past
28
+ # versions BOO. We'll just choose based on the presence/absence of
29
+ # EM::Mongo::Cursor YAY
30
+ #
27
31
 
28
- f = Fiber.current
29
- cb = proc { |res| f.resume(res) }
32
+ #
33
+ # em-mongo version > 0.3.6
34
+ #
35
+ if defined?(EM::Mongo::Cursor)
30
36
 
31
- skip = opts.delete(:skip) || 0
32
- limit = opts.delete(:limit) || 0
37
+ # afind is the old (async) find
38
+ # afind_one is rewritten to call afind
39
+ # find is sync, using a callback on the cursor
40
+ # find_one is sync, by calling find and taking the first element.
41
+ # first is sync, an alias for find_one
33
42
 
34
- @connection.find(@name, skip, limit, selector, nil, &cb)
35
- Fiber.yield
36
- end
43
+ alias :afind :find
44
+ def find(*args)
45
+ f = Fiber.current
46
+ cursor = afind(*args)
47
+ cursor.to_a.callback{ |res| f.resume(res) }
48
+ Fiber.yield
49
+ end
50
+
51
+ # need to rewrite afind_one manually, as it calls 'find' (reasonably
52
+ # expecting it to be what is now known as 'afind')
53
+
54
+ def afind_one(spec_or_object_id=nil, opts={})
55
+ spec = case spec_or_object_id
56
+ when nil
57
+ {}
58
+ when BSON::ObjectId
59
+ {:_id => spec_or_object_id}
60
+ when Hash
61
+ spec_or_object_id
62
+ else
63
+ raise TypeError, "spec_or_object_id must be an instance of ObjectId or Hash, or nil"
64
+ end
65
+ afind(spec, opts.merge(:limit => -1)).next_document
66
+ end
67
+ alias :afirst :afind_one
68
+
69
+ def find_one(selector={}, opts={})
70
+ opts[:limit] = 1
71
+ find(selector, opts).first
72
+ end
73
+ alias :first :find_one
74
+
75
+ #
76
+ # em-mongo version <= 0.3.6
77
+ #
78
+ else
37
79
 
38
- alias :afirst :first
39
- def first(selector={}, opts={})
40
- opts[:limit] = 1
41
- find(selector, opts).first
80
+ alias :afind :find
81
+ def find(selector={}, opts={})
82
+
83
+ f = Fiber.current
84
+ cb = proc { |res| f.resume(res) }
85
+
86
+ skip = opts.delete(:skip) || 0
87
+ limit = opts.delete(:limit) || 0
88
+ order = opts.delete(:order)
89
+
90
+ @connection.find(@name, skip, limit, order, selector, nil, &cb)
91
+ Fiber.yield
92
+ end
93
+
94
+ # need to rewrite afirst manually, as it calls 'find' (reasonably
95
+ # expecting it to be what is now known as 'afind')
96
+
97
+ def afirst(selector={}, opts={}, &blk)
98
+ opts[:limit] = 1
99
+ afind(selector, opts) do |res|
100
+ yield res.first
101
+ end
102
+ end
103
+
104
+ def first(selector={}, opts={})
105
+ opts[:limit] = 1
106
+ find(selector, opts).first
107
+ end
42
108
  end
109
+
43
110
  end
44
111
 
45
112
  end
@@ -21,13 +21,13 @@ module EventMachine
21
21
 
22
22
  Fiber.yield
23
23
  end
24
-
24
+
25
25
  alias :old_call_command :call_command
26
-
26
+
27
+ SYNC = ['add', 'auth']
27
28
  def call_command(argv, &blk)
28
- # async commands are 'a' prefixed, but do check
29
- # for the 'add' command corner case (ugh)
30
- if argv.first.size > 3 && argv.first[0] == 'a'
29
+ # async commands are 'a' prefixed
30
+ if (argv.first[0] == 'a') && !SYNC.include?(argv.first.to_s)
31
31
  argv[0] = argv[0].to_s.slice(1,argv[0].size)
32
32
  old_call_command(argv, &blk)
33
33
 
@@ -42,6 +42,29 @@ module EventMachine
42
42
  Fiber.yield
43
43
  end
44
44
  end
45
+
46
+ # adapted from em-redis' implementation to use
47
+ # the asynchronous version of mget
48
+ def amapped_mget(*keys)
49
+ self.amget(*keys) do |response|
50
+ result = {}
51
+ response.each do |value|
52
+ key = keys.shift
53
+ result.merge!(key => value) unless value.nil?
54
+ end
55
+ yield result if block_given?
56
+ end
57
+ end
58
+
59
+ def mapped_mget(*keys)
60
+ f = Fiber.current
61
+
62
+ self.amapped_mget(*keys) do |values|
63
+ f.resume(values)
64
+ end
65
+
66
+ Fiber.yield
67
+ end
45
68
  end
46
69
  end
47
- end
70
+ end
@@ -11,14 +11,18 @@ module Memcached
11
11
  Memcached.servers = servers
12
12
 
13
13
  f = Fiber.current
14
+ @w = EM::Timer.new(10.0) { f.resume :error }
14
15
  @t = EM::PeriodicTimer.new(0.01) do
15
16
  if Memcached.usable?
17
+ @w.cancel
16
18
  @t.cancel
17
19
  f.resume(self)
18
20
  end
19
21
  end
20
22
 
21
- Fiber.yield
23
+ r = Fiber.yield
24
+
25
+ (r == :error) ? (raise Exception.new('Cannot connect to memcached server')) : r
22
26
  end
23
27
 
24
28
  %w[add get set delete].each do |type|
@@ -30,42 +34,62 @@ module Memcached
30
34
  cb = Proc.new { |res| df.succeed(res) }
31
35
  operation Request::#{type.capitalize}, contents, &cb
32
36
 
33
- df
37
+ df
34
38
  end
35
39
 
36
40
  def #{type}(contents, &callback)
37
41
  fiber = Fiber.current
42
+ paused = false
43
+
44
+ cb = Proc.new do |res|
45
+ if paused
46
+ fiber.resume(res)
47
+ else
48
+ return res
49
+ end
50
+ end
38
51
 
39
- df = a#{type}(contents, &Proc.new { |res| fiber.resume(res) })
52
+ df = a#{type}(contents, &cb)
40
53
  df.callback &callback
41
54
 
55
+ paused = true
42
56
  Fiber.yield
43
57
  end
44
58
  ]
45
59
  end
46
60
 
47
61
  %w[add get set delete].each do |type|
48
- class_eval %[
49
- def amulti_#{type}(contents, &callback)
50
- df = EventMachine::DefaultDeferrable.new
51
- df.callback &callback
62
+ class_eval %[
63
+ def amulti_#{type}(contents, &callback)
64
+ df = EventMachine::DefaultDeferrable.new
65
+ df.callback &callback
66
+
67
+ cb = Proc.new { |res| df.succeed(res) }
68
+ multi_operation Request::#{type.capitalize}, contents, &cb
52
69
 
53
- cb = Proc.new { |res| df.succeed(res) }
54
- multi_operation Request::#{type.capitalize}, contents, &cb
70
+ df
71
+ end
55
72
 
56
- df
57
- end
73
+ def multi_#{type}(contents, &callback)
74
+ fiber = Fiber.current
75
+ paused = false
58
76
 
59
- def multi_#{type}(contents, &callback)
60
- fiber = Fiber.current
77
+ cb = Proc.new do |res|
78
+ if paused
79
+ fiber.resume(res)
80
+ else
81
+ return res
82
+ end
83
+ end
61
84
 
62
- df = amulti_#{type}(contents, &Proc.new { |res| fiber.resume(res) })
63
- df.callback &callback
85
+ df = amulti_#{type}(contents, &cb)
86
+ df.callback &callback
64
87
 
65
- Fiber.yield
66
- end
67
- ]
68
- end
88
+ paused = true
89
+ Fiber.yield
90
+ end
91
+ ]
92
+ end
69
93
 
70
94
  end
71
95
  end
@@ -0,0 +1,18 @@
1
+ module EventMachine
2
+ module Synchrony
3
+
4
+ class FiberIterator < EM::Synchrony::Iterator
5
+
6
+ # execute each iterator block within its own fiber
7
+ # and auto-advance the iterator after each call
8
+ def each(foreach=nil, after=nil, &blk)
9
+ fe = Proc.new do |obj, iter|
10
+ Fiber.new { (foreach || blk).call(obj); iter.next }.resume
11
+ end
12
+
13
+ super(fe, after)
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,33 @@
1
+ module EventMachine
2
+ module Synchrony
3
+ class Keyboard
4
+ attr_reader :current_fiber, :separator
5
+
6
+ def gets
7
+ @current_fiber = Fiber.current
8
+ EM.open_keyboard(EventMachine::Synchrony::KeyboardHandler, self)
9
+
10
+ Fiber.yield
11
+ end
12
+ end
13
+
14
+ class KeyboardHandler < EM::Connection
15
+ include EM::Protocols::LineText2
16
+
17
+ def initialize(keyboard)
18
+ @keyboard = keyboard
19
+ end
20
+
21
+ def receive_line(line)
22
+ # Simulate gets by adding a trailing line feed
23
+ @input = "#{line}#{$/}"
24
+
25
+ close_connection
26
+ end
27
+
28
+ def unbind
29
+ @keyboard.current_fiber.resume @input
30
+ end
31
+ end
32
+ end
33
+ end
@@ -11,4 +11,22 @@ silence_warnings do
11
11
  Mutex = ::EventMachine::Synchrony::Thread::Mutex
12
12
  ConditionVariable = ::EventMachine::Synchrony::Thread::ConditionVariable
13
13
  end
14
- end
14
+
15
+ class Mongo::Pool
16
+ TCPSocket = ::EventMachine::Synchrony::TCPSocket
17
+ Mutex = ::EventMachine::Synchrony::Thread::Mutex
18
+ ConditionVariable = ::EventMachine::Synchrony::Thread::ConditionVariable
19
+ end
20
+
21
+ class EventMachine::Synchrony::MongoTimeoutHandler
22
+ def self.timeout(op_timeout, ex_class, &block)
23
+ f = Fiber.current
24
+ timer = EM::Timer.new(op_timeout) { f.resume(nil) }
25
+ res = block.call
26
+ timer.cancel
27
+ res
28
+ end
29
+ end
30
+
31
+ Mongo::TimeoutHandler = EventMachine::Synchrony::MongoTimeoutHandler
32
+ end
@@ -0,0 +1,25 @@
1
+ begin
2
+ require 'mysql2/em'
3
+ rescue LoadError => error
4
+ raise 'Missing EM-Synchrony dependency: gem install mysql2'
5
+ end
6
+
7
+ module Mysql2
8
+ module EM
9
+ class Client
10
+
11
+ alias :aquery :query
12
+ def query(sql, opts={})
13
+ deferable = aquery(sql, opts)
14
+
15
+ f = Fiber.current
16
+ deferable.callback { |res| f.resume(res) }
17
+ deferable.errback { |err| f.resume(err) }
18
+
19
+ Fiber.yield.tap do |result|
20
+ raise result if result.is_a?(::Exception)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end