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.
Files changed (69) hide show
  1. data/.gitignore +2 -0
  2. data/Gemfile +1 -0
  3. data/README +1 -2
  4. data/Rakefile +4 -76
  5. data/docs/DEFERRABLES +183 -70
  6. data/docs/KEYBOARD +15 -11
  7. data/docs/LIGHTWEIGHT_CONCURRENCY +84 -24
  8. data/docs/SMTP +3 -1
  9. data/docs/SPAWNED_PROCESSES +84 -25
  10. data/eventmachine.gemspec +19 -26
  11. data/examples/ex_tick_loop_array.rb +15 -0
  12. data/examples/ex_tick_loop_counter.rb +32 -0
  13. data/ext/binder.cpp +0 -1
  14. data/ext/cmain.cpp +36 -11
  15. data/ext/cplusplus.cpp +1 -1
  16. data/ext/ed.cpp +104 -113
  17. data/ext/ed.h +24 -30
  18. data/ext/em.cpp +347 -248
  19. data/ext/em.h +23 -16
  20. data/ext/eventmachine.h +5 -3
  21. data/ext/extconf.rb +5 -3
  22. data/ext/fastfilereader/extconf.rb +5 -3
  23. data/ext/fastfilereader/mapper.cpp +1 -1
  24. data/ext/kb.cpp +1 -3
  25. data/ext/pipe.cpp +9 -11
  26. data/ext/project.h +12 -4
  27. data/ext/rubymain.cpp +138 -89
  28. data/java/src/com/rubyeventmachine/EmReactor.java +1 -0
  29. data/lib/em/channel.rb +1 -1
  30. data/lib/em/connection.rb +6 -1
  31. data/lib/em/deferrable.rb +16 -2
  32. data/lib/em/iterator.rb +270 -0
  33. data/lib/em/protocols.rb +1 -1
  34. data/lib/em/protocols/httpclient.rb +5 -0
  35. data/lib/em/protocols/line_protocol.rb +28 -0
  36. data/lib/em/protocols/smtpserver.rb +101 -8
  37. data/lib/em/protocols/stomp.rb +1 -1
  38. data/lib/{pr_eventmachine.rb → em/pure_ruby.rb} +1 -11
  39. data/lib/em/queue.rb +1 -0
  40. data/lib/em/streamer.rb +1 -1
  41. data/lib/em/tick_loop.rb +85 -0
  42. data/lib/em/timers.rb +2 -1
  43. data/lib/em/version.rb +1 -1
  44. data/lib/eventmachine.rb +38 -84
  45. data/lib/jeventmachine.rb +1 -0
  46. data/tests/test_attach.rb +13 -3
  47. data/tests/test_basic.rb +60 -95
  48. data/tests/test_channel.rb +3 -2
  49. data/tests/test_defer.rb +14 -12
  50. data/tests/test_deferrable.rb +35 -0
  51. data/tests/test_file_watch.rb +1 -1
  52. data/tests/test_futures.rb +1 -1
  53. data/tests/test_hc.rb +40 -68
  54. data/tests/test_httpclient.rb +15 -6
  55. data/tests/test_httpclient2.rb +3 -2
  56. data/tests/test_inactivity_timeout.rb +3 -3
  57. data/tests/test_ltp.rb +13 -5
  58. data/tests/test_next_tick.rb +1 -1
  59. data/tests/test_pending_connect_timeout.rb +2 -2
  60. data/tests/test_process_watch.rb +36 -34
  61. data/tests/test_proxy_connection.rb +52 -0
  62. data/tests/test_pure.rb +10 -1
  63. data/tests/test_sasl.rb +1 -1
  64. data/tests/test_send_file.rb +16 -7
  65. data/tests/test_servers.rb +1 -1
  66. data/tests/test_tick_loop.rb +59 -0
  67. data/tests/test_timers.rb +13 -15
  68. metadata +45 -17
  69. data/web/whatis +0 -7
@@ -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
@@ -45,6 +45,7 @@ module EventMachine
45
45
  @popq.shift.call @items.shift until @items.empty? || @popq.empty?
46
46
  end
47
47
  end
48
+ alias :<< :push
48
49
 
49
50
  # N.B. This is a peek, it's not thread safe, and may only tend toward
50
51
  # accuracy.
@@ -53,7 +53,7 @@ module EventMachine
53
53
  @http_chunks = args[:http_chunks]
54
54
 
55
55
  if File.exist?(filename)
56
- @size = File.size?(filename)
56
+ @size = File.size(filename)
57
57
  if @size <= MappingThreshold
58
58
  stream_without_mapping filename
59
59
  else
@@ -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
@@ -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, method(:fire)
48
+ EventMachine::add_timer @interval, @work
48
49
  end
49
50
  def fire # :nodoc:
50
51
  unless @cancelled
@@ -1,3 +1,3 @@
1
1
  module EventMachine
2
- VERSION = "0.12.10"
2
+ VERSION = "1.0.0.beta.1"
3
3
  end
@@ -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 # :cascade
58
- # This is the case that most user code will take.
59
- # Prefer the extension if available.
4
+ else
60
5
  begin
61
- if RUBY_PLATFORM =~ /java/
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 "# EventMachine fell back to pure ruby mode" if $DEBUG
71
- require 'pr_eventmachine'
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 = nil
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
- # ruby 1.9 has no kill!
270
- t.respond_to?(:kill!) ? t.kill! : t.kill
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 = nil
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
- jobs = @next_tick_mutex.synchronize do
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
- (@next_tick_queue ||= []) << ( pr || block )
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
- raise ConnectionNotBound, "recieved ConnectionUnbound for an unknown signature: #{conn_binding}"
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
- Class.new(klass){ include handler }
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
@@ -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
@@ -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.5){ EM.stop }
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 |c|
92
- c.notify_readable = false
101
+ c = EM.watch $r, PipeWatch do |con|
102
+ con.notify_readable = false
93
103
  end
94
104
 
95
105
  EM.next_tick{
@@ -30,44 +30,17 @@ require 'socket'
30
30
  require 'test/unit'
31
31
 
32
32
  class TestBasic < Test::Unit::TestCase
33
-
34
- def setup
35
- assert(!EM.reactor_running?)
36
- end
37
-
38
- def teardown
39
- assert(!EM.reactor_running?)
40
- end
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 "Blooey"
111
+ raise ERR
148
112
  end
149
113
  end
150
114
 
151
- def xxx_test_unbind_error
152
- assert_raises( RuntimeError ) {
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
- EM::add_timer(0.5) { assert(false, 'test timed out'); EM.stop; Kernel.warn "test timed out!" }
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
- end
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