sensu-em 2.0.0-java
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.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.travis.yml +12 -0
- data/.yardopts +7 -0
- data/CHANGELOG.md +33 -0
- data/GNU +281 -0
- data/Gemfile +2 -0
- data/LICENSE +60 -0
- data/README.md +109 -0
- data/Rakefile +20 -0
- data/docs/DocumentationGuidesIndex.md +27 -0
- data/docs/GettingStarted.md +521 -0
- data/docs/old/ChangeLog +211 -0
- data/docs/old/DEFERRABLES +246 -0
- data/docs/old/EPOLL +141 -0
- data/docs/old/INSTALL +13 -0
- data/docs/old/KEYBOARD +42 -0
- data/docs/old/LEGAL +25 -0
- data/docs/old/LIGHTWEIGHT_CONCURRENCY +130 -0
- data/docs/old/PURE_RUBY +75 -0
- data/docs/old/RELEASE_NOTES +94 -0
- data/docs/old/SMTP +4 -0
- data/docs/old/SPAWNED_PROCESSES +148 -0
- data/docs/old/TODO +8 -0
- data/eventmachine.gemspec +38 -0
- data/examples/guides/getting_started/01_eventmachine_echo_server.rb +18 -0
- data/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb +22 -0
- data/examples/guides/getting_started/03_simple_chat_server.rb +149 -0
- data/examples/guides/getting_started/04_simple_chat_server_step_one.rb +27 -0
- data/examples/guides/getting_started/05_simple_chat_server_step_two.rb +43 -0
- data/examples/guides/getting_started/06_simple_chat_server_step_three.rb +98 -0
- data/examples/guides/getting_started/07_simple_chat_server_step_four.rb +121 -0
- data/examples/guides/getting_started/08_simple_chat_server_step_five.rb +141 -0
- data/examples/old/ex_channel.rb +43 -0
- data/examples/old/ex_queue.rb +2 -0
- data/examples/old/ex_tick_loop_array.rb +15 -0
- data/examples/old/ex_tick_loop_counter.rb +32 -0
- data/examples/old/helper.rb +2 -0
- data/ext/binder.cpp +124 -0
- data/ext/binder.h +46 -0
- data/ext/cmain.cpp +887 -0
- data/ext/ed.cpp +1988 -0
- data/ext/ed.h +422 -0
- data/ext/em.cpp +2352 -0
- data/ext/em.h +253 -0
- data/ext/eventmachine.h +128 -0
- data/ext/extconf.rb +179 -0
- data/ext/fastfilereader/extconf.rb +103 -0
- data/ext/fastfilereader/mapper.cpp +214 -0
- data/ext/fastfilereader/mapper.h +59 -0
- data/ext/fastfilereader/rubymain.cpp +127 -0
- data/ext/kb.cpp +79 -0
- data/ext/page.cpp +107 -0
- data/ext/page.h +51 -0
- data/ext/pipe.cpp +347 -0
- data/ext/project.h +161 -0
- data/ext/rubymain.cpp +1318 -0
- data/ext/ssl.cpp +468 -0
- data/ext/ssl.h +94 -0
- data/java/.classpath +6 -0
- data/java/.gitignore +1 -0
- data/java/.project +17 -0
- data/java/src/com/rubyeventmachine/DatagramPacket.java +13 -0
- data/java/src/com/rubyeventmachine/EmReactor.java +529 -0
- data/java/src/com/rubyeventmachine/EmReactorException.java +40 -0
- data/java/src/com/rubyeventmachine/EventCallback.java +7 -0
- data/java/src/com/rubyeventmachine/EventCode.java +26 -0
- data/java/src/com/rubyeventmachine/EventableChannel.java +130 -0
- data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +180 -0
- data/java/src/com/rubyeventmachine/EventableSocketChannel.java +405 -0
- data/java/src/com/rubyeventmachine/SslBox.java +310 -0
- data/lib/em/buftok.rb +110 -0
- data/lib/em/callback.rb +58 -0
- data/lib/em/channel.rb +64 -0
- data/lib/em/completion.rb +304 -0
- data/lib/em/connection.rb +712 -0
- data/lib/em/deferrable/pool.rb +2 -0
- data/lib/em/deferrable.rb +210 -0
- data/lib/em/file_watch.rb +73 -0
- data/lib/em/future.rb +61 -0
- data/lib/em/iterator.rb +231 -0
- data/lib/em/messages.rb +66 -0
- data/lib/em/pool.rb +151 -0
- data/lib/em/process_watch.rb +45 -0
- data/lib/em/processes.rb +123 -0
- data/lib/em/protocols/header_and_content.rb +138 -0
- data/lib/em/protocols/httpclient.rb +279 -0
- data/lib/em/protocols/httpclient2.rb +600 -0
- data/lib/em/protocols/line_and_text.rb +125 -0
- data/lib/em/protocols/line_protocol.rb +29 -0
- data/lib/em/protocols/linetext2.rb +161 -0
- data/lib/em/protocols/memcache.rb +331 -0
- data/lib/em/protocols/object_protocol.rb +46 -0
- data/lib/em/protocols/postgres3.rb +246 -0
- data/lib/em/protocols/saslauth.rb +175 -0
- data/lib/em/protocols/smtpclient.rb +365 -0
- data/lib/em/protocols/smtpserver.rb +643 -0
- data/lib/em/protocols/socks4.rb +66 -0
- data/lib/em/protocols/stomp.rb +205 -0
- data/lib/em/protocols/tcptest.rb +54 -0
- data/lib/em/protocols.rb +37 -0
- data/lib/em/pure_ruby.rb +1017 -0
- data/lib/em/queue.rb +71 -0
- data/lib/em/resolver.rb +209 -0
- data/lib/em/spawnable.rb +84 -0
- data/lib/em/streamer.rb +118 -0
- data/lib/em/threaded_resource.rb +90 -0
- data/lib/em/tick_loop.rb +85 -0
- data/lib/em/timers.rb +61 -0
- data/lib/em/version.rb +3 -0
- data/lib/eventmachine.rb +1553 -0
- data/lib/jeventmachine.rb +321 -0
- data/lib/rubyeventmachine.jar +0 -0
- data/rakelib/cpp.rake_example +77 -0
- data/rakelib/package.rake +98 -0
- data/rakelib/test.rake +8 -0
- data/tests/client.crt +31 -0
- data/tests/client.key +51 -0
- data/tests/em_test_helper.rb +64 -0
- data/tests/server.crt +36 -0
- data/tests/server.key +51 -0
- data/tests/test_attach.rb +150 -0
- data/tests/test_basic.rb +294 -0
- data/tests/test_channel.rb +62 -0
- data/tests/test_completion.rb +177 -0
- data/tests/test_connection_count.rb +53 -0
- data/tests/test_defer.rb +18 -0
- data/tests/test_deferrable.rb +35 -0
- data/tests/test_epoll.rb +145 -0
- data/tests/test_error_handler.rb +38 -0
- data/tests/test_exc.rb +28 -0
- data/tests/test_file_watch.rb +65 -0
- data/tests/test_futures.rb +170 -0
- data/tests/test_get_sock_opt.rb +37 -0
- data/tests/test_handler_check.rb +35 -0
- data/tests/test_hc.rb +155 -0
- data/tests/test_httpclient.rb +190 -0
- data/tests/test_httpclient2.rb +133 -0
- data/tests/test_idle_connection.rb +25 -0
- data/tests/test_inactivity_timeout.rb +54 -0
- data/tests/test_iterator.rb +97 -0
- data/tests/test_kb.rb +34 -0
- data/tests/test_line_protocol.rb +33 -0
- data/tests/test_ltp.rb +138 -0
- data/tests/test_ltp2.rb +288 -0
- data/tests/test_next_tick.rb +104 -0
- data/tests/test_object_protocol.rb +36 -0
- data/tests/test_pause.rb +102 -0
- data/tests/test_pending_connect_timeout.rb +52 -0
- data/tests/test_pool.rb +194 -0
- data/tests/test_process_watch.rb +48 -0
- data/tests/test_processes.rb +128 -0
- data/tests/test_proxy_connection.rb +180 -0
- data/tests/test_pure.rb +88 -0
- data/tests/test_queue.rb +50 -0
- data/tests/test_resolver.rb +55 -0
- data/tests/test_running.rb +14 -0
- data/tests/test_sasl.rb +47 -0
- data/tests/test_send_file.rb +217 -0
- data/tests/test_servers.rb +33 -0
- data/tests/test_set_sock_opt.rb +37 -0
- data/tests/test_shutdown_hooks.rb +23 -0
- data/tests/test_smtpclient.rb +55 -0
- data/tests/test_smtpserver.rb +57 -0
- data/tests/test_spawn.rb +293 -0
- data/tests/test_ssl_args.rb +78 -0
- data/tests/test_ssl_echo_data.rb +60 -0
- data/tests/test_ssl_methods.rb +56 -0
- data/tests/test_ssl_verify.rb +82 -0
- data/tests/test_stomp.rb +37 -0
- data/tests/test_system.rb +42 -0
- data/tests/test_threaded_resource.rb +53 -0
- data/tests/test_tick_loop.rb +59 -0
- data/tests/test_timers.rb +123 -0
- data/tests/test_ud.rb +8 -0
- data/tests/test_unbind_reason.rb +48 -0
- metadata +297 -0
data/lib/em/queue.rb
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
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
|
+
@items = []
|
|
21
|
+
@popq = []
|
|
22
|
+
end
|
|
23
|
+
|
|
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
|
|
26
|
+
# the next tick, if the queue has data, or when the queue is populated.
|
|
27
|
+
#
|
|
28
|
+
# @return [NilClass] nil
|
|
29
|
+
def pop(*a, &b)
|
|
30
|
+
cb = EM::Callback(*a, &b)
|
|
31
|
+
EM.schedule do
|
|
32
|
+
if @items.empty?
|
|
33
|
+
@popq << cb
|
|
34
|
+
else
|
|
35
|
+
cb.call @items.shift
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
nil # Always returns nil
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Push items onto the queue in the reactor thread. The items will not appear
|
|
42
|
+
# in the queue immediately, but will be scheduled for addition during the
|
|
43
|
+
# next reactor tick.
|
|
44
|
+
def push(*items)
|
|
45
|
+
EM.schedule do
|
|
46
|
+
@items.push(*items)
|
|
47
|
+
@popq.shift.call @items.shift until @items.empty? || @popq.empty?
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
alias :<< :push
|
|
51
|
+
|
|
52
|
+
# @return [Boolean]
|
|
53
|
+
# @note This is a peek, it's not thread safe, and may only tend toward accuracy.
|
|
54
|
+
def empty?
|
|
55
|
+
@items.empty?
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# @return [Integer] Queue size
|
|
59
|
+
# @note This is a peek, it's not thread safe, and may only tend toward accuracy.
|
|
60
|
+
def size
|
|
61
|
+
@items.size
|
|
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
|
data/lib/em/resolver.rb
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
module EventMachine
|
|
2
|
+
module DNS
|
|
3
|
+
class Resolver
|
|
4
|
+
|
|
5
|
+
def self.resolve(hostname)
|
|
6
|
+
Request.new(socket, hostname)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
@socket = @nameservers = nil
|
|
10
|
+
|
|
11
|
+
def self.socket
|
|
12
|
+
if !@socket || (@socket && @socket.error?)
|
|
13
|
+
@socket = Socket.open
|
|
14
|
+
|
|
15
|
+
@hosts = {}
|
|
16
|
+
IO.readlines('/etc/hosts').each do |line|
|
|
17
|
+
next if line =~ /^#/
|
|
18
|
+
addr, host = line.split(/\s+/)
|
|
19
|
+
|
|
20
|
+
if @hosts[host]
|
|
21
|
+
@hosts[host] << addr
|
|
22
|
+
else
|
|
23
|
+
@hosts[host] = [addr]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
@socket
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.nameservers=(ns)
|
|
32
|
+
@nameservers = ns
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.nameservers
|
|
36
|
+
if !@nameservers
|
|
37
|
+
@nameservers = []
|
|
38
|
+
IO.readlines('/etc/resolv.conf').each do |line|
|
|
39
|
+
if line =~ /^nameserver (.+)$/
|
|
40
|
+
@nameservers << $1.split(/\s+/).first
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
@nameservers
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.nameserver
|
|
48
|
+
nameservers.shuffle.first
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def self.hosts
|
|
52
|
+
@hosts
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class RequestIdAlreadyUsed < RuntimeError; end
|
|
57
|
+
|
|
58
|
+
class Socket < EventMachine::Connection
|
|
59
|
+
def self.open
|
|
60
|
+
EventMachine::open_datagram_socket('0.0.0.0', 0, self)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def initialize
|
|
64
|
+
@nameserver = nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def post_init
|
|
68
|
+
@requests = {}
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def start_timer
|
|
72
|
+
@timer ||= EM.add_periodic_timer(0.1, &method(:tick))
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def stop_timer
|
|
76
|
+
EM.cancel_timer(@timer)
|
|
77
|
+
@timer = nil
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def unbind
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def tick
|
|
84
|
+
@requests.each do |id,req|
|
|
85
|
+
req.tick
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def register_request(id, req)
|
|
90
|
+
if @requests.has_key?(id)
|
|
91
|
+
raise RequestIdAlreadyUsed
|
|
92
|
+
else
|
|
93
|
+
@requests[id] = req
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
start_timer
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def deregister_request(id, req)
|
|
100
|
+
@requests.delete(id)
|
|
101
|
+
stop_timer if @requests.length == 0
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def send_packet(pkt)
|
|
105
|
+
send_datagram(pkt, nameserver, 53)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def nameserver=(ns)
|
|
109
|
+
@nameserver = ns
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def nameserver
|
|
113
|
+
@nameserver || Resolver.nameserver
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Decodes the packet, looks for the request and passes the
|
|
117
|
+
# response over to the requester
|
|
118
|
+
def receive_data(data)
|
|
119
|
+
msg = nil
|
|
120
|
+
begin
|
|
121
|
+
msg = Resolv::DNS::Message.decode data
|
|
122
|
+
rescue
|
|
123
|
+
else
|
|
124
|
+
req = @requests[msg.id]
|
|
125
|
+
if req
|
|
126
|
+
@requests.delete(msg.id)
|
|
127
|
+
stop_timer if @requests.length == 0
|
|
128
|
+
req.receive_answer(msg)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
class Request
|
|
135
|
+
include Deferrable
|
|
136
|
+
attr_accessor :retry_interval, :max_tries
|
|
137
|
+
|
|
138
|
+
def initialize(socket, hostname)
|
|
139
|
+
@socket = socket
|
|
140
|
+
@hostname = hostname
|
|
141
|
+
@tries = 0
|
|
142
|
+
@last_send = Time.at(0)
|
|
143
|
+
@retry_interval = 3
|
|
144
|
+
@max_tries = 5
|
|
145
|
+
|
|
146
|
+
if addrs = Resolver.hosts[hostname]
|
|
147
|
+
succeed addrs
|
|
148
|
+
else
|
|
149
|
+
EM.next_tick { tick }
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def tick
|
|
154
|
+
# Break early if nothing to do
|
|
155
|
+
return if @last_send + @retry_interval > Time.now
|
|
156
|
+
if @tries < @max_tries
|
|
157
|
+
send
|
|
158
|
+
else
|
|
159
|
+
@socket.deregister_request(@id, self)
|
|
160
|
+
fail 'retries exceeded'
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def receive_answer(msg)
|
|
165
|
+
addrs = []
|
|
166
|
+
msg.each_answer do |name,ttl,data|
|
|
167
|
+
if data.kind_of?(Resolv::DNS::Resource::IN::A) ||
|
|
168
|
+
data.kind_of?(Resolv::DNS::Resource::IN::AAAA)
|
|
169
|
+
addrs << data.address.to_s
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
if addrs.empty?
|
|
174
|
+
fail "rcode=#{msg.rcode}"
|
|
175
|
+
else
|
|
176
|
+
succeed addrs
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
private
|
|
181
|
+
|
|
182
|
+
def send
|
|
183
|
+
@tries += 1
|
|
184
|
+
@last_send = Time.now
|
|
185
|
+
@socket.send_packet(packet.encode)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def id
|
|
189
|
+
begin
|
|
190
|
+
@id = rand(65535)
|
|
191
|
+
@socket.register_request(@id, self)
|
|
192
|
+
rescue RequestIdAlreadyUsed
|
|
193
|
+
retry
|
|
194
|
+
end unless defined?(@id)
|
|
195
|
+
|
|
196
|
+
@id
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def packet
|
|
200
|
+
msg = Resolv::DNS::Message.new
|
|
201
|
+
msg.id = id
|
|
202
|
+
msg.rd = 1
|
|
203
|
+
msg.add_question @hostname, Resolv::DNS::Resource::IN::A
|
|
204
|
+
msg
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
data/lib/em/spawnable.rb
ADDED
|
@@ -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
|
data/lib/em/streamer.rb
ADDED
|
@@ -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 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
|
data/lib/em/tick_loop.rb
ADDED
|
@@ -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
|
+
#
|
|
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
|
data/lib/em/timers.rb
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module EventMachine
|
|
2
|
+
# Creates a one-time timer
|
|
3
|
+
#
|
|
4
|
+
# timer = EventMachine::Timer.new(5) do
|
|
5
|
+
# # this will never fire because we cancel it
|
|
6
|
+
# end
|
|
7
|
+
# timer.cancel
|
|
8
|
+
#
|
|
9
|
+
class Timer
|
|
10
|
+
# Create a new timer that fires after a given number of seconds
|
|
11
|
+
def initialize interval, callback=nil, &block
|
|
12
|
+
@signature = EventMachine::add_timer(interval, callback || block)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Cancel the timer
|
|
16
|
+
def cancel
|
|
17
|
+
EventMachine.send :cancel_timer, @signature
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Creates a periodic timer
|
|
22
|
+
#
|
|
23
|
+
# @example
|
|
24
|
+
# n = 0
|
|
25
|
+
# timer = EventMachine::PeriodicTimer.new(5) do
|
|
26
|
+
# puts "the time is #{Time.now}"
|
|
27
|
+
# timer.cancel if (n+=1) > 5
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
class PeriodicTimer
|
|
31
|
+
# Create a new periodic timer that executes every interval seconds
|
|
32
|
+
def initialize interval, callback=nil, &block
|
|
33
|
+
@interval = interval
|
|
34
|
+
@code = callback || block
|
|
35
|
+
@cancelled = false
|
|
36
|
+
@work = method(:fire)
|
|
37
|
+
schedule
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Cancel the periodic timer
|
|
41
|
+
def cancel
|
|
42
|
+
@cancelled = true
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Fire the timer every interval seconds
|
|
46
|
+
attr_accessor :interval
|
|
47
|
+
|
|
48
|
+
# @private
|
|
49
|
+
def schedule
|
|
50
|
+
EventMachine::add_timer @interval, @work
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @private
|
|
54
|
+
def fire
|
|
55
|
+
unless @cancelled
|
|
56
|
+
@code.call
|
|
57
|
+
schedule
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|