eventmachine 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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