eventmachine-mkroman 1.3.0.dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +179 -0
  3. data/GNU +281 -0
  4. data/LICENSE +60 -0
  5. data/README.md +110 -0
  6. data/docs/DocumentationGuidesIndex.md +27 -0
  7. data/docs/GettingStarted.md +520 -0
  8. data/docs/old/ChangeLog +211 -0
  9. data/docs/old/DEFERRABLES +246 -0
  10. data/docs/old/EPOLL +141 -0
  11. data/docs/old/INSTALL +13 -0
  12. data/docs/old/KEYBOARD +42 -0
  13. data/docs/old/LEGAL +25 -0
  14. data/docs/old/LIGHTWEIGHT_CONCURRENCY +130 -0
  15. data/docs/old/PURE_RUBY +75 -0
  16. data/docs/old/RELEASE_NOTES +94 -0
  17. data/docs/old/SMTP +4 -0
  18. data/docs/old/SPAWNED_PROCESSES +148 -0
  19. data/docs/old/TODO +8 -0
  20. data/examples/guides/getting_started/01_eventmachine_echo_server.rb +18 -0
  21. data/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb +22 -0
  22. data/examples/guides/getting_started/03_simple_chat_server.rb +149 -0
  23. data/examples/guides/getting_started/04_simple_chat_server_step_one.rb +27 -0
  24. data/examples/guides/getting_started/05_simple_chat_server_step_two.rb +43 -0
  25. data/examples/guides/getting_started/06_simple_chat_server_step_three.rb +98 -0
  26. data/examples/guides/getting_started/07_simple_chat_server_step_four.rb +121 -0
  27. data/examples/guides/getting_started/08_simple_chat_server_step_five.rb +141 -0
  28. data/examples/old/ex_channel.rb +43 -0
  29. data/examples/old/ex_queue.rb +2 -0
  30. data/examples/old/ex_tick_loop_array.rb +15 -0
  31. data/examples/old/ex_tick_loop_counter.rb +32 -0
  32. data/examples/old/helper.rb +2 -0
  33. data/ext/binder.cpp +124 -0
  34. data/ext/binder.h +52 -0
  35. data/ext/cmain.cpp +1046 -0
  36. data/ext/ed.cpp +2243 -0
  37. data/ext/ed.h +463 -0
  38. data/ext/em.cpp +2378 -0
  39. data/ext/em.h +266 -0
  40. data/ext/eventmachine.h +152 -0
  41. data/ext/extconf.rb +291 -0
  42. data/ext/fastfilereader/extconf.rb +120 -0
  43. data/ext/fastfilereader/mapper.cpp +214 -0
  44. data/ext/fastfilereader/mapper.h +59 -0
  45. data/ext/fastfilereader/rubymain.cpp +126 -0
  46. data/ext/kb.cpp +79 -0
  47. data/ext/page.cpp +107 -0
  48. data/ext/page.h +51 -0
  49. data/ext/pipe.cpp +354 -0
  50. data/ext/project.h +174 -0
  51. data/ext/rubymain.cpp +1643 -0
  52. data/ext/ssl.cpp +701 -0
  53. data/ext/ssl.h +103 -0
  54. data/ext/wait_for_single_fd.h +36 -0
  55. data/java/.classpath +8 -0
  56. data/java/.project +17 -0
  57. data/java/src/com/rubyeventmachine/EmReactor.java +625 -0
  58. data/java/src/com/rubyeventmachine/EmReactorException.java +40 -0
  59. data/java/src/com/rubyeventmachine/EmReactorInterface.java +70 -0
  60. data/java/src/com/rubyeventmachine/EventableChannel.java +72 -0
  61. data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +201 -0
  62. data/java/src/com/rubyeventmachine/EventableSocketChannel.java +415 -0
  63. data/java/src/com/rubyeventmachine/NullEmReactor.java +157 -0
  64. data/java/src/com/rubyeventmachine/NullEventableChannel.java +81 -0
  65. data/lib/em/buftok.rb +59 -0
  66. data/lib/em/callback.rb +58 -0
  67. data/lib/em/channel.rb +69 -0
  68. data/lib/em/completion.rb +307 -0
  69. data/lib/em/connection.rb +802 -0
  70. data/lib/em/deferrable/pool.rb +2 -0
  71. data/lib/em/deferrable.rb +210 -0
  72. data/lib/em/file_watch.rb +73 -0
  73. data/lib/em/future.rb +61 -0
  74. data/lib/em/io_streamer.rb +68 -0
  75. data/lib/em/iterator.rb +252 -0
  76. data/lib/em/messages.rb +66 -0
  77. data/lib/em/pool.rb +151 -0
  78. data/lib/em/process_watch.rb +45 -0
  79. data/lib/em/processes.rb +123 -0
  80. data/lib/em/protocols/header_and_content.rb +138 -0
  81. data/lib/em/protocols/httpclient.rb +303 -0
  82. data/lib/em/protocols/httpclient2.rb +602 -0
  83. data/lib/em/protocols/line_and_text.rb +125 -0
  84. data/lib/em/protocols/line_protocol.rb +33 -0
  85. data/lib/em/protocols/linetext2.rb +179 -0
  86. data/lib/em/protocols/memcache.rb +331 -0
  87. data/lib/em/protocols/object_protocol.rb +46 -0
  88. data/lib/em/protocols/postgres3.rb +246 -0
  89. data/lib/em/protocols/saslauth.rb +175 -0
  90. data/lib/em/protocols/smtpclient.rb +394 -0
  91. data/lib/em/protocols/smtpserver.rb +666 -0
  92. data/lib/em/protocols/socks4.rb +66 -0
  93. data/lib/em/protocols/stomp.rb +205 -0
  94. data/lib/em/protocols/tcptest.rb +54 -0
  95. data/lib/em/protocols.rb +37 -0
  96. data/lib/em/pure_ruby.rb +1300 -0
  97. data/lib/em/queue.rb +80 -0
  98. data/lib/em/resolver.rb +232 -0
  99. data/lib/em/spawnable.rb +84 -0
  100. data/lib/em/streamer.rb +118 -0
  101. data/lib/em/threaded_resource.rb +90 -0
  102. data/lib/em/tick_loop.rb +85 -0
  103. data/lib/em/timers.rb +61 -0
  104. data/lib/em/version.rb +3 -0
  105. data/lib/eventmachine.rb +1602 -0
  106. data/lib/jeventmachine.rb +319 -0
  107. data/rakelib/package.rake +120 -0
  108. data/rakelib/test.rake +6 -0
  109. data/rakelib/test_pure.rake +11 -0
  110. data/tests/client.crt +31 -0
  111. data/tests/client.key +51 -0
  112. data/tests/dhparam.pem +13 -0
  113. data/tests/em_ssl_handlers.rb +165 -0
  114. data/tests/em_test_helper.rb +198 -0
  115. data/tests/encoded_client.key +54 -0
  116. data/tests/jruby/test_jeventmachine.rb +38 -0
  117. data/tests/test_attach.rb +199 -0
  118. data/tests/test_basic.rb +321 -0
  119. data/tests/test_channel.rb +75 -0
  120. data/tests/test_completion.rb +178 -0
  121. data/tests/test_connection_count.rb +83 -0
  122. data/tests/test_connection_write.rb +35 -0
  123. data/tests/test_defer.rb +35 -0
  124. data/tests/test_deferrable.rb +35 -0
  125. data/tests/test_epoll.rb +141 -0
  126. data/tests/test_error_handler.rb +38 -0
  127. data/tests/test_exc.rb +37 -0
  128. data/tests/test_file_watch.rb +86 -0
  129. data/tests/test_fork.rb +75 -0
  130. data/tests/test_futures.rb +170 -0
  131. data/tests/test_handler_check.rb +35 -0
  132. data/tests/test_hc.rb +155 -0
  133. data/tests/test_httpclient.rb +238 -0
  134. data/tests/test_httpclient2.rb +132 -0
  135. data/tests/test_idle_connection.rb +31 -0
  136. data/tests/test_inactivity_timeout.rb +102 -0
  137. data/tests/test_io_streamer.rb +48 -0
  138. data/tests/test_ipv4.rb +96 -0
  139. data/tests/test_ipv6.rb +107 -0
  140. data/tests/test_iterator.rb +122 -0
  141. data/tests/test_kb.rb +28 -0
  142. data/tests/test_keepalive.rb +113 -0
  143. data/tests/test_line_protocol.rb +33 -0
  144. data/tests/test_ltp.rb +155 -0
  145. data/tests/test_ltp2.rb +332 -0
  146. data/tests/test_many_fds.rb +21 -0
  147. data/tests/test_next_tick.rb +104 -0
  148. data/tests/test_object_protocol.rb +36 -0
  149. data/tests/test_pause.rb +109 -0
  150. data/tests/test_pending_connect_timeout.rb +52 -0
  151. data/tests/test_pool.rb +196 -0
  152. data/tests/test_process_watch.rb +50 -0
  153. data/tests/test_processes.rb +147 -0
  154. data/tests/test_proxy_connection.rb +180 -0
  155. data/tests/test_pure.rb +156 -0
  156. data/tests/test_queue.rb +64 -0
  157. data/tests/test_resolver.rb +129 -0
  158. data/tests/test_running.rb +14 -0
  159. data/tests/test_sasl.rb +46 -0
  160. data/tests/test_send_file.rb +217 -0
  161. data/tests/test_servers.rb +32 -0
  162. data/tests/test_shutdown_hooks.rb +23 -0
  163. data/tests/test_smtpclient.rb +75 -0
  164. data/tests/test_smtpserver.rb +90 -0
  165. data/tests/test_sock_opt.rb +53 -0
  166. data/tests/test_spawn.rb +290 -0
  167. data/tests/test_ssl_args.rb +70 -0
  168. data/tests/test_ssl_dhparam.rb +57 -0
  169. data/tests/test_ssl_ecdh_curve.rb +57 -0
  170. data/tests/test_ssl_extensions.rb +24 -0
  171. data/tests/test_ssl_inline_cert.rb +222 -0
  172. data/tests/test_ssl_methods.rb +31 -0
  173. data/tests/test_ssl_protocols.rb +190 -0
  174. data/tests/test_ssl_verify.rb +108 -0
  175. data/tests/test_stomp.rb +38 -0
  176. data/tests/test_system.rb +46 -0
  177. data/tests/test_threaded_resource.rb +68 -0
  178. data/tests/test_tick_loop.rb +58 -0
  179. data/tests/test_timers.rb +150 -0
  180. data/tests/test_ud.rb +8 -0
  181. data/tests/test_unbind_reason.rb +40 -0
  182. metadata +389 -0
data/lib/em/queue.rb ADDED
@@ -0,0 +1,80 @@
1
+ module EventMachine
2
+ # A cross thread, reactor scheduled, linear queue.
3
+ #
4
+ # This class provides a simple queue abstraction on top of the reactor
5
+ # scheduler. It services two primary purposes:
6
+ #
7
+ # * API sugar for stateful protocols
8
+ # * Pushing processing onto the reactor thread
9
+ #
10
+ # @example
11
+ #
12
+ # q = EM::Queue.new
13
+ # q.push('one', 'two', 'three')
14
+ # 3.times do
15
+ # q.pop { |msg| puts(msg) }
16
+ # end
17
+ #
18
+ class Queue
19
+ def initialize
20
+ @sink = []
21
+ @drain = []
22
+ @popq = []
23
+ end
24
+
25
+ # Pop items off the queue, running the block on the reactor thread. The pop
26
+ # will not happen immediately, but at some point in the future, either in
27
+ # the next tick, if the queue has data, or when the queue is populated.
28
+ #
29
+ # @return [NilClass] nil
30
+ def pop(*a, &b)
31
+ cb = EM::Callback(*a, &b)
32
+ EM.schedule do
33
+ if @drain.empty?
34
+ @drain = @sink
35
+ @sink = []
36
+ end
37
+ if @drain.empty?
38
+ @popq << cb
39
+ else
40
+ cb.call @drain.shift
41
+ end
42
+ end
43
+ nil # Always returns nil
44
+ end
45
+
46
+ # Push items onto the queue in the reactor thread. The items will not appear
47
+ # in the queue immediately, but will be scheduled for addition during the
48
+ # next reactor tick.
49
+ def push(*items)
50
+ EM.schedule do
51
+ @sink.push(*items)
52
+ unless @popq.empty?
53
+ @drain = @sink
54
+ @sink = []
55
+ @popq.shift.call @drain.shift until @drain.empty? || @popq.empty?
56
+ end
57
+ end
58
+ end
59
+ alias :<< :push
60
+
61
+ # @return [Boolean]
62
+ # @note This is a peek, it's not thread safe, and may only tend toward accuracy.
63
+ def empty?
64
+ @drain.empty? && @sink.empty?
65
+ end
66
+
67
+ # @return [Integer] Queue size
68
+ # @note This is a peek, it's not thread safe, and may only tend toward accuracy.
69
+ def size
70
+ @drain.size + @sink.size
71
+ end
72
+
73
+ # @return [Integer] Waiting size
74
+ # @note This is a peek at the number of jobs that are currently waiting on the Queue
75
+ def num_waiting
76
+ @popq.size
77
+ end
78
+
79
+ end # Queue
80
+ end # EventMachine
@@ -0,0 +1,232 @@
1
+ module EventMachine
2
+ module DNS
3
+ class Resolver
4
+
5
+ def self.windows?
6
+ if RUBY_PLATFORM =~ /mswin32|cygwin|mingw|bccwin/
7
+ require 'win32/resolv'
8
+ true
9
+ else
10
+ false
11
+ end
12
+ end
13
+
14
+ HOSTS_FILE = windows? ? Win32::Resolv.get_hosts_path : '/etc/hosts'
15
+
16
+ @hosts = nil
17
+ @nameservers = nil
18
+ @socket = nil
19
+
20
+ def self.resolve(hostname)
21
+ Request.new(socket, hostname)
22
+ end
23
+
24
+ def self.socket
25
+ if @socket && @socket.error?
26
+ @socket = Socket.open
27
+ else
28
+ @socket ||= Socket.open
29
+ end
30
+ end
31
+
32
+ def self.nameservers=(ns)
33
+ @nameservers = ns
34
+ end
35
+
36
+ def self.nameservers
37
+ return @nameservers if @nameservers
38
+
39
+ if windows?
40
+ _, ns = Win32::Resolv.get_resolv_info
41
+ return @nameservers = ns || []
42
+ end
43
+
44
+ @nameservers = []
45
+ IO.readlines('/etc/resolv.conf').each do |line|
46
+ if line =~ /^nameserver (.+)$/
47
+ @nameservers << $1.split(/\s+/).first
48
+ end
49
+ end
50
+
51
+ @nameservers
52
+ rescue
53
+ @nameservers = []
54
+ end
55
+
56
+ def self.nameserver
57
+ nameservers.shuffle.first
58
+ end
59
+
60
+ def self.hosts
61
+ return @hosts if @hosts
62
+
63
+ @hosts = {}
64
+ IO.readlines(HOSTS_FILE).each do |line|
65
+ next if line =~ /^#/
66
+ addr, host = line.split(/\s+/)
67
+
68
+ next unless addr && host
69
+ @hosts[host] ||= []
70
+ @hosts[host] << addr
71
+ end
72
+
73
+ @hosts
74
+ rescue
75
+ @hosts = {}
76
+ end
77
+ end
78
+
79
+ class RequestIdAlreadyUsed < RuntimeError; end
80
+
81
+ class Socket < EventMachine::Connection
82
+ def self.open
83
+ EventMachine::open_datagram_socket('0.0.0.0', 0, self)
84
+ end
85
+
86
+ def initialize
87
+ @nameserver = nil
88
+ end
89
+
90
+ def post_init
91
+ @requests = {}
92
+ end
93
+
94
+ def start_timer
95
+ @timer ||= EM.add_periodic_timer(0.1, &method(:tick))
96
+ end
97
+
98
+ def stop_timer
99
+ EM.cancel_timer(@timer)
100
+ @timer = nil
101
+ end
102
+
103
+ def unbind
104
+ end
105
+
106
+ def tick
107
+ @requests.each do |id,req|
108
+ req.tick
109
+ end
110
+ end
111
+
112
+ def register_request(id, req)
113
+ if @requests.has_key?(id)
114
+ raise RequestIdAlreadyUsed
115
+ else
116
+ @requests[id] = req
117
+ end
118
+
119
+ start_timer
120
+ end
121
+
122
+ def deregister_request(id, req)
123
+ @requests.delete(id)
124
+ stop_timer if @requests.length == 0
125
+ end
126
+
127
+ def send_packet(pkt)
128
+ send_datagram(pkt, nameserver, 53)
129
+ end
130
+
131
+ def nameserver=(ns)
132
+ @nameserver = ns
133
+ end
134
+
135
+ def nameserver
136
+ @nameserver || Resolver.nameserver
137
+ end
138
+
139
+ # Decodes the packet, looks for the request and passes the
140
+ # response over to the requester
141
+ def receive_data(data)
142
+ msg = nil
143
+ begin
144
+ msg = Resolv::DNS::Message.decode data
145
+ rescue
146
+ else
147
+ req = @requests[msg.id]
148
+ if req
149
+ @requests.delete(msg.id)
150
+ stop_timer if @requests.length == 0
151
+ req.receive_answer(msg)
152
+ end
153
+ end
154
+ end
155
+ end
156
+
157
+ class Request
158
+ include Deferrable
159
+ attr_accessor :retry_interval, :max_tries
160
+
161
+ def initialize(socket, hostname)
162
+ @socket = socket
163
+ @hostname = hostname
164
+ @tries = 0
165
+ @last_send = Time.at(0)
166
+ @retry_interval = 3
167
+ @max_tries = 5
168
+
169
+ if addrs = Resolver.hosts[hostname]
170
+ succeed addrs
171
+ else
172
+ EM.next_tick { tick }
173
+ end
174
+ end
175
+
176
+ def tick
177
+ # Break early if nothing to do
178
+ return if @last_send + @retry_interval > Time.now
179
+ if @tries < @max_tries
180
+ send
181
+ else
182
+ @socket.deregister_request(@id, self)
183
+ fail 'retries exceeded'
184
+ end
185
+ end
186
+
187
+ def receive_answer(msg)
188
+ addrs = []
189
+ msg.each_answer do |name,ttl,data|
190
+ if data.kind_of?(Resolv::DNS::Resource::IN::A) ||
191
+ data.kind_of?(Resolv::DNS::Resource::IN::AAAA)
192
+ addrs << data.address.to_s
193
+ end
194
+ end
195
+
196
+ if addrs.empty?
197
+ fail "rcode=#{msg.rcode}"
198
+ else
199
+ succeed addrs
200
+ end
201
+ end
202
+
203
+ private
204
+
205
+ def send
206
+ @tries += 1
207
+ @last_send = Time.now
208
+ @socket.send_packet(packet.encode)
209
+ end
210
+
211
+ def id
212
+ begin
213
+ @id = rand(65535)
214
+ @socket.register_request(@id, self)
215
+ rescue RequestIdAlreadyUsed
216
+ retry
217
+ end unless defined?(@id)
218
+
219
+ @id
220
+ end
221
+
222
+ def packet
223
+ msg = Resolv::DNS::Message.new
224
+ msg.id = id
225
+ msg.rd = 1
226
+ msg.add_question @hostname, Resolv::DNS::Resource::IN::A
227
+ msg
228
+ end
229
+
230
+ end
231
+ end
232
+ end
@@ -0,0 +1,84 @@
1
+ #--
2
+ #
3
+ # Author:: Francis Cianfrocca (gmail: blackhedd)
4
+ # Homepage:: http://rubyeventmachine.com
5
+ # Date:: 25 Aug 2007
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
+ module EventMachine
27
+ # Support for Erlang-style processes.
28
+ #
29
+ class SpawnedProcess
30
+ # Send a message to the spawned process
31
+ def notify *x
32
+ me = self
33
+ EM.next_tick {
34
+ # A notification executes in the context of this
35
+ # SpawnedProcess object. That makes self and notify
36
+ # work as one would expect.
37
+ #
38
+ y = me.call(*x)
39
+ if y and y.respond_to?(:pull_out_yield_block)
40
+ a,b = y.pull_out_yield_block
41
+ set_receiver a
42
+ self.notify if b
43
+ end
44
+ }
45
+ end
46
+ alias_method :resume, :notify
47
+ alias_method :run, :notify # for formulations like (EM.spawn {xxx}).run
48
+
49
+ def set_receiver blk
50
+ (class << self ; self ; end).class_eval do
51
+ remove_method :call if method_defined? :call
52
+ define_method :call, blk
53
+ end
54
+ end
55
+
56
+ end
57
+
58
+ # @private
59
+ class YieldBlockFromSpawnedProcess
60
+ def initialize block, notify
61
+ @block = [block,notify]
62
+ end
63
+ def pull_out_yield_block
64
+ @block
65
+ end
66
+ end
67
+
68
+ # Spawn an erlang-style process
69
+ def self.spawn &block
70
+ s = SpawnedProcess.new
71
+ s.set_receiver block
72
+ s
73
+ end
74
+
75
+ # @private
76
+ def self.yield &block
77
+ return YieldBlockFromSpawnedProcess.new( block, false )
78
+ end
79
+
80
+ # @private
81
+ def self.yield_and_notify &block
82
+ return YieldBlockFromSpawnedProcess.new( block, true )
83
+ end
84
+ end
@@ -0,0 +1,118 @@
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
22
+ class FileStreamer
23
+ include Deferrable
24
+
25
+ # Use mapped streamer for files bigger than 16k
26
+ MappingThreshold = 16384
27
+ # Wait until next tick to send more data when 50k is still in the outgoing buffer
28
+ BackpressureLevel = 50000
29
+ # Send 16k chunks at a time
30
+ ChunkSize = 16384
31
+
32
+ # @param [EventMachine::Connection] connection
33
+ # @param [String] filename File path
34
+ #
35
+ # @option args [Boolean] :http_chunks (false) Use HTTP 1.1 style chunked-encoding semantics.
36
+ def initialize connection, filename, args = {}
37
+ @connection = connection
38
+ @http_chunks = args[:http_chunks]
39
+
40
+ if File.exist?(filename)
41
+ @size = File.size(filename)
42
+ if @size <= MappingThreshold
43
+ stream_without_mapping filename
44
+ else
45
+ stream_with_mapping filename
46
+ end
47
+ else
48
+ fail "file not found"
49
+ end
50
+ end
51
+
52
+ # @private
53
+ def stream_without_mapping filename
54
+ if @http_chunks
55
+ @connection.send_data "#{@size.to_s(16)}\r\n"
56
+ @connection.send_file_data filename
57
+ @connection.send_data "\r\n0\r\n\r\n"
58
+ else
59
+ @connection.send_file_data filename
60
+ end
61
+ succeed
62
+ end
63
+ private :stream_without_mapping
64
+
65
+ # @private
66
+ def stream_with_mapping filename
67
+ ensure_mapping_extension_is_present
68
+
69
+ @position = 0
70
+ @mapping = EventMachine::FastFileReader::Mapper.new filename
71
+ stream_one_chunk
72
+ end
73
+ private :stream_with_mapping
74
+
75
+ # Used internally to stream one chunk at a time over multiple reactor ticks
76
+ # @private
77
+ def stream_one_chunk
78
+ loop {
79
+ if @position < @size
80
+ if @connection.get_outbound_data_size > BackpressureLevel
81
+ EventMachine::next_tick {stream_one_chunk}
82
+ break
83
+ else
84
+ len = @size - @position
85
+ len = ChunkSize if (len > ChunkSize)
86
+
87
+ @connection.send_data( "#{len.to_s(16)}\r\n" ) if @http_chunks
88
+ @connection.send_data( @mapping.get_chunk( @position, len ))
89
+ @connection.send_data("\r\n") if @http_chunks
90
+
91
+ @position += len
92
+ end
93
+ else
94
+ @connection.send_data "0\r\n\r\n" if @http_chunks
95
+ @mapping.close
96
+ succeed
97
+ break
98
+ end
99
+ }
100
+ end
101
+
102
+ #
103
+ # We use an outboard extension class to get memory-mapped files.
104
+ # It's outboard to avoid polluting the core distro, but that means
105
+ # there's a "hidden" dependency on it. The first time we get here in
106
+ # any run, try to load up the dependency extension. User code will see
107
+ # a LoadError if it's not available, but code that doesn't require
108
+ # mapped files will work fine without it. This is a somewhat difficult
109
+ # compromise between usability and proper modularization.
110
+ #
111
+ # @private
112
+ def ensure_mapping_extension_is_present
113
+ @@fastfilereader ||= (require 'fastfilereaderext')
114
+ end
115
+ private :ensure_mapping_extension_is_present
116
+
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 block 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 => 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
@@ -0,0 +1,85 @@
1
+ module EventMachine
2
+ # Creates and immediately starts an EventMachine::TickLoop
3
+ def self.tick_loop(*a, &b)
4
+ TickLoop.new(*a, &b).start
5
+ end
6
+
7
+ # A TickLoop is useful when one needs to distribute amounts of work
8
+ # throughout ticks in order to maintain response times. It is also useful for
9
+ # simple repeated checks and metrics.
10
+ # @example
11
+ # # Here we run through an array one item per tick until it is empty,
12
+ # # printing each element.
13
+ # # When the array is empty, we return :stop from the callback, and the
14
+ # # loop will terminate.
15
+ # # When the loop terminates, the on_stop callbacks will be called.
16
+ # EM.run do
17
+ # array = (1..100).to_a
18
+ #
19
+ # tickloop = EM.tick_loop do
20
+ # if array.empty?
21
+ # :stop
22
+ # else
23
+ # puts array.shift
24
+ # end
25
+ # end
26
+ #
27
+ # tickloop.on_stop { EM.stop }
28
+ # end
29
+ #
30
+ class TickLoop
31
+
32
+ # Arguments: A callback (EM::Callback) to call each tick. If the call
33
+ # returns +:stop+ then the loop will be stopped. Any other value is
34
+ # ignored.
35
+ def initialize(*a, &b)
36
+ @work = EM::Callback(*a, &b)
37
+ @stops = []
38
+ @stopped = true
39
+ end
40
+
41
+ # Arguments: A callback (EM::Callback) to call once on the next stop (or
42
+ # immediately if already stopped).
43
+ def on_stop(*a, &b)
44
+ if @stopped
45
+ EM::Callback(*a, &b).call
46
+ else
47
+ @stops << EM::Callback(*a, &b)
48
+ end
49
+ end
50
+
51
+ # Stop the tick loop immediately, and call it's on_stop callbacks.
52
+ def stop
53
+ @stopped = true
54
+ until @stops.empty?
55
+ @stops.shift.call
56
+ end
57
+ end
58
+
59
+ # Query if the loop is stopped.
60
+ def stopped?
61
+ @stopped
62
+ end
63
+
64
+ # Start the tick loop, will raise argument error if the loop is already
65
+ # running.
66
+ def start
67
+ raise ArgumentError, "double start" unless @stopped
68
+ @stopped = false
69
+ schedule
70
+ end
71
+
72
+ private
73
+ def schedule
74
+ EM.next_tick do
75
+ next if @stopped
76
+ if @work.call == :stop
77
+ stop
78
+ else
79
+ schedule
80
+ end
81
+ end
82
+ self
83
+ end
84
+ end
85
+ end