eventmachine 0.12.10 → 1.0.0.beta.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.
- 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
|