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.
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