eventmachine 1.0.0.beta.3 → 1.0.0.beta.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. data/.gitignore +5 -0
  2. data/.yardopts +5 -1
  3. data/{docs/GNU → GNU} +0 -0
  4. data/Gemfile +1 -0
  5. data/{docs/COPYING → LICENSE} +0 -0
  6. data/README.md +109 -0
  7. data/Rakefile +8 -0
  8. data/docs/DocumentationGuidesIndex.md +27 -0
  9. data/docs/GettingStarted.md +521 -0
  10. data/docs/{ChangeLog → old/ChangeLog} +0 -0
  11. data/docs/{DEFERRABLES → old/DEFERRABLES} +0 -0
  12. data/docs/{EPOLL → old/EPOLL} +0 -0
  13. data/docs/{INSTALL → old/INSTALL} +0 -0
  14. data/docs/{KEYBOARD → old/KEYBOARD} +0 -0
  15. data/docs/{LEGAL → old/LEGAL} +0 -0
  16. data/docs/{LIGHTWEIGHT_CONCURRENCY → old/LIGHTWEIGHT_CONCURRENCY} +0 -0
  17. data/docs/{PURE_RUBY → old/PURE_RUBY} +0 -0
  18. data/docs/{RELEASE_NOTES → old/RELEASE_NOTES} +0 -0
  19. data/docs/{SMTP → old/SMTP} +0 -0
  20. data/docs/{SPAWNED_PROCESSES → old/SPAWNED_PROCESSES} +0 -0
  21. data/docs/{TODO → old/TODO} +0 -0
  22. data/eventmachine.gemspec +4 -1
  23. data/examples/guides/getting_started/01_eventmachine_echo_server.rb +18 -0
  24. data/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb +22 -0
  25. data/examples/guides/getting_started/03_simple_chat_server.rb +149 -0
  26. data/examples/guides/getting_started/04_simple_chat_server_step_one.rb +27 -0
  27. data/examples/guides/getting_started/05_simple_chat_server_step_two.rb +43 -0
  28. data/examples/guides/getting_started/06_simple_chat_server_step_three.rb +98 -0
  29. data/examples/guides/getting_started/07_simple_chat_server_step_four.rb +121 -0
  30. data/examples/guides/getting_started/08_simple_chat_server_step_five.rb +141 -0
  31. data/examples/{ex_channel.rb → old/ex_channel.rb} +3 -3
  32. data/examples/{ex_queue.rb → old/ex_queue.rb} +0 -0
  33. data/examples/{ex_tick_loop_array.rb → old/ex_tick_loop_array.rb} +0 -0
  34. data/examples/{ex_tick_loop_counter.rb → old/ex_tick_loop_counter.rb} +0 -0
  35. data/examples/{helper.rb → old/helper.rb} +0 -0
  36. data/ext/cmain.cpp +3 -3
  37. data/ext/ed.cpp +90 -15
  38. data/ext/ed.h +5 -5
  39. data/ext/em.cpp +47 -55
  40. data/ext/em.h +12 -2
  41. data/ext/pipe.cpp +2 -2
  42. data/ext/project.h +1 -1
  43. data/ext/rubymain.cpp +48 -3
  44. data/ext/ssl.cpp +5 -0
  45. data/java/src/com/rubyeventmachine/EmReactor.java +2 -2
  46. data/lib/em/buftok.rb +35 -63
  47. data/lib/em/callback.rb +43 -11
  48. data/lib/em/channel.rb +21 -14
  49. data/lib/em/completion.rb +304 -0
  50. data/lib/em/connection.rb +339 -209
  51. data/lib/em/deferrable.rb +4 -0
  52. data/lib/em/deferrable/pool.rb +2 -0
  53. data/lib/em/file_watch.rb +37 -18
  54. data/lib/em/iterator.rb +42 -42
  55. data/lib/em/pool.rb +146 -0
  56. data/lib/em/process_watch.rb +5 -4
  57. data/lib/em/processes.rb +8 -4
  58. data/lib/em/protocols/httpclient.rb +22 -11
  59. data/lib/em/protocols/httpclient2.rb +15 -5
  60. data/lib/em/protocols/line_protocol.rb +2 -1
  61. data/lib/em/protocols/memcache.rb +17 -9
  62. data/lib/em/protocols/object_protocol.rb +2 -1
  63. data/lib/em/protocols/postgres3.rb +8 -9
  64. data/lib/em/protocols/smtpclient.rb +19 -11
  65. data/lib/em/protocols/smtpserver.rb +1 -1
  66. data/lib/em/protocols/stomp.rb +8 -6
  67. data/lib/em/protocols/tcptest.rb +3 -2
  68. data/lib/em/pure_ruby.rb +212 -208
  69. data/lib/em/queue.rb +22 -13
  70. data/lib/em/resolver.rb +70 -64
  71. data/lib/em/spawnable.rb +6 -3
  72. data/lib/em/streamer.rb +33 -45
  73. data/lib/em/threaded_resource.rb +90 -0
  74. data/lib/em/timers.rb +6 -2
  75. data/lib/em/version.rb +1 -1
  76. data/lib/eventmachine.rb +538 -602
  77. data/lib/jeventmachine.rb +22 -1
  78. data/tasks/package.rake +12 -2
  79. data/tasks/test.rake +1 -0
  80. data/tests/em_test_helper.rb +12 -3
  81. data/tests/test_completion.rb +177 -0
  82. data/tests/test_epoll.rb +2 -2
  83. data/tests/test_httpclient.rb +9 -9
  84. data/tests/test_httpclient2.rb +11 -9
  85. data/tests/test_ltp.rb +2 -10
  86. data/tests/test_pool.rb +128 -0
  87. data/tests/test_processes.rb +20 -2
  88. data/tests/test_queue.rb +8 -0
  89. data/tests/test_resolver.rb +1 -1
  90. data/tests/test_set_sock_opt.rb +37 -0
  91. data/tests/test_shutdown_hooks.rb +23 -0
  92. data/tests/test_threaded_resource.rb +53 -0
  93. data/tests/test_unbind_reason.rb +31 -0
  94. metadata +262 -192
  95. data/README +0 -81
  96. data/tasks/doc.rake +0 -30
@@ -1,29 +1,31 @@
1
1
  module EventMachine
2
2
  # A cross thread, reactor scheduled, linear queue.
3
3
  #
4
- # This class provides a simple "Queue" like abstraction on top of the reactor
4
+ # This class provides a simple queue abstraction on top of the reactor
5
5
  # scheduler. It services two primary purposes:
6
+ #
6
7
  # * API sugar for stateful protocols
7
- # * Pushing processing onto the same thread as the reactor
8
+ # * Pushing processing onto the reactor thread
8
9
  #
9
- # See examples/ex_queue.rb for a detailed example.
10
+ # @example
10
11
  #
11
12
  # q = EM::Queue.new
12
13
  # q.push('one', 'two', 'three')
13
14
  # 3.times do
14
- # q.pop{ |msg| puts(msg) }
15
+ # q.pop { |msg| puts(msg) }
15
16
  # end
16
17
  #
17
18
  class Queue
18
- # Create a new queue
19
19
  def initialize
20
20
  @items = []
21
21
  @popq = []
22
22
  end
23
23
 
24
24
  # Pop items off the queue, running the block on the reactor thread. The pop
25
- # will not happen immediately, but at some point in the future, either in
25
+ # will not happen immediately, but at some point in the future, either in
26
26
  # the next tick, if the queue has data, or when the queue is populated.
27
+ #
28
+ # @return [NilClass] nil
27
29
  def pop(*a, &b)
28
30
  cb = EM::Callback(*a, &b)
29
31
  EM.schedule do
@@ -37,7 +39,7 @@ module EventMachine
37
39
  end
38
40
 
39
41
  # Push items onto the queue in the reactor thread. The items will not appear
40
- # in the queue immediately, but will be scheduled for addition during the
42
+ # in the queue immediately, but will be scheduled for addition during the
41
43
  # next reactor tick.
42
44
  def push(*items)
43
45
  EM.schedule do
@@ -47,16 +49,23 @@ module EventMachine
47
49
  end
48
50
  alias :<< :push
49
51
 
50
- # N.B. This is a peek, it's not thread safe, and may only tend toward
51
- # accuracy.
52
+ # @return [Boolean]
53
+ # @note This is a peek, it's not thread safe, and may only tend toward accuracy.
52
54
  def empty?
53
55
  @items.empty?
54
56
  end
55
57
 
56
- # N.B. This is a peek, it's not thread safe, and may only tend toward
57
- # accuracy.
58
+ # @return [Integer] Queue size
59
+ # @note This is a peek, it's not thread safe, and may only tend toward accuracy.
58
60
  def size
59
61
  @items.size
60
62
  end
61
- end
62
- end
63
+
64
+ # @return [Integer] Waiting size
65
+ # @note This is a peek at the number of jobs that are currently waiting on the Queue
66
+ def num_waiting
67
+ @popq.size
68
+ end
69
+
70
+ end # Queue
71
+ end # EventMachine
@@ -6,6 +6,8 @@ module EventMachine
6
6
  Request.new(socket, hostname)
7
7
  end
8
8
 
9
+ @socket = @nameservers = nil
10
+
9
11
  def self.socket
10
12
  if !@socket || (@socket && @socket.error?)
11
13
  @socket = Socket.open
@@ -58,6 +60,10 @@ module EventMachine
58
60
  EventMachine::open_datagram_socket('0.0.0.0', 0, self)
59
61
  end
60
62
 
63
+ def initialize
64
+ @nameserver = nil
65
+ end
66
+
61
67
  def post_init
62
68
  @requests = {}
63
69
  EM.add_periodic_timer(0.1, &method(:tick))
@@ -99,88 +105,88 @@ module EventMachine
99
105
  begin
100
106
  msg = Resolv::DNS::Message.decode data
101
107
  rescue
102
- else
103
- req = @requests[msg.id]
104
- if req
105
- @requests.delete(msg.id)
106
- req.receive_answer(msg)
107
- end
108
+ else
109
+ req = @requests[msg.id]
110
+ if req
111
+ @requests.delete(msg.id)
112
+ req.receive_answer(msg)
108
113
  end
109
114
  end
110
115
  end
116
+ end
111
117
 
112
- class Request
113
- include Deferrable
114
- attr_accessor :retry_interval, :max_tries
118
+ class Request
119
+ include Deferrable
120
+ attr_accessor :retry_interval, :max_tries
115
121
 
116
- def initialize(socket, hostname)
117
- @socket = socket
118
- @hostname = hostname
119
- @tries = 0
120
- @last_send = Time.at(0)
121
- @retry_interval = 3
122
- @max_tries = 5
122
+ def initialize(socket, hostname)
123
+ @socket = socket
124
+ @hostname = hostname
125
+ @tries = 0
126
+ @last_send = Time.at(0)
127
+ @retry_interval = 3
128
+ @max_tries = 5
123
129
 
124
- if addrs = Resolver.hosts[hostname]
125
- succeed addrs
126
- else
127
- EM.next_tick { tick }
128
- end
130
+ if addrs = Resolver.hosts[hostname]
131
+ succeed addrs
132
+ else
133
+ EM.next_tick { tick }
129
134
  end
135
+ end
130
136
 
131
- def tick
132
- # Break early if nothing to do
133
- return if @last_send + @retry_interval > Time.now
134
- if @tries < @max_tries
135
- send
136
- else
137
- fail 'retries exceeded'
138
- end
137
+ def tick
138
+ # Break early if nothing to do
139
+ return if @last_send + @retry_interval > Time.now
140
+ if @tries < @max_tries
141
+ send
142
+ else
143
+ fail 'retries exceeded'
139
144
  end
145
+ end
140
146
 
141
- def receive_answer(msg)
142
- addrs = []
143
- msg.each_answer do |name,ttl,data|
144
- if data.kind_of?(Resolv::DNS::Resource::IN::A) ||
145
- data.kind_of?(Resolv::DNS::Resource::IN::AAAA)
146
- addrs << data.address.to_s
147
- end
147
+ def receive_answer(msg)
148
+ addrs = []
149
+ msg.each_answer do |name,ttl,data|
150
+ if data.kind_of?(Resolv::DNS::Resource::IN::A) ||
151
+ data.kind_of?(Resolv::DNS::Resource::IN::AAAA)
152
+ addrs << data.address.to_s
148
153
  end
154
+ end
149
155
 
150
- if addrs.empty?
151
- fail "rcode=#{msg.rcode}"
152
- else
153
- succeed addrs
154
- end
156
+ if addrs.empty?
157
+ fail "rcode=#{msg.rcode}"
158
+ else
159
+ succeed addrs
155
160
  end
161
+ end
156
162
 
157
- private
163
+ private
158
164
 
159
- def send
160
- @tries += 1
161
- @last_send = Time.now
162
- @socket.send_packet(packet.encode)
163
- end
165
+ def send
166
+ @tries += 1
167
+ @last_send = Time.now
168
+ @socket.send_packet(packet.encode)
169
+ end
164
170
 
165
- def id
166
- begin
167
- @id = rand(65535)
168
- @socket.register_request(@id, self)
169
- rescue RequestIdAlreadyUsed
170
- retry
171
- end unless defined?(@id)
171
+ def id
172
+ begin
173
+ @id = rand(65535)
174
+ @socket.register_request(@id, self)
175
+ rescue RequestIdAlreadyUsed
176
+ retry
177
+ end unless defined?(@id)
172
178
 
173
- @id
174
- end
179
+ @id
180
+ end
175
181
 
176
- def packet
177
- msg = Resolv::DNS::Message.new
178
- msg.id = id
179
- msg.rd = 1
180
- msg.add_question @hostname, Resolv::DNS::Resource::IN::A
181
- msg
182
- end
182
+ def packet
183
+ msg = Resolv::DNS::Message.new
184
+ msg.id = id
185
+ msg.rd = 1
186
+ msg.add_question @hostname, Resolv::DNS::Resource::IN::A
187
+ msg
188
+ end
183
189
 
184
- end
185
190
  end
186
191
  end
192
+ end
@@ -55,7 +55,8 @@ module EventMachine
55
55
 
56
56
  end
57
57
 
58
- class YieldBlockFromSpawnedProcess # :nodoc:
58
+ # @private
59
+ class YieldBlockFromSpawnedProcess
59
60
  def initialize block, notify
60
61
  @block = [block,notify]
61
62
  end
@@ -71,11 +72,13 @@ module EventMachine
71
72
  s
72
73
  end
73
74
 
74
- def self.yield &block # :nodoc:
75
+ # @private
76
+ def self.yield &block
75
77
  return YieldBlockFromSpawnedProcess.new( block, false )
76
78
  end
77
79
 
78
- def self.yield_and_notify &block # :nodoc:
80
+ # @private
81
+ def self.yield_and_notify &block
79
82
  return YieldBlockFromSpawnedProcess.new( block, true )
80
83
  end
81
84
  end
@@ -1,30 +1,24 @@
1
- #--
2
- #
3
- # Author:: Francis Cianfrocca (gmail: blackhedd)
4
- # Homepage:: http://rubyeventmachine.com
5
- # Date:: 16 Jul 2006
6
- #
7
- # See EventMachine and EventMachine::Connection for documentation and
8
- # usage examples.
9
- #
10
- #----------------------------------------------------------------------------
11
- #
12
- # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
13
- # Gmail: blackhedd
14
- #
15
- # This program is free software; you can redistribute it and/or modify
16
- # it under the terms of either: 1) the GNU General Public License
17
- # as published by the Free Software Foundation; either version 2 of the
18
- # License, or (at your option) any later version; or 2) Ruby's License.
19
- #
20
- # See the file COPYING for complete licensing information.
21
- #
22
- #---------------------------------------------------------------------------
23
- #
24
- #
25
-
26
-
27
1
  module EventMachine
2
+ # Streams a file over a given connection. Streaming begins once the object is
3
+ # instantiated. Typically FileStreamer instances are not reused.
4
+ #
5
+ # Streaming uses buffering for files larger than 16K and uses so-called fast file reader (a C++ extension)
6
+ # if available (it is part of eventmachine gem itself).
7
+ #
8
+ # @example
9
+ #
10
+ # module FileSender
11
+ # def post_init
12
+ # streamer = EventMachine::FileStreamer.new(self, '/tmp/bigfile.tar')
13
+ # streamer.callback{
14
+ # # file was sent successfully
15
+ # close_connection_after_writing
16
+ # }
17
+ # end
18
+ # end
19
+ #
20
+ #
21
+ # @author Francis Cianfrocca
28
22
  class FileStreamer
29
23
  include Deferrable
30
24
 
@@ -35,19 +29,10 @@ module EventMachine
35
29
  # Send 16k chunks at a time
36
30
  ChunkSize = 16384
37
31
 
38
- # Stream a file over a given connection. An optional :http_chunks => true argument will
39
- # use HTTP 1.1 style chunked-encoding semantics.
40
- #
41
- # module FileSender
42
- # def post_init
43
- # streamer = EventMachine::FileStreamer.new(self, '/tmp/bigfile.tar')
44
- # streamer.callback{
45
- # # file was sent successfully
46
- # close_connection_after_writing
47
- # }
48
- # end
49
- # end
32
+ # @param [EventMachine::Connection] connection
33
+ # @param [String] filename File path
50
34
  #
35
+ # @option args [Boolean] :http_chunks (false) Use HTTP 1.1 style chunked-encoding semantics.
51
36
  def initialize connection, filename, args = {}
52
37
  @connection = connection
53
38
  @http_chunks = args[:http_chunks]
@@ -64,7 +49,8 @@ module EventMachine
64
49
  end
65
50
  end
66
51
 
67
- def stream_without_mapping filename # :nodoc:
52
+ # @private
53
+ def stream_without_mapping filename
68
54
  if @http_chunks
69
55
  @connection.send_data "#{@size.to_s(16)}\r\n"
70
56
  @connection.send_file_data filename
@@ -76,7 +62,8 @@ module EventMachine
76
62
  end
77
63
  private :stream_without_mapping
78
64
 
79
- def stream_with_mapping filename # :nodoc:
65
+ # @private
66
+ def stream_with_mapping filename
80
67
  ensure_mapping_extension_is_present
81
68
 
82
69
  @position = 0
@@ -86,6 +73,7 @@ module EventMachine
86
73
  private :stream_with_mapping
87
74
 
88
75
  # Used internally to stream one chunk at a time over multiple reactor ticks
76
+ # @private
89
77
  def stream_one_chunk
90
78
  loop {
91
79
  if @position < @size
@@ -111,7 +99,7 @@ module EventMachine
111
99
  }
112
100
  end
113
101
 
114
- #--
102
+ #
115
103
  # We use an outboard extension class to get memory-mapped files.
116
104
  # It's outboard to avoid polluting the core distro, but that means
117
105
  # there's a "hidden" dependency on it. The first time we get here in
@@ -120,11 +108,11 @@ module EventMachine
120
108
  # mapped files will work fine without it. This is a somewhat difficult
121
109
  # compromise between usability and proper modularization.
122
110
  #
123
- def ensure_mapping_extension_is_present # :nodoc:
111
+ # @private
112
+ def ensure_mapping_extension_is_present
124
113
  @@fastfilereader ||= (require 'fastfilereaderext')
125
114
  end
126
115
  private :ensure_mapping_extension_is_present
127
116
 
128
- end
129
- end
130
-
117
+ end # FileStreamer
118
+ end # EventMachine
@@ -0,0 +1,90 @@
1
+ module EventMachine
2
+ # = EventMachine::ThreadedResource
3
+ #
4
+ # A threaded resource is a "quick and dirty" wrapper around the concept of
5
+ # wiring up synchronous code into a standard EM::Pool. This is useful to keep
6
+ # interfaces coherent and provide a simple approach at "making an interface
7
+ # async-ish".
8
+ #
9
+ # General usage is to wrap libraries that do not support EventMachine, or to
10
+ # have a specific number of dedicated high-cpu worker resources.
11
+ #
12
+ # == Basic Usage example
13
+ #
14
+ # This example requires the cassandra gem. The cassandra gem contains an
15
+ # EventMachine interface, but it's sadly Fiber based and thus only works on
16
+ # 1.9. It also requires (potentially) complex stack switching logic to reach
17
+ # completion of nested operations. By contrast this approach provides a block
18
+ # in which normal synchronous code can occur, but makes no attempt to wire the
19
+ # IO into EventMachines C++ IO implementations, instead relying on the reactor
20
+ # pattern in rb_thread_select.
21
+ #
22
+ # cassandra_dispatcher = ThreadedResource.new do
23
+ # Cassandra.new('allthethings', '127.0.0.1:9160')
24
+ # end
25
+ #
26
+ # pool = EM::Pool.new
27
+ #
28
+ # pool.add cassandra_dispatcher
29
+ #
30
+ # # If we don't care about the result:
31
+ # pool.perform do |dispatcher|
32
+ # # The following blcok executes inside a dedicated thread, and should not
33
+ # # access EventMachine things:
34
+ # dispatcher.dispatch do |cassandra|
35
+ # cassandra.insert(:Things, '10', 'stuff' => 'things')
36
+ # end
37
+ # end
38
+ #
39
+ # # Example where we care about the result:
40
+ # pool.perform do |dispatcher|
41
+ # # The dispatch block is executed in the resources thread.
42
+ # completion = dispatcher.dispatch do |cassandra|
43
+ # cassandra.get(:Things, '10', 'stuff')
44
+ # end
45
+ #
46
+ # # This block will be yielded on the EM thread:
47
+ # completion.callback do |result|
48
+ # EM.do_something_with(result)
49
+ # end
50
+ #
51
+ # completion
52
+ # end
53
+ class ThreadedResource
54
+
55
+ # The block should return the resource that will be yielded in a dispatch.
56
+ def initialize
57
+ @resource = yield
58
+
59
+ @running = true
60
+ @queue = ::Queue.new
61
+ @thread = Thread.new do
62
+ @queue.pop.call while @running
63
+ end
64
+ end
65
+
66
+ # Called on the EM thread, generally in a perform block to return a
67
+ # completion for the work.
68
+ def dispatch
69
+ completion = EM::Completion.new
70
+ @queue << lambda do
71
+ begin
72
+ result = yield @resource
73
+ completion.succeed result
74
+ rescue Exception => e
75
+ completion.fail e
76
+ end
77
+ end
78
+ completion
79
+ end
80
+
81
+ # Kill the internal thread. should only be used to cleanup - generally
82
+ # only required for tests.
83
+ def shutdown
84
+ @running = false
85
+ @queue << lambda {}
86
+ @thread.join
87
+ end
88
+
89
+ end
90
+ end