eventmachine 0.9.0 → 0.10.0

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.
@@ -1,4 +1,4 @@
1
- # $Id: eventmachine_version.rb 536 2007-09-16 23:55:03Z blackhedd $
1
+ # $Id: eventmachine_version.rb 608 2007-12-09 21:04:39Z blackhedd $
2
2
  #
3
3
  # Author:: Francis Cianfrocca (gmail: blackhedd)
4
4
  # Homepage:: http://rubyeventmachine.com
@@ -25,7 +25,7 @@
25
25
 
26
26
  module EventMachine
27
27
 
28
- VERSION = "0.9.0"
28
+ VERSION = "0.10.0"
29
29
 
30
30
  end
31
31
 
@@ -1,4 +1,4 @@
1
- # $Id: pr_eventmachine.rb 319 2007-05-22 22:11:18Z blackhedd $
1
+ # $Id: pr_eventmachine.rb 604 2007-12-06 12:31:39Z blackhedd $
2
2
  #
3
3
  # Author:: Francis Cianfrocca (gmail: blackhedd)
4
4
  # Homepage:: http://rubyeventmachine.com
@@ -32,6 +32,7 @@ require 'singleton'
32
32
  require 'forwardable'
33
33
  require 'socket'
34
34
  require 'fcntl'
35
+ require 'set'
35
36
 
36
37
 
37
38
  module EventMachine
@@ -99,6 +100,23 @@ module EventMachine
99
100
  s.uuid
100
101
  end
101
102
 
103
+ # #stop_tcp_server
104
+ def stop_tcp_server sig
105
+ s = Reactor.instance.get_selectable(sig)
106
+ s.schedule_close
107
+ end
108
+
109
+ # #start_unix_server
110
+ def start_unix_server chain
111
+ (s = EvmaUNIXServer.start_server chain) or raise "no acceptor"
112
+ s.uuid
113
+ end
114
+
115
+ # #connect_unix_server
116
+ def connect_unix_server chain
117
+ EvmaUNIXClient.connect(chain).uuid
118
+ end
119
+
102
120
  # #signal_loopbreak
103
121
  def signal_loopbreak
104
122
  Reactor.instance.signal_loopbreak
@@ -129,6 +147,54 @@ module EventMachine
129
147
  Reactor.instance.set_timer_quantum(( 1.0 * interval) / 1000.0)
130
148
  end
131
149
 
150
+ # #epoll is a harmless no-op in the pure-Ruby implementation. This is intended to ensure
151
+ # that user code behaves properly across different EM implementations.
152
+ def epoll
153
+ end
154
+
155
+ # #set_rlimit_nofile is a no-op in the pure-Ruby implementation. We simply return Ruby's built-in
156
+ # per-process file-descriptor limit.
157
+ def set_rlimit_nofile n
158
+ 1024
159
+ end
160
+
161
+ # #set_max_timer_count is a harmless no-op in pure Ruby, which doesn't have a built-in limit
162
+ # on the number of available timers.
163
+ def set_max_timer_count n
164
+ end
165
+
166
+ # #send_file_data
167
+ def send_file_data sig, filename
168
+ sz = File.size(filename)
169
+ raise "file too large" if sz > 32*1024
170
+ data =
171
+ begin
172
+ File.read filename
173
+ rescue
174
+ ""
175
+ end
176
+ send_data sig, data, data.length
177
+ end
178
+
179
+ # #get_outbound_data_size
180
+ #
181
+ def get_outbound_data_size sig
182
+ r = Reactor.instance.get_selectable( sig ) or raise "unknown get_outbound_data_size target"
183
+ r.get_outbound_data_size
184
+ end
185
+
186
+ # #read_keyboard
187
+ #
188
+ def read_keyboard
189
+ EvmaKeyboard.open.uuid
190
+ end
191
+
192
+ # #set_comm_inactivity_timeout
193
+ #
194
+ def set_comm_inactivity_timeout sig, tm
195
+ r = Reactor.instance.get_selectable( sig ) or raise "unknown set_comm_inactivity_timeout target"
196
+ r.set_inactivity_timeout tm
197
+ end
132
198
  end
133
199
 
134
200
  end
@@ -144,6 +210,16 @@ end
144
210
 
145
211
  #-----------------------------------------------------------------
146
212
 
213
+ module EventMachine
214
+ class Connection
215
+ def get_outbound_data_size
216
+ EventMachine::get_outbound_data_size @signature
217
+ end
218
+ end
219
+ end
220
+
221
+ #-----------------------------------------------------------------
222
+
147
223
  module EventMachine
148
224
 
149
225
  # Factored out so we can substitute other implementations
@@ -171,11 +247,11 @@ end
171
247
  module EventMachine
172
248
 
173
249
  TimerFired = 100
174
- ConnectionData = 101
175
- ConnectionUnbound = 102
176
- ConnectionAccepted = 103
177
- ConnectionCompleted = 104
178
- LoopbreakSignalled = 105
250
+ ConnectionData = 101
251
+ ConnectionUnbound = 102
252
+ ConnectionAccepted = 103
253
+ ConnectionCompleted = 104
254
+ LoopbreakSignalled = 105
179
255
 
180
256
  end
181
257
 
@@ -185,14 +261,21 @@ module EventMachine
185
261
  class Reactor
186
262
  include Singleton
187
263
 
264
+ HeartbeatInterval = 2
265
+
266
+ attr_reader :current_loop_time
267
+
188
268
  def initialize
189
269
  initialize_for_run
190
270
  end
191
271
 
272
+ #--
273
+ # Replaced original implementation 05Dec07, was way too slow because of the sort.
192
274
  def install_oneshot_timer interval
193
275
  uuid = UuidGenerator::generate
194
- @timers << [Time.now + interval, uuid]
195
- @timers.sort! {|a,b| a.first <=> b.first}
276
+ #@timers << [Time.now + interval, uuid]
277
+ #@timers.sort! {|a,b| a.first <=> b.first}
278
+ @timers.add([Time.now + interval, uuid])
196
279
  uuid
197
280
  end
198
281
 
@@ -202,8 +285,10 @@ class Reactor
202
285
  @running = false
203
286
  @stop_scheduled = false
204
287
  @selectables ||= {}; @selectables.clear
205
- @timers = []
206
- set_timer_quantum(0.5)
288
+ @timers = SortedSet.new # []
289
+ set_timer_quantum(0.1)
290
+ @current_loop_time = Time.now
291
+ @next_heartbeat = @current_loop_time + HeartbeatInterval
207
292
  end
208
293
 
209
294
  def add_selectable io
@@ -217,27 +302,49 @@ class Reactor
217
302
  def run
218
303
  raise Error.new( "already running" ) if @running
219
304
  @running = true
220
- open_loopbreaker
221
305
 
222
- loop {
223
- break if @stop_scheduled
224
- run_timers
225
- break if @stop_scheduled
226
- crank_selectables
227
- }
306
+ begin
307
+ open_loopbreaker
228
308
 
229
- close_loopbreaker
230
- @selectables.each {|k, io| io.close}
231
- @selectables.clear
309
+ loop {
310
+ @current_loop_time = Time.now
311
+
312
+ break if @stop_scheduled
313
+ run_timers
314
+ break if @stop_scheduled
315
+ crank_selectables
316
+ break if @stop_scheduled
317
+ run_heartbeats
318
+ }
319
+ ensure
320
+ close_loopbreaker
321
+ @selectables.each {|k, io| io.close}
322
+ @selectables.clear
323
+
324
+ @running = false
325
+ end
232
326
 
233
- @running = false
234
327
  end
235
328
 
236
329
  def run_timers
237
- now = Time.now
238
- while @timers.length > 0 and @timers.first.first <= now
239
- t = @timers.shift
240
- EventMachine::event_callback "", TimerFired, t.last
330
+ @timers.each {|t|
331
+ if t.first <= @current_loop_time
332
+ @timers.delete t
333
+ EventMachine::event_callback "", TimerFired, t.last
334
+ else
335
+ break
336
+ end
337
+ }
338
+ #while @timers.length > 0 and @timers.first.first <= now
339
+ # t = @timers.shift
340
+ # EventMachine::event_callback "", TimerFired, t.last
341
+ #end
342
+ end
343
+
344
+ def run_heartbeats
345
+ if @next_heartbeat <= @current_loop_time
346
+ @next_heartbeat = @current_loop_time + HeartbeatInterval
347
+ @selectables.each {|k,io| io.heartbeat}
241
348
  end
242
349
  end
243
350
 
@@ -304,6 +411,9 @@ class IO
304
411
  def_delegator :@my_selectable, :schedule_close
305
412
  def_delegator :@my_selectable, :get_peername
306
413
  def_delegator :@my_selectable, :send_datagram
414
+ def_delegator :@my_selectable, :get_outbound_data_size
415
+ def_delegator :@my_selectable, :set_inactivity_timeout
416
+ def_delegator :@my_selectable, :heartbeat
307
417
  end
308
418
 
309
419
  #--------------------------------------------------------------
@@ -316,6 +426,7 @@ module EventMachine
316
426
  def initialize io
317
427
  @uuid = UuidGenerator.generate
318
428
  @io = io
429
+ @last_activity = Reactor.instance.current_loop_time
319
430
 
320
431
  m = @io.fcntl(Fcntl::F_GETFL, 0)
321
432
  @io.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK | m)
@@ -344,6 +455,12 @@ module EventMachine
344
455
  nil
345
456
  end
346
457
 
458
+ def set_inactivity_timeout tm
459
+ @inactivity_timeout = tm
460
+ end
461
+
462
+ def heartbeat
463
+ end
347
464
  end
348
465
 
349
466
  end
@@ -384,9 +501,12 @@ module EventMachine
384
501
  # Proper nonblocking I/O was added to Ruby 1.8.4 in May 2006.
385
502
  # If we have it, then we can read multiple times safely to improve
386
503
  # performance.
504
+ # The last-activity clock ASSUMES that we only come here when we
505
+ # have selected readable.
387
506
  # TODO, coalesce multiple reads into a single event.
388
507
  # TODO, do the function check somewhere else and cache it.
389
508
  def eventable_read
509
+ @last_activity = Reactor.instance.current_loop_time
390
510
  begin
391
511
  if io.respond_to?(:read_nonblock)
392
512
  10.times {
@@ -399,7 +519,7 @@ module EventMachine
399
519
  end
400
520
  rescue Errno::EAGAIN
401
521
  # no-op
402
- rescue Errno::ECONNRESET, EOFError
522
+ rescue Errno::ECONNRESET, Errno::ECONNREFUSED, EOFError
403
523
  @close_scheduled = true
404
524
  EventMachine::event_callback uuid, ConnectionUnbound, nil
405
525
  end
@@ -416,8 +536,11 @@ module EventMachine
416
536
  # one busy connection could hog output buffers and slow down other
417
537
  # connections. Also we should coalesce small writes.
418
538
  # URGENT TODO: Coalesce small writes. They are a performance killer.
539
+ # The last-activity recorder ASSUMES we'll only come here if we've
540
+ # selected writable.
419
541
  def eventable_write
420
542
  # coalesce the outbound array here, perhaps
543
+ @last_activity = Reactor.instance.current_loop_time
421
544
  while data = @outbound_q.shift do
422
545
  begin
423
546
  data = data.to_s
@@ -433,7 +556,7 @@ module EventMachine
433
556
  end
434
557
  rescue Errno::EAGAIN
435
558
  @outbound_q.unshift data
436
- rescue EOFError, Errno::ECONNRESET
559
+ rescue EOFError, Errno::ECONNRESET, Errno::ECONNREFUSED
437
560
  @close_scheduled = true
438
561
  @outbound_q.clear
439
562
  end
@@ -467,6 +590,16 @@ module EventMachine
467
590
  io.getpeername
468
591
  end
469
592
 
593
+ # #get_outbound_data_size
594
+ def get_outbound_data_size
595
+ @outbound_q.inject(0) {|memo,obj| memo += (obj || "").length}
596
+ end
597
+
598
+ def heartbeat
599
+ if @inactivity_timeout and (@last_activity + @inactivity_timeout) < Reactor.instance.current_loop_time
600
+ schedule_close true
601
+ end
602
+ end
470
603
  end
471
604
 
472
605
 
@@ -509,7 +642,88 @@ module EventMachine
509
642
  def eventable_write
510
643
  if @pending
511
644
  @pending = false
512
- EventMachine::event_callback uuid, ConnectionCompleted, ""
645
+ if 0 == io.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR).unpack("i").first
646
+ EventMachine::event_callback uuid, ConnectionCompleted, ""
647
+ end
648
+ else
649
+ super
650
+ end
651
+ end
652
+
653
+
654
+
655
+ end
656
+ end
657
+
658
+ #--------------------------------------------------------------
659
+
660
+
661
+
662
+ module EventMachine
663
+ class EvmaKeyboard < StreamObject
664
+
665
+ def self.open
666
+ EvmaKeyboard.new STDIN
667
+ end
668
+
669
+
670
+ def initialize io
671
+ super
672
+ end
673
+
674
+
675
+ def select_for_writing?
676
+ false
677
+ end
678
+
679
+ def select_for_reading?
680
+ true
681
+ end
682
+
683
+
684
+ end
685
+ end
686
+
687
+
688
+ #--------------------------------------------------------------
689
+
690
+
691
+
692
+ module EventMachine
693
+ class EvmaUNIXClient < StreamObject
694
+
695
+ def self.connect chain
696
+ sd = Socket.new( Socket::AF_LOCAL, Socket::SOCK_STREAM, 0 )
697
+ begin
698
+ # TODO, this assumes a current Ruby snapshot.
699
+ # We need to degrade to a nonblocking connect otherwise.
700
+ sd.connect_nonblock( Socket.pack_sockaddr_un( chain ))
701
+ rescue Errno::EINPROGRESS
702
+ end
703
+ EvmaUNIXClient.new sd
704
+ end
705
+
706
+
707
+ def initialize io
708
+ super
709
+ @pending = true
710
+ end
711
+
712
+
713
+ def select_for_writing?
714
+ @pending ? true : super
715
+ end
716
+
717
+ def select_for_reading?
718
+ @pending ? false : super
719
+ end
720
+
721
+ def eventable_write
722
+ if @pending
723
+ @pending = false
724
+ if 0 == io.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR).unpack("i").first
725
+ EventMachine::event_callback uuid, ConnectionCompleted, ""
726
+ end
513
727
  else
514
728
  super
515
729
  end
@@ -526,6 +740,8 @@ end
526
740
  module EventMachine
527
741
  class EvmaTCPServer < Selectable
528
742
 
743
+ # TODO, refactor and unify with EvmaUNIXServer.
744
+
529
745
  class << self
530
746
  # Versions of ruby 1.8.4 later than May 26 2006 will work properly
531
747
  # with an object of type TCPServer. Prior versions won't so we
@@ -564,6 +780,66 @@ module EventMachine
564
780
  end
565
781
  end
566
782
 
783
+ #--
784
+ #
785
+ def schedule_close
786
+ @close_scheduled = true
787
+ end
788
+
789
+ end
790
+ end
791
+
792
+
793
+ #--------------------------------------------------------------
794
+
795
+ module EventMachine
796
+ class EvmaUNIXServer < Selectable
797
+
798
+ # TODO, refactor and unify with EvmaTCPServer.
799
+
800
+ class << self
801
+ # Versions of ruby 1.8.4 later than May 26 2006 will work properly
802
+ # with an object of type TCPServer. Prior versions won't so we
803
+ # play it safe and just build a socket.
804
+ #
805
+ def start_server chain
806
+ sd = Socket.new( Socket::AF_LOCAL, Socket::SOCK_STREAM, 0 )
807
+ sd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true )
808
+ sd.bind( Socket.pack_sockaddr_un( chain ))
809
+ sd.listen( 50 ) # 5 is what you see in all the books. Ain't enough.
810
+ EvmaUNIXServer.new sd
811
+ end
812
+ end
813
+
814
+ def initialize io
815
+ super io
816
+ end
817
+
818
+
819
+ def select_for_reading?
820
+ true
821
+ end
822
+
823
+ #--
824
+ # accept_nonblock returns an array consisting of the accepted
825
+ # socket and a sockaddr_in which names the peer.
826
+ # Don't accept more than 10 at a time.
827
+ def eventable_read
828
+ begin
829
+ 10.times {
830
+ descriptor,peername = io.accept_nonblock
831
+ sd = StreamObject.new descriptor
832
+ EventMachine::event_callback uuid, ConnectionAccepted, sd.uuid
833
+ }
834
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN
835
+ end
836
+ end
837
+
838
+ #--
839
+ #
840
+ def schedule_close
841
+ @close_scheduled = true
842
+ end
567
843
 
568
844
  end
569
845
  end
@@ -623,6 +899,11 @@ module EventMachine
623
899
  true
624
900
  end
625
901
 
902
+ # #get_outbound_data_size
903
+ def get_outbound_data_size
904
+ @outbound_q.inject(0) {|memo,obj| memo += (obj || "").length}
905
+ end
906
+
626
907
 
627
908
  end
628
909