eventmachine 0.12.10 → 1.0.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/Gemfile +1 -0
- data/README +1 -2
- data/Rakefile +4 -76
- data/docs/DEFERRABLES +183 -70
- data/docs/KEYBOARD +15 -11
- data/docs/LIGHTWEIGHT_CONCURRENCY +84 -24
- data/docs/SMTP +3 -1
- data/docs/SPAWNED_PROCESSES +84 -25
- data/eventmachine.gemspec +19 -26
- data/examples/ex_tick_loop_array.rb +15 -0
- data/examples/ex_tick_loop_counter.rb +32 -0
- data/ext/binder.cpp +0 -1
- data/ext/cmain.cpp +36 -11
- data/ext/cplusplus.cpp +1 -1
- data/ext/ed.cpp +104 -113
- data/ext/ed.h +24 -30
- data/ext/em.cpp +347 -248
- data/ext/em.h +23 -16
- data/ext/eventmachine.h +5 -3
- data/ext/extconf.rb +5 -3
- data/ext/fastfilereader/extconf.rb +5 -3
- data/ext/fastfilereader/mapper.cpp +1 -1
- data/ext/kb.cpp +1 -3
- data/ext/pipe.cpp +9 -11
- data/ext/project.h +12 -4
- data/ext/rubymain.cpp +138 -89
- data/java/src/com/rubyeventmachine/EmReactor.java +1 -0
- data/lib/em/channel.rb +1 -1
- data/lib/em/connection.rb +6 -1
- data/lib/em/deferrable.rb +16 -2
- data/lib/em/iterator.rb +270 -0
- data/lib/em/protocols.rb +1 -1
- data/lib/em/protocols/httpclient.rb +5 -0
- data/lib/em/protocols/line_protocol.rb +28 -0
- data/lib/em/protocols/smtpserver.rb +101 -8
- data/lib/em/protocols/stomp.rb +1 -1
- data/lib/{pr_eventmachine.rb → em/pure_ruby.rb} +1 -11
- data/lib/em/queue.rb +1 -0
- data/lib/em/streamer.rb +1 -1
- data/lib/em/tick_loop.rb +85 -0
- data/lib/em/timers.rb +2 -1
- data/lib/em/version.rb +1 -1
- data/lib/eventmachine.rb +38 -84
- data/lib/jeventmachine.rb +1 -0
- data/tests/test_attach.rb +13 -3
- data/tests/test_basic.rb +60 -95
- data/tests/test_channel.rb +3 -2
- data/tests/test_defer.rb +14 -12
- data/tests/test_deferrable.rb +35 -0
- data/tests/test_file_watch.rb +1 -1
- data/tests/test_futures.rb +1 -1
- data/tests/test_hc.rb +40 -68
- data/tests/test_httpclient.rb +15 -6
- data/tests/test_httpclient2.rb +3 -2
- data/tests/test_inactivity_timeout.rb +3 -3
- data/tests/test_ltp.rb +13 -5
- data/tests/test_next_tick.rb +1 -1
- data/tests/test_pending_connect_timeout.rb +2 -2
- data/tests/test_process_watch.rb +36 -34
- data/tests/test_proxy_connection.rb +52 -0
- data/tests/test_pure.rb +10 -1
- data/tests/test_sasl.rb +1 -1
- data/tests/test_send_file.rb +16 -7
- data/tests/test_servers.rb +1 -1
- data/tests/test_tick_loop.rb +59 -0
- data/tests/test_timers.rb +13 -15
- metadata +45 -17
- data/web/whatis +0 -7
data/lib/em/protocols/stomp.rb
CHANGED
@@ -106,7 +106,7 @@ module EventMachine
|
|
106
106
|
ary = [verb, "\n"]
|
107
107
|
headers.each {|k,v| ary << "#{k}:#{v}\n" }
|
108
108
|
ary << "content-length: #{body.to_s.length}\n"
|
109
|
-
ary << "content-type: text/plain; charset=UTF-8\n"
|
109
|
+
ary << "content-type: text/plain; charset=UTF-8\n" unless headers.has_key? 'content-type'
|
110
110
|
ary << "\n"
|
111
111
|
ary << body.to_s
|
112
112
|
ary << "\0"
|
@@ -34,10 +34,7 @@ require 'socket'
|
|
34
34
|
require 'fcntl'
|
35
35
|
require 'set'
|
36
36
|
|
37
|
-
|
38
37
|
module EventMachine
|
39
|
-
|
40
|
-
|
41
38
|
class << self
|
42
39
|
# This is mostly useful for automated tests.
|
43
40
|
# Return a distinctive symbol so the caller knows whether he's dealing
|
@@ -236,15 +233,8 @@ module EventMachine
|
|
236
233
|
module UuidGenerator
|
237
234
|
|
238
235
|
def self.generate
|
239
|
-
if @ix and @ix >= 10000
|
240
|
-
@ix = nil
|
241
|
-
@seed = nil
|
242
|
-
end
|
243
|
-
|
244
|
-
@seed ||= `uuidgen`.chomp.gsub(/-/,"")
|
245
236
|
@ix ||= 0
|
246
|
-
|
247
|
-
"#{@seed}#{@ix += 1}"
|
237
|
+
@ix += 1
|
248
238
|
end
|
249
239
|
|
250
240
|
end
|
data/lib/em/queue.rb
CHANGED
data/lib/em/streamer.rb
CHANGED
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
CHANGED
@@ -32,6 +32,7 @@ module EventMachine
|
|
32
32
|
@interval = interval
|
33
33
|
@code = callback || block
|
34
34
|
@cancelled = false
|
35
|
+
@work = method(:fire)
|
35
36
|
schedule
|
36
37
|
end
|
37
38
|
|
@@ -44,7 +45,7 @@ module EventMachine
|
|
44
45
|
attr_accessor :interval
|
45
46
|
|
46
47
|
def schedule # :nodoc:
|
47
|
-
EventMachine::add_timer @interval,
|
48
|
+
EventMachine::add_timer @interval, @work
|
48
49
|
end
|
49
50
|
def fire # :nodoc:
|
50
51
|
unless @cancelled
|
data/lib/em/version.rb
CHANGED
data/lib/eventmachine.rb
CHANGED
@@ -1,75 +1,12 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
# Author:: Francis Cianfrocca (gmail: blackhedd)
|
4
|
-
# Homepage:: http://rubyeventmachine.com
|
5
|
-
# Date:: 8 Apr 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
|
-
#-- Select in a library based on a global variable.
|
28
|
-
# PROVISIONALLY commented out this whole mechanism which selects
|
29
|
-
# a pure-Ruby EM implementation if the extension is not available.
|
30
|
-
# I expect this will cause a lot of people's code to break, as it
|
31
|
-
# exposes misconfigurations and path problems that were masked up
|
32
|
-
# till now. The reason I'm disabling it is because the pure-Ruby
|
33
|
-
# code will have problems of its own, and it's not nearly as fast
|
34
|
-
# anyway. Suggested by a problem report from Moshe Litvin. 05Jun07.
|
35
|
-
#
|
36
|
-
# 05Dec07: Re-enabled the pure-ruby mechanism, but without the automatic
|
37
|
-
# fallback feature that tripped up Moshe Litvin. We shouldn't fail over to
|
38
|
-
# the pure Ruby version because it's possible that the user intended to
|
39
|
-
# run the extension but failed to do so because of a compilation or
|
40
|
-
# similar error. So we require either a global variable or an environment
|
41
|
-
# string be set in order to select the pure-Ruby version.
|
42
|
-
#
|
43
|
-
|
44
|
-
|
45
|
-
unless defined?($eventmachine_library)
|
46
|
-
$eventmachine_library = ENV['EVENTMACHINE_LIBRARY'] || :cascade
|
47
|
-
end
|
48
|
-
$eventmachine_library = $eventmachine_library.to_sym
|
49
|
-
|
50
|
-
case $eventmachine_library
|
51
|
-
when :pure_ruby
|
52
|
-
require 'pr_eventmachine'
|
53
|
-
when :extension
|
54
|
-
require 'rubyeventmachine'
|
55
|
-
when :java
|
1
|
+
if RUBY_PLATFORM =~ /java/
|
2
|
+
require 'java'
|
56
3
|
require 'jeventmachine'
|
57
|
-
else
|
58
|
-
# This is the case that most user code will take.
|
59
|
-
# Prefer the extension if available.
|
4
|
+
else
|
60
5
|
begin
|
61
|
-
|
62
|
-
require 'java'
|
63
|
-
require 'jeventmachine'
|
64
|
-
$eventmachine_library = :java
|
65
|
-
else
|
66
|
-
require 'rubyeventmachine'
|
67
|
-
$eventmachine_library = :extension
|
68
|
-
end
|
6
|
+
require 'rubyeventmachine'
|
69
7
|
rescue LoadError
|
70
|
-
warn "
|
71
|
-
|
72
|
-
$eventmachine_library = :pure_ruby
|
8
|
+
warn "Unable to load the EventMachine C extension; To use the pure-ruby reactor, require 'em/pure_ruby'"
|
9
|
+
raise
|
73
10
|
end
|
74
11
|
end
|
75
12
|
|
@@ -79,6 +16,7 @@ require 'em/future'
|
|
79
16
|
require 'em/streamer'
|
80
17
|
require 'em/spawnable'
|
81
18
|
require 'em/processes'
|
19
|
+
require 'em/iterator'
|
82
20
|
require 'em/buftok'
|
83
21
|
require 'em/timers'
|
84
22
|
require 'em/protocols'
|
@@ -88,6 +26,7 @@ require 'em/queue'
|
|
88
26
|
require 'em/channel'
|
89
27
|
require 'em/file_watch'
|
90
28
|
require 'em/process_watch'
|
29
|
+
require 'em/tick_loop'
|
91
30
|
|
92
31
|
require 'shellwords'
|
93
32
|
require 'thread'
|
@@ -189,7 +128,7 @@ module EventMachine
|
|
189
128
|
end
|
190
129
|
@next_tick_mutex = Mutex.new
|
191
130
|
@reactor_running = false
|
192
|
-
@next_tick_queue =
|
131
|
+
@next_tick_queue = []
|
193
132
|
@threadpool = nil
|
194
133
|
|
195
134
|
|
@@ -266,15 +205,21 @@ module EventMachine
|
|
266
205
|
@threadpool.each { |t| t.exit }
|
267
206
|
@threadpool.each do |t|
|
268
207
|
next unless t.alive?
|
269
|
-
|
270
|
-
|
208
|
+
begin
|
209
|
+
# Thread#kill! does not exist on 1.9 or rbx, and raises
|
210
|
+
# NotImplemented on jruby
|
211
|
+
t.kill!
|
212
|
+
rescue NoMethodError, NotImplementedError
|
213
|
+
t.kill
|
214
|
+
# XXX t.join here?
|
215
|
+
end
|
271
216
|
end
|
272
217
|
@threadqueue = nil
|
273
218
|
@resultqueue = nil
|
274
219
|
@threadpool = nil
|
275
220
|
end
|
276
221
|
|
277
|
-
@next_tick_queue =
|
222
|
+
@next_tick_queue = []
|
278
223
|
end
|
279
224
|
@reactor_running = false
|
280
225
|
@reactor_thread = nil
|
@@ -989,11 +934,10 @@ module EventMachine
|
|
989
934
|
cback.call result if cback
|
990
935
|
end
|
991
936
|
|
992
|
-
|
937
|
+
@next_tick_mutex.synchronize do
|
993
938
|
jobs, @next_tick_queue = @next_tick_queue, []
|
994
939
|
jobs
|
995
|
-
end
|
996
|
-
jobs.each { |j| j.call }
|
940
|
+
end.each { |j| j.call }
|
997
941
|
end
|
998
942
|
|
999
943
|
|
@@ -1055,6 +999,7 @@ module EventMachine
|
|
1055
999
|
def self.spawn_threadpool # :nodoc:
|
1056
1000
|
until @threadpool.size == @threadpool_size.to_i
|
1057
1001
|
thread = Thread.new do
|
1002
|
+
Thread.current.abort_on_exception = true
|
1058
1003
|
while true
|
1059
1004
|
op, cback = *@threadqueue.pop
|
1060
1005
|
result = op.call
|
@@ -1092,7 +1037,7 @@ module EventMachine
|
|
1092
1037
|
def self.next_tick pr=nil, &block
|
1093
1038
|
raise ArgumentError, "no proc or block given" unless ((pr && pr.respond_to?(:call)) or block)
|
1094
1039
|
@next_tick_mutex.synchronize do
|
1095
|
-
|
1040
|
+
@next_tick_queue << ( pr || block )
|
1096
1041
|
end
|
1097
1042
|
signal_loopbreak if reactor_running?
|
1098
1043
|
end
|
@@ -1369,8 +1314,8 @@ module EventMachine
|
|
1369
1314
|
# EM.run {
|
1370
1315
|
# EM.start_server("127.0.0.1", 8080, ProxyServer)
|
1371
1316
|
# }
|
1372
|
-
def self.enable_proxy(from, to, bufsize=0)
|
1373
|
-
EM::start_proxy(from.signature, to.signature, bufsize)
|
1317
|
+
def self.enable_proxy(from, to, bufsize=0, length=0)
|
1318
|
+
EM::start_proxy(from.signature, to.signature, bufsize, length)
|
1374
1319
|
end
|
1375
1320
|
|
1376
1321
|
# disable_proxy takes just one argument, a Connection that has proxying enabled via enable_proxy.
|
@@ -1422,7 +1367,12 @@ module EventMachine
|
|
1422
1367
|
elsif c = @acceptors.delete( conn_binding )
|
1423
1368
|
# no-op
|
1424
1369
|
else
|
1425
|
-
|
1370
|
+
if $! # Bubble user generated errors.
|
1371
|
+
@wrapped_exception = $!
|
1372
|
+
EM.stop
|
1373
|
+
else
|
1374
|
+
raise ConnectionNotBound, "recieved ConnectionUnbound for an unknown signature: #{conn_binding}"
|
1375
|
+
end
|
1426
1376
|
end
|
1427
1377
|
elsif opcode == ConnectionAccepted
|
1428
1378
|
accep,args,blk = @acceptors[conn_binding]
|
@@ -1431,12 +1381,12 @@ module EventMachine
|
|
1431
1381
|
@conns[data] = c
|
1432
1382
|
blk and blk.call(c)
|
1433
1383
|
c # (needed?)
|
1434
|
-
elsif opcode == ConnectionCompleted
|
1435
|
-
c = @conns[conn_binding] or raise ConnectionNotBound, "received ConnectionCompleted for unknown signature: #{conn_binding}"
|
1436
|
-
c.connection_completed
|
1437
1384
|
##
|
1438
1385
|
# The remaining code is a fallback for the pure ruby and java reactors.
|
1439
1386
|
# In the C++ reactor, these events are handled in the C event_callback() in rubymain.cpp
|
1387
|
+
elsif opcode == ConnectionCompleted
|
1388
|
+
c = @conns[conn_binding] or raise ConnectionNotBound, "received ConnectionCompleted for unknown signature: #{conn_binding}"
|
1389
|
+
c.connection_completed
|
1440
1390
|
elsif opcode == TimerFired
|
1441
1391
|
t = @timers.delete( data )
|
1442
1392
|
return if t == false # timer cancelled
|
@@ -1572,7 +1522,11 @@ module EventMachine
|
|
1572
1522
|
raise ArgumentError, "must provide module or subclass of #{klass.name}" unless klass >= handler
|
1573
1523
|
handler
|
1574
1524
|
elsif handler
|
1575
|
-
|
1525
|
+
begin
|
1526
|
+
handler::EM_CONNECTION_CLASS
|
1527
|
+
rescue NameError
|
1528
|
+
handler::const_set(:EM_CONNECTION_CLASS, Class.new(klass) {include handler})
|
1529
|
+
end
|
1576
1530
|
else
|
1577
1531
|
klass
|
1578
1532
|
end
|
data/lib/jeventmachine.rb
CHANGED
@@ -70,6 +70,7 @@ module EventMachine
|
|
70
70
|
SslHandshakeCompleted = 108
|
71
71
|
|
72
72
|
# Exceptions that are defined in rubymain.cpp
|
73
|
+
class ConnectionError < RuntimeError; end
|
73
74
|
class ConnectionNotBound < RuntimeError; end
|
74
75
|
class UnknownTimerFired < RuntimeError; end
|
75
76
|
class Unsupported < RuntimeError; end
|
data/tests/test_attach.rb
CHANGED
@@ -46,11 +46,21 @@ class TestAttach < Test::Unit::TestCase
|
|
46
46
|
def unbind
|
47
47
|
EM.next_tick do
|
48
48
|
$sock.write("def\n")
|
49
|
-
EM.add_timer(0.
|
49
|
+
EM.add_timer(0.1){ EM.stop }
|
50
50
|
end
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
+
def setup
|
55
|
+
$read, $write, $sock, $r, $w, $fd, $sock, $before, $after = nil
|
56
|
+
end
|
57
|
+
|
58
|
+
def teardown
|
59
|
+
[$read, $write, $sock, $r, $w, $fd, $sock, $before, $after].each do |io|
|
60
|
+
io.close rescue nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
54
64
|
def test_attach
|
55
65
|
EM.run{
|
56
66
|
EM.start_server Host, Port, EchoServer
|
@@ -88,8 +98,8 @@ class TestAttach < Test::Unit::TestCase
|
|
88
98
|
def test_set_readable
|
89
99
|
EM.run{
|
90
100
|
$r, $w = IO.pipe
|
91
|
-
c = EM.watch $r, PipeWatch do |
|
92
|
-
|
101
|
+
c = EM.watch $r, PipeWatch do |con|
|
102
|
+
con.notify_readable = false
|
93
103
|
end
|
94
104
|
|
95
105
|
EM.next_tick{
|
data/tests/test_basic.rb
CHANGED
@@ -30,44 +30,17 @@ require 'socket'
|
|
30
30
|
require 'test/unit'
|
31
31
|
|
32
32
|
class TestBasic < Test::Unit::TestCase
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
def test_libtype
|
45
|
-
lt = EventMachine.library_type
|
46
|
-
em_lib = (ENV["EVENTMACHINE_LIBRARY"] || $eventmachine_library || :xxx).to_sym
|
47
|
-
|
48
|
-
# Running from test runner, under jruby.
|
49
|
-
if RUBY_PLATFORM == 'java'
|
50
|
-
unless em_lib == :pure_ruby
|
51
|
-
assert_equal( :java, lt )
|
52
|
-
return
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
case em_lib
|
57
|
-
when :pure_ruby
|
58
|
-
assert_equal( :pure_ruby, lt )
|
59
|
-
when :extension
|
60
|
-
assert_equal( :extension, lt )
|
61
|
-
when :java
|
62
|
-
assert_equal( :java, lt )
|
63
|
-
else
|
64
|
-
# Running from jruby as a standalone test.
|
65
|
-
if RUBY_PLATFORM == 'java'
|
66
|
-
assert_equal( :java, lt )
|
67
|
-
else
|
68
|
-
assert_equal( :extension, lt )
|
69
|
-
end
|
70
|
-
end
|
33
|
+
def test_connection_class_cache
|
34
|
+
mod = Module.new
|
35
|
+
a, b = nil, nil
|
36
|
+
EM.run {
|
37
|
+
EM.start_server '127.0.0.1', 9999, mod
|
38
|
+
a = EM.connect '127.0.0.1', 9999, mod
|
39
|
+
b = EM.connect '127.0.0.1', 9999, mod
|
40
|
+
EM.stop
|
41
|
+
}
|
42
|
+
assert_equal a.class, b.class
|
43
|
+
assert_kind_of EM::Connection, a
|
71
44
|
end
|
72
45
|
|
73
46
|
#-------------------------------------
|
@@ -123,20 +96,11 @@ class TestBasic < Test::Unit::TestCase
|
|
123
96
|
assert !EM.reactor_running?
|
124
97
|
end
|
125
98
|
|
126
|
-
|
127
|
-
#--------------------------------------
|
128
|
-
|
129
|
-
# TODO! This is an unfinished edge case.
|
130
|
-
# EM mishandles uncaught Ruby exceptions that fire from within #unbind handlers.
|
131
|
-
# A uncaught Ruby exception results in a call to EM::release_machine (which is in an ensure
|
132
|
-
# block in EM::run). But if EM is processing an unbind request, the release_machine call
|
133
|
-
# will cause a segmentation fault.
|
134
|
-
#
|
135
|
-
|
136
99
|
TestHost = "127.0.0.1"
|
137
100
|
TestPort = 9070
|
138
101
|
|
139
102
|
class UnbindError < EM::Connection
|
103
|
+
ERR = Class.new(StandardError)
|
140
104
|
def initialize *args
|
141
105
|
super
|
142
106
|
end
|
@@ -144,12 +108,12 @@ class TestBasic < Test::Unit::TestCase
|
|
144
108
|
close_connection_after_writing
|
145
109
|
end
|
146
110
|
def unbind
|
147
|
-
raise
|
111
|
+
raise ERR
|
148
112
|
end
|
149
113
|
end
|
150
114
|
|
151
|
-
def
|
152
|
-
assert_raises(
|
115
|
+
def test_unbind_error
|
116
|
+
assert_raises( UnbindError::ERR ) {
|
153
117
|
EM.run {
|
154
118
|
EM.start_server TestHost, TestPort
|
155
119
|
EM.connect TestHost, TestPort, UnbindError
|
@@ -157,47 +121,6 @@ class TestBasic < Test::Unit::TestCase
|
|
157
121
|
}
|
158
122
|
end
|
159
123
|
|
160
|
-
#------------------------------------
|
161
|
-
#
|
162
|
-
# TODO. This is an unfinished bug fix.
|
163
|
-
# This case was originally reported by Dan Aquino. If you throw a Ruby exception
|
164
|
-
# in a post_init handler, it gets rethrown as a confusing reactor exception.
|
165
|
-
# The problem is in eventmachine.rb, which calls post_init within the private
|
166
|
-
# initialize method of the EM::Connection class. This happens in both the EM::connect
|
167
|
-
# method and in the code that responds to connection-accepted events.
|
168
|
-
# What happens is that we instantiate the new connection object, which calls
|
169
|
-
# initialize, and then after initialize returns, we stick the new connection object
|
170
|
-
# into EM's @conns hashtable.
|
171
|
-
# But the problem is that Connection::initialize calls #post_init before it returns,
|
172
|
-
# and this may be user-written code that may throw an uncaught Ruby exception.
|
173
|
-
# If that happens, the reactor will abort, and it will then try to run down open
|
174
|
-
# connections. Because @conns never got a chance to properly reflect the new connection
|
175
|
-
# (because initialize never returned), we throw a ConnectionNotBound error
|
176
|
-
# (eventmachine.rb line 1080).
|
177
|
-
# When the bug is fixed, activate this test case.
|
178
|
-
#
|
179
|
-
|
180
|
-
class PostInitError < EM::Connection
|
181
|
-
def post_init
|
182
|
-
aaa bbb # should produce a Ruby exception
|
183
|
-
end
|
184
|
-
end
|
185
|
-
# This test causes issues, the machine becomes unreleasable after
|
186
|
-
# release_machine suffers an exception in event_callback.
|
187
|
-
def xxx_test_post_init_error
|
188
|
-
assert_raises( EventMachine::ConnectionNotBound ) {
|
189
|
-
EM.run {
|
190
|
-
EM::Timer.new(1) {EM.stop}
|
191
|
-
EM.start_server TestHost, TestPort
|
192
|
-
EM.connect TestHost, TestPort, PostInitError
|
193
|
-
}
|
194
|
-
}
|
195
|
-
EM.run {
|
196
|
-
EM.stop
|
197
|
-
}
|
198
|
-
assert !EM.reactor_running?
|
199
|
-
end
|
200
|
-
|
201
124
|
module BrsTestSrv
|
202
125
|
def receive_data data
|
203
126
|
$received << data
|
@@ -213,6 +136,15 @@ class TestBasic < Test::Unit::TestCase
|
|
213
136
|
end
|
214
137
|
end
|
215
138
|
|
139
|
+
def setup_timeout(timeout = 4)
|
140
|
+
EM.schedule {
|
141
|
+
start_time = EM.current_time
|
142
|
+
EM.add_periodic_timer(0.01) {
|
143
|
+
raise "timeout" if EM.current_time - start_time >= timeout
|
144
|
+
}
|
145
|
+
}
|
146
|
+
end
|
147
|
+
|
216
148
|
# From ticket #50
|
217
149
|
def test_byte_range_send
|
218
150
|
$received = ''
|
@@ -221,7 +153,7 @@ class TestBasic < Test::Unit::TestCase
|
|
221
153
|
EM::start_server TestHost, TestPort, BrsTestSrv
|
222
154
|
EM::connect TestHost, TestPort, BrsTestCli
|
223
155
|
|
224
|
-
|
156
|
+
setup_timeout
|
225
157
|
}
|
226
158
|
assert_equal($sent, $received)
|
227
159
|
end
|
@@ -280,5 +212,38 @@ class TestBasic < Test::Unit::TestCase
|
|
280
212
|
}
|
281
213
|
assert_equal(interval, $interval)
|
282
214
|
end
|
283
|
-
|
284
|
-
|
215
|
+
|
216
|
+
module PostInitRaiser
|
217
|
+
ERR = Class.new(StandardError)
|
218
|
+
def post_init
|
219
|
+
raise ERR
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def test_bubble_errors_from_post_init
|
224
|
+
localhost, port = '127.0.0.1', 9000
|
225
|
+
assert_raises(PostInitRaiser::ERR) do
|
226
|
+
EM.run do
|
227
|
+
EM.start_server localhost, port
|
228
|
+
EM.connect localhost, port, PostInitRaiser
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
module InitializeRaiser
|
234
|
+
ERR = Class.new(StandardError)
|
235
|
+
def initialize
|
236
|
+
raise ERR
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def test_bubble_errors_from_initialize
|
241
|
+
localhost, port = '127.0.0.1', 9000
|
242
|
+
assert_raises(InitializeRaiser::ERR) do
|
243
|
+
EM.run do
|
244
|
+
EM.start_server localhost, port
|
245
|
+
EM.connect localhost, port, InitializeRaiser
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|