eventmachine 0.12.10-x86-mswin32-60 → 1.0.0.beta.2-x86-mswin32-60

Sign up to get free protection for your applications and to get access to all the features.
Files changed (150) hide show
  1. data/.gitignore +2 -0
  2. data/Gemfile +1 -0
  3. data/README +80 -81
  4. data/Rakefile +7 -370
  5. data/docs/COPYING +60 -60
  6. data/docs/ChangeLog +211 -211
  7. data/docs/DEFERRABLES +246 -133
  8. data/docs/EPOLL +141 -141
  9. data/docs/GNU +281 -281
  10. data/docs/INSTALL +13 -13
  11. data/docs/KEYBOARD +42 -38
  12. data/docs/LEGAL +25 -25
  13. data/docs/LIGHTWEIGHT_CONCURRENCY +130 -70
  14. data/docs/PURE_RUBY +75 -75
  15. data/docs/RELEASE_NOTES +94 -94
  16. data/docs/SMTP +4 -2
  17. data/docs/SPAWNED_PROCESSES +148 -89
  18. data/docs/TODO +8 -8
  19. data/eventmachine.gemspec +19 -26
  20. data/examples/ex_channel.rb +42 -42
  21. data/examples/ex_queue.rb +2 -2
  22. data/examples/ex_tick_loop_array.rb +15 -0
  23. data/examples/ex_tick_loop_counter.rb +32 -0
  24. data/examples/helper.rb +1 -1
  25. data/ext/binder.cpp +0 -1
  26. data/ext/cmain.cpp +36 -25
  27. data/ext/ed.cpp +104 -113
  28. data/ext/ed.h +24 -30
  29. data/ext/em.cpp +349 -283
  30. data/ext/em.h +25 -29
  31. data/ext/eventmachine.h +5 -4
  32. data/ext/extconf.rb +58 -49
  33. data/ext/fastfilereader/extconf.rb +5 -3
  34. data/ext/fastfilereader/mapper.cpp +214 -214
  35. data/ext/fastfilereader/mapper.h +59 -59
  36. data/ext/fastfilereader/rubymain.cpp +127 -127
  37. data/ext/kb.cpp +1 -3
  38. data/ext/page.cpp +107 -107
  39. data/ext/page.h +51 -51
  40. data/ext/pipe.cpp +9 -11
  41. data/ext/project.h +12 -8
  42. data/ext/rubymain.cpp +138 -104
  43. data/java/.classpath +8 -8
  44. data/java/.project +17 -17
  45. data/java/src/com/rubyeventmachine/EmReactor.java +1 -0
  46. data/java/src/com/rubyeventmachine/EmReactorException.java +40 -40
  47. data/lib/em/buftok.rb +138 -138
  48. data/lib/em/callback.rb +25 -25
  49. data/lib/em/channel.rb +1 -1
  50. data/lib/em/connection.rb +6 -1
  51. data/lib/em/deferrable.rb +16 -2
  52. data/lib/em/file_watch.rb +53 -53
  53. data/lib/em/future.rb +61 -61
  54. data/lib/em/iterator.rb +270 -0
  55. data/lib/em/messages.rb +66 -66
  56. data/lib/em/process_watch.rb +43 -43
  57. data/lib/em/protocols.rb +1 -1
  58. data/lib/em/protocols/header_and_content.rb +138 -138
  59. data/lib/em/protocols/httpclient.rb +267 -262
  60. data/lib/em/protocols/line_protocol.rb +28 -0
  61. data/lib/em/protocols/memcache.rb +322 -322
  62. data/lib/em/protocols/postgres3.rb +247 -247
  63. data/lib/em/protocols/saslauth.rb +175 -175
  64. data/lib/em/protocols/smtpserver.rb +640 -547
  65. data/lib/em/protocols/stomp.rb +200 -200
  66. data/lib/em/protocols/tcptest.rb +52 -52
  67. data/lib/{pr_eventmachine.rb → em/pure_ruby.rb} +1013 -1022
  68. data/lib/em/queue.rb +1 -0
  69. data/lib/em/spawnable.rb +85 -85
  70. data/lib/em/streamer.rb +130 -130
  71. data/lib/em/tick_loop.rb +85 -0
  72. data/lib/em/timers.rb +2 -1
  73. data/lib/em/version.rb +1 -1
  74. data/lib/eventmachine.rb +40 -84
  75. data/lib/jeventmachine.rb +2 -1
  76. data/lib/rubyeventmachine.rb +2 -0
  77. data/setup.rb +1585 -1585
  78. data/tasks/doc.rake +30 -0
  79. data/tasks/package.rake +85 -0
  80. data/tasks/test.rake +6 -0
  81. data/tests/client.crt +31 -31
  82. data/tests/client.key +51 -51
  83. data/tests/test_attach.rb +13 -3
  84. data/tests/test_basic.rb +60 -95
  85. data/tests/test_channel.rb +3 -2
  86. data/tests/test_defer.rb +49 -47
  87. data/tests/test_deferrable.rb +35 -0
  88. data/tests/test_error_handler.rb +35 -35
  89. data/tests/test_errors.rb +82 -82
  90. data/tests/test_exc.rb +55 -55
  91. data/tests/test_file_watch.rb +49 -49
  92. data/tests/test_futures.rb +198 -198
  93. data/tests/test_handler_check.rb +36 -36
  94. data/tests/test_hc.rb +190 -218
  95. data/tests/test_httpclient.rb +227 -218
  96. data/tests/test_httpclient2.rb +3 -2
  97. data/tests/test_inactivity_timeout.rb +3 -3
  98. data/tests/test_kb.rb +60 -60
  99. data/tests/test_ltp.rb +13 -5
  100. data/tests/test_ltp2.rb +317 -317
  101. data/tests/test_next_tick.rb +1 -1
  102. data/tests/test_object_protocol.rb +36 -36
  103. data/tests/test_pending_connect_timeout.rb +2 -2
  104. data/tests/test_process_watch.rb +50 -48
  105. data/tests/test_proxy_connection.rb +52 -0
  106. data/tests/test_pure.rb +134 -125
  107. data/tests/test_queue.rb +44 -44
  108. data/tests/test_running.rb +42 -42
  109. data/tests/test_sasl.rb +72 -72
  110. data/tests/test_send_file.rb +251 -242
  111. data/tests/test_servers.rb +76 -76
  112. data/tests/test_smtpclient.rb +83 -83
  113. data/tests/test_smtpserver.rb +85 -85
  114. data/tests/test_spawn.rb +322 -322
  115. data/tests/test_ssl_methods.rb +49 -49
  116. data/tests/test_ssl_verify.rb +82 -82
  117. data/tests/test_tick_loop.rb +59 -0
  118. data/tests/test_timers.rb +13 -15
  119. data/tests/test_ud.rb +36 -36
  120. data/tests/testem.rb +31 -31
  121. metadata +66 -51
  122. data/ext/cplusplus.cpp +0 -202
  123. data/ext/emwin.cpp +0 -300
  124. data/ext/emwin.h +0 -94
  125. data/ext/epoll.cpp +0 -26
  126. data/ext/epoll.h +0 -25
  127. data/ext/eventmachine_cpp.h +0 -96
  128. data/ext/files.cpp +0 -94
  129. data/ext/files.h +0 -65
  130. data/ext/sigs.cpp +0 -89
  131. data/ext/sigs.h +0 -32
  132. data/java/src/com/rubyeventmachine/application/Application.java +0 -194
  133. data/java/src/com/rubyeventmachine/application/Connection.java +0 -74
  134. data/java/src/com/rubyeventmachine/application/ConnectionFactory.java +0 -37
  135. data/java/src/com/rubyeventmachine/application/DefaultConnectionFactory.java +0 -46
  136. data/java/src/com/rubyeventmachine/application/PeriodicTimer.java +0 -38
  137. data/java/src/com/rubyeventmachine/application/Timer.java +0 -54
  138. data/java/src/com/rubyeventmachine/tests/ApplicationTest.java +0 -109
  139. data/java/src/com/rubyeventmachine/tests/ConnectTest.java +0 -148
  140. data/java/src/com/rubyeventmachine/tests/EMTest.java +0 -80
  141. data/java/src/com/rubyeventmachine/tests/TestDatagrams.java +0 -53
  142. data/java/src/com/rubyeventmachine/tests/TestServers.java +0 -75
  143. data/java/src/com/rubyeventmachine/tests/TestTimers.java +0 -90
  144. data/lib/evma.rb +0 -32
  145. data/lib/evma/callback.rb +0 -32
  146. data/lib/evma/container.rb +0 -75
  147. data/lib/evma/factory.rb +0 -77
  148. data/lib/evma/protocol.rb +0 -87
  149. data/lib/evma/reactor.rb +0 -48
  150. data/web/whatis +0 -7
@@ -1,1022 +1,1013 @@
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
- # TODO List:
27
- # TCP-connects currently assume non-blocking connect is available- need to
28
- # degrade automatically on versions of Ruby prior to June 2006.
29
- #
30
-
31
- require 'singleton'
32
- require 'forwardable'
33
- require 'socket'
34
- require 'fcntl'
35
- require 'set'
36
-
37
-
38
- module EventMachine
39
-
40
-
41
- class << self
42
- # This is mostly useful for automated tests.
43
- # Return a distinctive symbol so the caller knows whether he's dealing
44
- # with an extension or with a pure-Ruby library.
45
- def library_type
46
- :pure_ruby
47
- end
48
-
49
- # #initialize_event_machine
50
- def initialize_event_machine
51
- Reactor.instance.initialize_for_run
52
- end
53
-
54
- # #add_oneshot_timer
55
- #--
56
- # Changed 04Oct06: intervals from the caller are now in milliseconds, but our native-ruby
57
- # processor still wants them in seconds.
58
- def add_oneshot_timer interval
59
- Reactor.instance.install_oneshot_timer(interval / 1000)
60
- end
61
-
62
- # run_machine
63
- def run_machine
64
- Reactor.instance.run
65
- end
66
-
67
- # release_machine. Probably a no-op.
68
- def release_machine
69
- end
70
-
71
- # #stop
72
- def stop
73
- Reactor.instance.stop
74
- end
75
-
76
- # #connect_server. Return a connection descriptor to the caller.
77
- # TODO, what do we return here if we can't connect?
78
- def connect_server host, port
79
- bind_connect_server nil, nil, host, port
80
- end
81
-
82
- def bind_connect_server bind_addr, bind_port, host, port
83
- EvmaTCPClient.connect(bind_addr, bind_port, host, port).uuid
84
- end
85
-
86
- # #send_data
87
- def send_data target, data, datalength
88
- selectable = Reactor.instance.get_selectable( target ) or raise "unknown send_data target"
89
- selectable.send_data data
90
- end
91
-
92
- # #close_connection
93
- # The extension version does NOT raise any kind of an error if an attempt is made
94
- # to close a non-existent connection. Not sure whether we should. For now, we'll
95
- # raise an error here in that case.
96
- def close_connection target, after_writing
97
- selectable = Reactor.instance.get_selectable( target ) or raise "unknown close_connection target"
98
- selectable.schedule_close after_writing
99
- end
100
-
101
- # #start_tcp_server
102
- def start_tcp_server host, port
103
- (s = EvmaTCPServer.start_server host, port) or raise "no acceptor"
104
- s.uuid
105
- end
106
-
107
- # #stop_tcp_server
108
- def stop_tcp_server sig
109
- s = Reactor.instance.get_selectable(sig)
110
- s.schedule_close
111
- end
112
-
113
- # #start_unix_server
114
- def start_unix_server chain
115
- (s = EvmaUNIXServer.start_server chain) or raise "no acceptor"
116
- s.uuid
117
- end
118
-
119
- # #connect_unix_server
120
- def connect_unix_server chain
121
- EvmaUNIXClient.connect(chain).uuid
122
- end
123
-
124
- # #signal_loopbreak
125
- def signal_loopbreak
126
- Reactor.instance.signal_loopbreak
127
- end
128
-
129
- # #get_peername
130
- def get_peername sig
131
- selectable = Reactor.instance.get_selectable( sig ) or raise "unknown get_peername target"
132
- selectable.get_peername
133
- end
134
-
135
- # #open_udp_socket
136
- def open_udp_socket host, port
137
- EvmaUDPSocket.create(host, port).uuid
138
- end
139
-
140
- # #send_datagram. This is currently only for UDP!
141
- # We need to make it work with unix-domain sockets as well.
142
- def send_datagram target, data, datalength, host, port
143
- selectable = Reactor.instance.get_selectable( target ) or raise "unknown send_data target"
144
- selectable.send_datagram data, Socket::pack_sockaddr_in(port, host)
145
- end
146
-
147
-
148
- # #set_timer_quantum in milliseconds. The underlying Reactor function wants a (possibly
149
- # fractional) number of seconds.
150
- def set_timer_quantum interval
151
- Reactor.instance.set_timer_quantum(( 1.0 * interval) / 1000.0)
152
- end
153
-
154
- # #epoll is a harmless no-op in the pure-Ruby implementation. This is intended to ensure
155
- # that user code behaves properly across different EM implementations.
156
- def epoll
157
- end
158
-
159
- # #ssl? is not implemented for pure-Ruby implementation
160
- def ssl?
161
- false
162
- end
163
-
164
- # #set_rlimit_nofile is a no-op in the pure-Ruby implementation. We simply return Ruby's built-in
165
- # per-process file-descriptor limit.
166
- def set_rlimit_nofile n
167
- 1024
168
- end
169
-
170
- # #set_max_timer_count is a harmless no-op in pure Ruby, which doesn't have a built-in limit
171
- # on the number of available timers.
172
- def set_max_timer_count n
173
- end
174
-
175
- # #send_file_data
176
- def send_file_data sig, filename
177
- sz = File.size(filename)
178
- raise "file too large" if sz > 32*1024
179
- data =
180
- begin
181
- File.read filename
182
- rescue
183
- ""
184
- end
185
- send_data sig, data, data.length
186
- end
187
-
188
- # #get_outbound_data_size
189
- #
190
- def get_outbound_data_size sig
191
- r = Reactor.instance.get_selectable( sig ) or raise "unknown get_outbound_data_size target"
192
- r.get_outbound_data_size
193
- end
194
-
195
- # #read_keyboard
196
- #
197
- def read_keyboard
198
- EvmaKeyboard.open.uuid
199
- end
200
-
201
- # #set_comm_inactivity_timeout
202
- #
203
- def set_comm_inactivity_timeout sig, tm
204
- r = Reactor.instance.get_selectable( sig ) or raise "unknown set_comm_inactivity_timeout target"
205
- r.set_inactivity_timeout tm
206
- end
207
- end
208
-
209
- end
210
-
211
-
212
- #-----------------------------------------------------------------
213
-
214
- module EventMachine
215
-
216
- class Error < Exception; end
217
-
218
- end
219
-
220
- #-----------------------------------------------------------------
221
-
222
- module EventMachine
223
- class Connection
224
- def get_outbound_data_size
225
- EventMachine::get_outbound_data_size @signature
226
- end
227
- end
228
- end
229
-
230
- #-----------------------------------------------------------------
231
-
232
- module EventMachine
233
-
234
- # Factored out so we can substitute other implementations
235
- # here if desired, such as the one in ActiveRBAC.
236
- module UuidGenerator
237
-
238
- def self.generate
239
- if @ix and @ix >= 10000
240
- @ix = nil
241
- @seed = nil
242
- end
243
-
244
- @seed ||= `uuidgen`.chomp.gsub(/-/,"")
245
- @ix ||= 0
246
-
247
- "#{@seed}#{@ix += 1}"
248
- end
249
-
250
- end
251
-
252
- end
253
-
254
- #-----------------------------------------------------------------
255
-
256
- module EventMachine
257
-
258
- TimerFired = 100
259
- ConnectionData = 101
260
- ConnectionUnbound = 102
261
- ConnectionAccepted = 103
262
- ConnectionCompleted = 104
263
- LoopbreakSignalled = 105
264
-
265
- end
266
-
267
- #-----------------------------------------------------------------
268
-
269
- module EventMachine
270
- class Reactor
271
- include Singleton
272
-
273
- HeartbeatInterval = 2
274
-
275
- attr_reader :current_loop_time
276
-
277
- def initialize
278
- initialize_for_run
279
- end
280
-
281
- #--
282
- # Replaced original implementation 05Dec07, was way too slow because of the sort.
283
- def install_oneshot_timer interval
284
- uuid = UuidGenerator::generate
285
- #@timers << [Time.now + interval, uuid]
286
- #@timers.sort! {|a,b| a.first <=> b.first}
287
- @timers.add([Time.now + interval, uuid])
288
- uuid
289
- end
290
-
291
- # Called before run, this is a good place to clear out arrays
292
- # with cruft that may be left over from a previous run.
293
- def initialize_for_run
294
- @running = false
295
- @stop_scheduled = false
296
- @selectables ||= {}; @selectables.clear
297
- @timers = SortedSet.new # []
298
- set_timer_quantum(0.1)
299
- @current_loop_time = Time.now
300
- @next_heartbeat = @current_loop_time + HeartbeatInterval
301
- end
302
-
303
- def add_selectable io
304
- @selectables[io.uuid] = io
305
- end
306
-
307
- def get_selectable uuid
308
- @selectables[uuid]
309
- end
310
-
311
- def run
312
- raise Error.new( "already running" ) if @running
313
- @running = true
314
-
315
- begin
316
- open_loopbreaker
317
-
318
- loop {
319
- @current_loop_time = Time.now
320
-
321
- break if @stop_scheduled
322
- run_timers
323
- break if @stop_scheduled
324
- crank_selectables
325
- break if @stop_scheduled
326
- run_heartbeats
327
- }
328
- ensure
329
- close_loopbreaker
330
- @selectables.each {|k, io| io.close}
331
- @selectables.clear
332
-
333
- @running = false
334
- end
335
-
336
- end
337
-
338
- def run_timers
339
- @timers.each {|t|
340
- if t.first <= @current_loop_time
341
- @timers.delete t
342
- EventMachine::event_callback "", TimerFired, t.last
343
- else
344
- break
345
- end
346
- }
347
- #while @timers.length > 0 and @timers.first.first <= now
348
- # t = @timers.shift
349
- # EventMachine::event_callback "", TimerFired, t.last
350
- #end
351
- end
352
-
353
- def run_heartbeats
354
- if @next_heartbeat <= @current_loop_time
355
- @next_heartbeat = @current_loop_time + HeartbeatInterval
356
- @selectables.each {|k,io| io.heartbeat}
357
- end
358
- end
359
-
360
- def crank_selectables
361
- #$stderr.write 'R'
362
-
363
- readers = @selectables.values.select {|io| io.select_for_reading?}
364
- writers = @selectables.values.select {|io| io.select_for_writing?}
365
-
366
- s = select( readers, writers, nil, @timer_quantum)
367
-
368
- s and s[1] and s[1].each {|w| w.eventable_write }
369
- s and s[0] and s[0].each {|r| r.eventable_read }
370
-
371
- @selectables.delete_if {|k,io|
372
- if io.close_scheduled?
373
- io.close
374
- true
375
- end
376
- }
377
- end
378
-
379
- # #stop
380
- def stop
381
- raise Error.new( "not running") unless @running
382
- @stop_scheduled = true
383
- end
384
-
385
- def open_loopbreaker
386
- # Can't use an IO.pipe because they can't be set nonselectable in Windows.
387
- # Pick a random localhost UDP port.
388
- #@loopbreak_writer.close if @loopbreak_writer
389
- #rd,@loopbreak_writer = IO.pipe
390
- @loopbreak_reader = UDPSocket.new
391
- @loopbreak_writer = UDPSocket.new
392
- bound = false
393
- 100.times {
394
- @loopbreak_port = rand(10000) + 40000
395
- begin
396
- @loopbreak_reader.bind "localhost", @loopbreak_port
397
- bound = true
398
- break
399
- rescue
400
- end
401
- }
402
- raise "Unable to bind Loopbreaker" unless bound
403
- LoopbreakReader.new(@loopbreak_reader)
404
- end
405
-
406
- def close_loopbreaker
407
- @loopbreak_writer.close
408
- @loopbreak_writer = nil
409
- end
410
-
411
- def signal_loopbreak
412
- #@loopbreak_writer.write '+' if @loopbreak_writer
413
- @loopbreak_writer.send('+',0,"localhost",@loopbreak_port) if @loopbreak_writer
414
- end
415
-
416
- def set_timer_quantum interval_in_seconds
417
- @timer_quantum = interval_in_seconds
418
- end
419
-
420
- end
421
-
422
- end
423
-
424
-
425
- #--------------------------------------------------------------
426
-
427
- class IO
428
- extend Forwardable
429
- def_delegator :@my_selectable, :close_scheduled?
430
- def_delegator :@my_selectable, :select_for_reading?
431
- def_delegator :@my_selectable, :select_for_writing?
432
- def_delegator :@my_selectable, :eventable_read
433
- def_delegator :@my_selectable, :eventable_write
434
- def_delegator :@my_selectable, :uuid
435
- def_delegator :@my_selectable, :send_data
436
- def_delegator :@my_selectable, :schedule_close
437
- def_delegator :@my_selectable, :get_peername
438
- def_delegator :@my_selectable, :send_datagram
439
- def_delegator :@my_selectable, :get_outbound_data_size
440
- def_delegator :@my_selectable, :set_inactivity_timeout
441
- def_delegator :@my_selectable, :heartbeat
442
- end
443
-
444
- #--------------------------------------------------------------
445
-
446
- module EventMachine
447
- class Selectable
448
-
449
- attr_reader :io, :uuid
450
-
451
- def initialize io
452
- @uuid = UuidGenerator.generate
453
- @io = io
454
- @last_activity = Reactor.instance.current_loop_time
455
-
456
- if defined?(Fcntl::F_GETFL)
457
- m = @io.fcntl(Fcntl::F_GETFL, 0)
458
- @io.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK | m)
459
- else
460
- # Windows doesn't define F_GETFL.
461
- # It's not very reliable about setting descriptors nonblocking either.
462
- begin
463
- s = Socket.for_fd(@io.fileno)
464
- s.fcntl( Fcntl::F_SETFL, Fcntl::O_NONBLOCK )
465
- rescue Errno::EINVAL, Errno::EBADF
466
- STDERR.puts "Serious error: unable to set descriptor non-blocking"
467
- end
468
- end
469
- # TODO, should set CLOEXEC on Unix?
470
-
471
- @close_scheduled = false
472
- @close_requested = false
473
-
474
- se = self; @io.instance_eval { @my_selectable = se }
475
- Reactor.instance.add_selectable @io
476
- end
477
-
478
- def close_scheduled?
479
- @close_scheduled
480
- end
481
-
482
- def select_for_reading?
483
- false
484
- end
485
-
486
- def select_for_writing?
487
- false
488
- end
489
-
490
- def get_peername
491
- nil
492
- end
493
-
494
- def set_inactivity_timeout tm
495
- @inactivity_timeout = tm
496
- end
497
-
498
- def heartbeat
499
- end
500
- end
501
-
502
- end
503
-
504
- #--------------------------------------------------------------
505
-
506
-
507
- module EventMachine
508
-
509
- class StreamObject < Selectable
510
- def initialize io
511
- super io
512
- @outbound_q = []
513
- end
514
-
515
- # If we have to close, or a close-after-writing has been requested,
516
- # then don't read any more data.
517
- def select_for_reading?
518
- true unless (@close_scheduled || @close_requested)
519
- end
520
-
521
- # If we have to close, don't select for writing.
522
- # Otherwise, see if the protocol is ready to close.
523
- # If not, see if he has data to send.
524
- # If a close-after-writing has been requested and the outbound queue
525
- # is empty, convert the status to close_scheduled.
526
- def select_for_writing?
527
- unless @close_scheduled
528
- if @outbound_q.empty?
529
- @close_scheduled = true if @close_requested
530
- false
531
- else
532
- true
533
- end
534
- end
535
- end
536
-
537
- # Proper nonblocking I/O was added to Ruby 1.8.4 in May 2006.
538
- # If we have it, then we can read multiple times safely to improve
539
- # performance.
540
- # The last-activity clock ASSUMES that we only come here when we
541
- # have selected readable.
542
- # TODO, coalesce multiple reads into a single event.
543
- # TODO, do the function check somewhere else and cache it.
544
- def eventable_read
545
- @last_activity = Reactor.instance.current_loop_time
546
- begin
547
- if io.respond_to?(:read_nonblock)
548
- 10.times {
549
- data = io.read_nonblock(4096)
550
- EventMachine::event_callback uuid, ConnectionData, data
551
- }
552
- else
553
- data = io.sysread(4096)
554
- EventMachine::event_callback uuid, ConnectionData, data
555
- end
556
- rescue Errno::EAGAIN, Errno::EWOULDBLOCK
557
- # no-op
558
- rescue Errno::ECONNRESET, Errno::ECONNREFUSED, EOFError
559
- @close_scheduled = true
560
- EventMachine::event_callback uuid, ConnectionUnbound, nil
561
- end
562
-
563
- end
564
-
565
- # Provisional implementation. Will be re-implemented in subclasses.
566
- # TODO: Complete this implementation. As it stands, this only writes
567
- # a single packet per cycle. Highly inefficient, but required unless
568
- # we're running on a Ruby with proper nonblocking I/O (Ruby 1.8.4
569
- # built from sources from May 25, 2006 or newer).
570
- # We need to improve the loop so it writes multiple times, however
571
- # not more than a certain number of bytes per cycle, otherwise
572
- # one busy connection could hog output buffers and slow down other
573
- # connections. Also we should coalesce small writes.
574
- # URGENT TODO: Coalesce small writes. They are a performance killer.
575
- # The last-activity recorder ASSUMES we'll only come here if we've
576
- # selected writable.
577
- def eventable_write
578
- # coalesce the outbound array here, perhaps
579
- @last_activity = Reactor.instance.current_loop_time
580
- while data = @outbound_q.shift do
581
- begin
582
- data = data.to_s
583
- w = if io.respond_to?(:write_nonblock)
584
- io.write_nonblock data
585
- else
586
- io.syswrite data
587
- end
588
-
589
- if w < data.length
590
- @outbound_q.unshift data[w..-1]
591
- break
592
- end
593
- rescue Errno::EAGAIN
594
- @outbound_q.unshift data
595
- rescue EOFError, Errno::ECONNRESET, Errno::ECONNREFUSED
596
- @close_scheduled = true
597
- @outbound_q.clear
598
- end
599
- end
600
-
601
- end
602
-
603
- # #send_data
604
- def send_data data
605
- # TODO, coalesce here perhaps by being smarter about appending to @outbound_q.last?
606
- unless @close_scheduled or @close_requested or !data or data.length <= 0
607
- @outbound_q << data.to_s
608
- end
609
- end
610
-
611
- # #schedule_close
612
- # The application wants to close the connection.
613
- def schedule_close after_writing
614
- if after_writing
615
- @close_requested = true
616
- else
617
- @close_scheduled = true
618
- end
619
- end
620
-
621
- # #get_peername
622
- # This is defined in the normal way on connected stream objects.
623
- # Return an object that is suitable for passing to Socket#unpack_sockaddr_in or variants.
624
- # We could also use a convenience method that did the unpacking automatically.
625
- def get_peername
626
- io.getpeername
627
- end
628
-
629
- # #get_outbound_data_size
630
- def get_outbound_data_size
631
- @outbound_q.inject(0) {|memo,obj| memo += (obj || "").length}
632
- end
633
-
634
- def heartbeat
635
- if @inactivity_timeout and @inactivity_timeout > 0 and (@last_activity + @inactivity_timeout) < Reactor.instance.current_loop_time
636
- schedule_close true
637
- end
638
- end
639
- end
640
-
641
-
642
- end
643
-
644
-
645
- #--------------------------------------------------------------
646
-
647
-
648
-
649
- module EventMachine
650
- class EvmaTCPClient < StreamObject
651
-
652
- def self.connect bind_addr, bind_port, host, port
653
- sd = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
654
- sd.bind( Socket.pack_sockaddr_in( bind_port, bind_addr )) if bind_addr
655
-
656
- begin
657
- # TODO, this assumes a current Ruby snapshot.
658
- # We need to degrade to a nonblocking connect otherwise.
659
- sd.connect_nonblock( Socket.pack_sockaddr_in( port, host ))
660
- rescue Errno::EINPROGRESS
661
- end
662
- EvmaTCPClient.new sd
663
- end
664
-
665
-
666
- def initialize io
667
- super
668
- @pending = true
669
- end
670
-
671
-
672
- def select_for_writing?
673
- @pending ? true : super
674
- end
675
-
676
- def select_for_reading?
677
- @pending ? false : super
678
- end
679
-
680
- def eventable_write
681
- if @pending
682
- @pending = false
683
- if 0 == io.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR).unpack("i").first
684
- EventMachine::event_callback uuid, ConnectionCompleted, ""
685
- end
686
- else
687
- super
688
- end
689
- end
690
-
691
-
692
-
693
- end
694
- end
695
-
696
- #--------------------------------------------------------------
697
-
698
-
699
-
700
- module EventMachine
701
- class EvmaKeyboard < StreamObject
702
-
703
- def self.open
704
- EvmaKeyboard.new STDIN
705
- end
706
-
707
-
708
- def initialize io
709
- super
710
- end
711
-
712
-
713
- def select_for_writing?
714
- false
715
- end
716
-
717
- def select_for_reading?
718
- true
719
- end
720
-
721
-
722
- end
723
- end
724
-
725
-
726
- #--------------------------------------------------------------
727
-
728
-
729
-
730
- module EventMachine
731
- class EvmaUNIXClient < StreamObject
732
-
733
- def self.connect chain
734
- sd = Socket.new( Socket::AF_LOCAL, Socket::SOCK_STREAM, 0 )
735
- begin
736
- # TODO, this assumes a current Ruby snapshot.
737
- # We need to degrade to a nonblocking connect otherwise.
738
- sd.connect_nonblock( Socket.pack_sockaddr_un( chain ))
739
- rescue Errno::EINPROGRESS
740
- end
741
- EvmaUNIXClient.new sd
742
- end
743
-
744
-
745
- def initialize io
746
- super
747
- @pending = true
748
- end
749
-
750
-
751
- def select_for_writing?
752
- @pending ? true : super
753
- end
754
-
755
- def select_for_reading?
756
- @pending ? false : super
757
- end
758
-
759
- def eventable_write
760
- if @pending
761
- @pending = false
762
- if 0 == io.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR).unpack("i").first
763
- EventMachine::event_callback uuid, ConnectionCompleted, ""
764
- end
765
- else
766
- super
767
- end
768
- end
769
-
770
-
771
-
772
- end
773
- end
774
-
775
-
776
- #--------------------------------------------------------------
777
-
778
- module EventMachine
779
- class EvmaTCPServer < Selectable
780
-
781
- # TODO, refactor and unify with EvmaUNIXServer.
782
-
783
- class << self
784
- # Versions of ruby 1.8.4 later than May 26 2006 will work properly
785
- # with an object of type TCPServer. Prior versions won't so we
786
- # play it safe and just build a socket.
787
- #
788
- def start_server host, port
789
- sd = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
790
- sd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true )
791
- sd.bind( Socket.pack_sockaddr_in( port, host ))
792
- sd.listen( 50 ) # 5 is what you see in all the books. Ain't enough.
793
- EvmaTCPServer.new sd
794
- end
795
- end
796
-
797
- def initialize io
798
- super io
799
- end
800
-
801
-
802
- def select_for_reading?
803
- true
804
- end
805
-
806
- #--
807
- # accept_nonblock returns an array consisting of the accepted
808
- # socket and a sockaddr_in which names the peer.
809
- # Don't accept more than 10 at a time.
810
- def eventable_read
811
- begin
812
- 10.times {
813
- descriptor,peername = io.accept_nonblock
814
- sd = StreamObject.new descriptor
815
- EventMachine::event_callback uuid, ConnectionAccepted, sd.uuid
816
- }
817
- rescue Errno::EWOULDBLOCK, Errno::EAGAIN
818
- end
819
- end
820
-
821
- #--
822
- #
823
- def schedule_close
824
- @close_scheduled = true
825
- end
826
-
827
- end
828
- end
829
-
830
-
831
- #--------------------------------------------------------------
832
-
833
- module EventMachine
834
- class EvmaUNIXServer < Selectable
835
-
836
- # TODO, refactor and unify with EvmaTCPServer.
837
-
838
- class << self
839
- # Versions of ruby 1.8.4 later than May 26 2006 will work properly
840
- # with an object of type TCPServer. Prior versions won't so we
841
- # play it safe and just build a socket.
842
- #
843
- def start_server chain
844
- sd = Socket.new( Socket::AF_LOCAL, Socket::SOCK_STREAM, 0 )
845
- sd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true )
846
- sd.bind( Socket.pack_sockaddr_un( chain ))
847
- sd.listen( 50 ) # 5 is what you see in all the books. Ain't enough.
848
- EvmaUNIXServer.new sd
849
- end
850
- end
851
-
852
- def initialize io
853
- super io
854
- end
855
-
856
-
857
- def select_for_reading?
858
- true
859
- end
860
-
861
- #--
862
- # accept_nonblock returns an array consisting of the accepted
863
- # socket and a sockaddr_in which names the peer.
864
- # Don't accept more than 10 at a time.
865
- def eventable_read
866
- begin
867
- 10.times {
868
- descriptor,peername = io.accept_nonblock
869
- sd = StreamObject.new descriptor
870
- EventMachine::event_callback uuid, ConnectionAccepted, sd.uuid
871
- }
872
- rescue Errno::EWOULDBLOCK, Errno::EAGAIN
873
- end
874
- end
875
-
876
- #--
877
- #
878
- def schedule_close
879
- @close_scheduled = true
880
- end
881
-
882
- end
883
- end
884
-
885
-
886
-
887
- #--------------------------------------------------------------
888
-
889
- module EventMachine
890
- class LoopbreakReader < Selectable
891
-
892
- def select_for_reading?
893
- true
894
- end
895
-
896
- def eventable_read
897
- io.sysread(128)
898
- EventMachine::event_callback "", LoopbreakSignalled, ""
899
- end
900
-
901
- end
902
- end
903
-
904
- #--------------------------------------------------------------
905
-
906
-
907
- module EventMachine
908
-
909
- class DatagramObject < Selectable
910
- def initialize io
911
- super io
912
- @outbound_q = []
913
- end
914
-
915
- # #send_datagram
916
- def send_datagram data, target
917
- # TODO, coalesce here perhaps by being smarter about appending to @outbound_q.last?
918
- unless @close_scheduled or @close_requested
919
- @outbound_q << [data.to_s, target]
920
- end
921
- end
922
-
923
- # #select_for_writing?
924
- def select_for_writing?
925
- unless @close_scheduled
926
- if @outbound_q.empty?
927
- @close_scheduled = true if @close_requested
928
- false
929
- else
930
- true
931
- end
932
- end
933
- end
934
-
935
- # #select_for_reading?
936
- def select_for_reading?
937
- true
938
- end
939
-
940
- # #get_outbound_data_size
941
- def get_outbound_data_size
942
- @outbound_q.inject(0) {|memo,obj| memo += (obj || "").length}
943
- end
944
-
945
-
946
- end
947
-
948
-
949
- end
950
-
951
-
952
- #--------------------------------------------------------------
953
-
954
- module EventMachine
955
- class EvmaUDPSocket < DatagramObject
956
-
957
- class << self
958
- def create host, port
959
- sd = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
960
- sd.bind Socket::pack_sockaddr_in( port, host )
961
- EvmaUDPSocket.new sd
962
- end
963
- end
964
-
965
- # #eventable_write
966
- # This really belongs in DatagramObject, but there is some UDP-specific stuff.
967
- def eventable_write
968
- 40.times {
969
- break if @outbound_q.empty?
970
- begin
971
- data,target = @outbound_q.first
972
-
973
- # This damn better be nonblocking.
974
- io.send data.to_s, 0, target
975
-
976
- @outbound_q.shift
977
- rescue Errno::EAGAIN
978
- # It's not been observed in testing that we ever get here.
979
- # True to the definition, packets will be accepted and quietly dropped
980
- # if the system is under pressure.
981
- break
982
- rescue EOFError, Errno::ECONNRESET
983
- @close_scheduled = true
984
- @outbound_q.clear
985
- end
986
- }
987
- end
988
-
989
- # Proper nonblocking I/O was added to Ruby 1.8.4 in May 2006.
990
- # If we have it, then we can read multiple times safely to improve
991
- # performance.
992
- def eventable_read
993
- begin
994
- if io.respond_to?(:recvfrom_nonblock)
995
- 40.times {
996
- data,@return_address = io.recvfrom_nonblock(16384)
997
- EventMachine::event_callback uuid, ConnectionData, data
998
- @return_address = nil
999
- }
1000
- else
1001
- raise "unimplemented datagram-read operation on this Ruby"
1002
- end
1003
- rescue Errno::EAGAIN
1004
- # no-op
1005
- rescue Errno::ECONNRESET, EOFError
1006
- @close_scheduled = true
1007
- EventMachine::event_callback uuid, ConnectionUnbound, nil
1008
- end
1009
-
1010
- end
1011
-
1012
-
1013
- def send_data data
1014
- send_datagram data, @return_address
1015
- end
1016
-
1017
- end
1018
- end
1019
-
1020
- #--------------------------------------------------------------
1021
-
1022
-
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
+ # TODO List:
27
+ # TCP-connects currently assume non-blocking connect is available- need to
28
+ # degrade automatically on versions of Ruby prior to June 2006.
29
+ #
30
+
31
+ require 'singleton'
32
+ require 'forwardable'
33
+ require 'socket'
34
+ require 'fcntl'
35
+ require 'set'
36
+
37
+ module EventMachine
38
+ class << self
39
+ # This is mostly useful for automated tests.
40
+ # Return a distinctive symbol so the caller knows whether he's dealing
41
+ # with an extension or with a pure-Ruby library.
42
+ def library_type
43
+ :pure_ruby
44
+ end
45
+
46
+ # #initialize_event_machine
47
+ def initialize_event_machine
48
+ Reactor.instance.initialize_for_run
49
+ end
50
+
51
+ # #add_oneshot_timer
52
+ #--
53
+ # Changed 04Oct06: intervals from the caller are now in milliseconds, but our native-ruby
54
+ # processor still wants them in seconds.
55
+ def add_oneshot_timer interval
56
+ Reactor.instance.install_oneshot_timer(interval / 1000)
57
+ end
58
+
59
+ # run_machine
60
+ def run_machine
61
+ Reactor.instance.run
62
+ end
63
+
64
+ # release_machine. Probably a no-op.
65
+ def release_machine
66
+ end
67
+
68
+ # #stop
69
+ def stop
70
+ Reactor.instance.stop
71
+ end
72
+
73
+ # #connect_server. Return a connection descriptor to the caller.
74
+ # TODO, what do we return here if we can't connect?
75
+ def connect_server host, port
76
+ bind_connect_server nil, nil, host, port
77
+ end
78
+
79
+ def bind_connect_server bind_addr, bind_port, host, port
80
+ EvmaTCPClient.connect(bind_addr, bind_port, host, port).uuid
81
+ end
82
+
83
+ # #send_data
84
+ def send_data target, data, datalength
85
+ selectable = Reactor.instance.get_selectable( target ) or raise "unknown send_data target"
86
+ selectable.send_data data
87
+ end
88
+
89
+ # #close_connection
90
+ # The extension version does NOT raise any kind of an error if an attempt is made
91
+ # to close a non-existent connection. Not sure whether we should. For now, we'll
92
+ # raise an error here in that case.
93
+ def close_connection target, after_writing
94
+ selectable = Reactor.instance.get_selectable( target ) or raise "unknown close_connection target"
95
+ selectable.schedule_close after_writing
96
+ end
97
+
98
+ # #start_tcp_server
99
+ def start_tcp_server host, port
100
+ (s = EvmaTCPServer.start_server host, port) or raise "no acceptor"
101
+ s.uuid
102
+ end
103
+
104
+ # #stop_tcp_server
105
+ def stop_tcp_server sig
106
+ s = Reactor.instance.get_selectable(sig)
107
+ s.schedule_close
108
+ end
109
+
110
+ # #start_unix_server
111
+ def start_unix_server chain
112
+ (s = EvmaUNIXServer.start_server chain) or raise "no acceptor"
113
+ s.uuid
114
+ end
115
+
116
+ # #connect_unix_server
117
+ def connect_unix_server chain
118
+ EvmaUNIXClient.connect(chain).uuid
119
+ end
120
+
121
+ # #signal_loopbreak
122
+ def signal_loopbreak
123
+ Reactor.instance.signal_loopbreak
124
+ end
125
+
126
+ # #get_peername
127
+ def get_peername sig
128
+ selectable = Reactor.instance.get_selectable( sig ) or raise "unknown get_peername target"
129
+ selectable.get_peername
130
+ end
131
+
132
+ # #open_udp_socket
133
+ def open_udp_socket host, port
134
+ EvmaUDPSocket.create(host, port).uuid
135
+ end
136
+
137
+ # #send_datagram. This is currently only for UDP!
138
+ # We need to make it work with unix-domain sockets as well.
139
+ def send_datagram target, data, datalength, host, port
140
+ selectable = Reactor.instance.get_selectable( target ) or raise "unknown send_data target"
141
+ selectable.send_datagram data, Socket::pack_sockaddr_in(port, host)
142
+ end
143
+
144
+
145
+ # #set_timer_quantum in milliseconds. The underlying Reactor function wants a (possibly
146
+ # fractional) number of seconds.
147
+ def set_timer_quantum interval
148
+ Reactor.instance.set_timer_quantum(( 1.0 * interval) / 1000.0)
149
+ end
150
+
151
+ # #epoll is a harmless no-op in the pure-Ruby implementation. This is intended to ensure
152
+ # that user code behaves properly across different EM implementations.
153
+ def epoll
154
+ end
155
+
156
+ # #ssl? is not implemented for pure-Ruby implementation
157
+ def ssl?
158
+ false
159
+ end
160
+
161
+ # #set_rlimit_nofile is a no-op in the pure-Ruby implementation. We simply return Ruby's built-in
162
+ # per-process file-descriptor limit.
163
+ def set_rlimit_nofile n
164
+ 1024
165
+ end
166
+
167
+ # #set_max_timer_count is a harmless no-op in pure Ruby, which doesn't have a built-in limit
168
+ # on the number of available timers.
169
+ def set_max_timer_count n
170
+ end
171
+
172
+ # #send_file_data
173
+ def send_file_data sig, filename
174
+ sz = File.size(filename)
175
+ raise "file too large" if sz > 32*1024
176
+ data =
177
+ begin
178
+ File.read filename
179
+ rescue
180
+ ""
181
+ end
182
+ send_data sig, data, data.length
183
+ end
184
+
185
+ # #get_outbound_data_size
186
+ #
187
+ def get_outbound_data_size sig
188
+ r = Reactor.instance.get_selectable( sig ) or raise "unknown get_outbound_data_size target"
189
+ r.get_outbound_data_size
190
+ end
191
+
192
+ # #read_keyboard
193
+ #
194
+ def read_keyboard
195
+ EvmaKeyboard.open.uuid
196
+ end
197
+
198
+ # #set_comm_inactivity_timeout
199
+ #
200
+ def set_comm_inactivity_timeout sig, tm
201
+ r = Reactor.instance.get_selectable( sig ) or raise "unknown set_comm_inactivity_timeout target"
202
+ r.set_inactivity_timeout tm
203
+ end
204
+ end
205
+
206
+ end
207
+
208
+
209
+ #-----------------------------------------------------------------
210
+
211
+ module EventMachine
212
+
213
+ class Error < Exception; end
214
+
215
+ end
216
+
217
+ #-----------------------------------------------------------------
218
+
219
+ module EventMachine
220
+ class Connection
221
+ def get_outbound_data_size
222
+ EventMachine::get_outbound_data_size @signature
223
+ end
224
+ end
225
+ end
226
+
227
+ #-----------------------------------------------------------------
228
+
229
+ module EventMachine
230
+
231
+ # Factored out so we can substitute other implementations
232
+ # here if desired, such as the one in ActiveRBAC.
233
+ module UuidGenerator
234
+
235
+ def self.generate
236
+ @ix ||= 0
237
+ @ix += 1
238
+ end
239
+
240
+ end
241
+
242
+ end
243
+
244
+ #-----------------------------------------------------------------
245
+
246
+ module EventMachine
247
+
248
+ TimerFired = 100
249
+ ConnectionData = 101
250
+ ConnectionUnbound = 102
251
+ ConnectionAccepted = 103
252
+ ConnectionCompleted = 104
253
+ LoopbreakSignalled = 105
254
+
255
+ end
256
+
257
+ #-----------------------------------------------------------------
258
+
259
+ module EventMachine
260
+ class Reactor
261
+ include Singleton
262
+
263
+ HeartbeatInterval = 2
264
+
265
+ attr_reader :current_loop_time
266
+
267
+ def initialize
268
+ initialize_for_run
269
+ end
270
+
271
+ #--
272
+ # Replaced original implementation 05Dec07, was way too slow because of the sort.
273
+ def install_oneshot_timer interval
274
+ uuid = UuidGenerator::generate
275
+ #@timers << [Time.now + interval, uuid]
276
+ #@timers.sort! {|a,b| a.first <=> b.first}
277
+ @timers.add([Time.now + interval, uuid])
278
+ uuid
279
+ end
280
+
281
+ # Called before run, this is a good place to clear out arrays
282
+ # with cruft that may be left over from a previous run.
283
+ def initialize_for_run
284
+ @running = false
285
+ @stop_scheduled = false
286
+ @selectables ||= {}; @selectables.clear
287
+ @timers = SortedSet.new # []
288
+ set_timer_quantum(0.1)
289
+ @current_loop_time = Time.now
290
+ @next_heartbeat = @current_loop_time + HeartbeatInterval
291
+ end
292
+
293
+ def add_selectable io
294
+ @selectables[io.uuid] = io
295
+ end
296
+
297
+ def get_selectable uuid
298
+ @selectables[uuid]
299
+ end
300
+
301
+ def run
302
+ raise Error.new( "already running" ) if @running
303
+ @running = true
304
+
305
+ begin
306
+ open_loopbreaker
307
+
308
+ loop {
309
+ @current_loop_time = Time.now
310
+
311
+ break if @stop_scheduled
312
+ run_timers
313
+ break if @stop_scheduled
314
+ crank_selectables
315
+ break if @stop_scheduled
316
+ run_heartbeats
317
+ }
318
+ ensure
319
+ close_loopbreaker
320
+ @selectables.each {|k, io| io.close}
321
+ @selectables.clear
322
+
323
+ @running = false
324
+ end
325
+
326
+ end
327
+
328
+ def run_timers
329
+ @timers.each {|t|
330
+ if t.first <= @current_loop_time
331
+ @timers.delete t
332
+ EventMachine::event_callback "", TimerFired, t.last
333
+ else
334
+ break
335
+ end
336
+ }
337
+ #while @timers.length > 0 and @timers.first.first <= now
338
+ # t = @timers.shift
339
+ # EventMachine::event_callback "", TimerFired, t.last
340
+ #end
341
+ end
342
+
343
+ def run_heartbeats
344
+ if @next_heartbeat <= @current_loop_time
345
+ @next_heartbeat = @current_loop_time + HeartbeatInterval
346
+ @selectables.each {|k,io| io.heartbeat}
347
+ end
348
+ end
349
+
350
+ def crank_selectables
351
+ #$stderr.write 'R'
352
+
353
+ readers = @selectables.values.select {|io| io.select_for_reading?}
354
+ writers = @selectables.values.select {|io| io.select_for_writing?}
355
+
356
+ s = select( readers, writers, nil, @timer_quantum)
357
+
358
+ s and s[1] and s[1].each {|w| w.eventable_write }
359
+ s and s[0] and s[0].each {|r| r.eventable_read }
360
+
361
+ @selectables.delete_if {|k,io|
362
+ if io.close_scheduled?
363
+ io.close
364
+ true
365
+ end
366
+ }
367
+ end
368
+
369
+ # #stop
370
+ def stop
371
+ raise Error.new( "not running") unless @running
372
+ @stop_scheduled = true
373
+ end
374
+
375
+ def open_loopbreaker
376
+ # Can't use an IO.pipe because they can't be set nonselectable in Windows.
377
+ # Pick a random localhost UDP port.
378
+ #@loopbreak_writer.close if @loopbreak_writer
379
+ #rd,@loopbreak_writer = IO.pipe
380
+ @loopbreak_reader = UDPSocket.new
381
+ @loopbreak_writer = UDPSocket.new
382
+ bound = false
383
+ 100.times {
384
+ @loopbreak_port = rand(10000) + 40000
385
+ begin
386
+ @loopbreak_reader.bind "localhost", @loopbreak_port
387
+ bound = true
388
+ break
389
+ rescue
390
+ end
391
+ }
392
+ raise "Unable to bind Loopbreaker" unless bound
393
+ LoopbreakReader.new(@loopbreak_reader)
394
+ end
395
+
396
+ def close_loopbreaker
397
+ @loopbreak_writer.close
398
+ @loopbreak_writer = nil
399
+ end
400
+
401
+ def signal_loopbreak
402
+ #@loopbreak_writer.write '+' if @loopbreak_writer
403
+ @loopbreak_writer.send('+',0,"localhost",@loopbreak_port) if @loopbreak_writer
404
+ end
405
+
406
+ def set_timer_quantum interval_in_seconds
407
+ @timer_quantum = interval_in_seconds
408
+ end
409
+
410
+ end
411
+
412
+ end
413
+
414
+
415
+ #--------------------------------------------------------------
416
+
417
+ class IO
418
+ extend Forwardable
419
+ def_delegator :@my_selectable, :close_scheduled?
420
+ def_delegator :@my_selectable, :select_for_reading?
421
+ def_delegator :@my_selectable, :select_for_writing?
422
+ def_delegator :@my_selectable, :eventable_read
423
+ def_delegator :@my_selectable, :eventable_write
424
+ def_delegator :@my_selectable, :uuid
425
+ def_delegator :@my_selectable, :send_data
426
+ def_delegator :@my_selectable, :schedule_close
427
+ def_delegator :@my_selectable, :get_peername
428
+ def_delegator :@my_selectable, :send_datagram
429
+ def_delegator :@my_selectable, :get_outbound_data_size
430
+ def_delegator :@my_selectable, :set_inactivity_timeout
431
+ def_delegator :@my_selectable, :heartbeat
432
+ end
433
+
434
+ #--------------------------------------------------------------
435
+
436
+ module EventMachine
437
+ class Selectable
438
+
439
+ attr_reader :io, :uuid
440
+
441
+ def initialize io
442
+ @uuid = UuidGenerator.generate
443
+ @io = io
444
+ @last_activity = Reactor.instance.current_loop_time
445
+
446
+ if defined?(Fcntl::F_GETFL)
447
+ m = @io.fcntl(Fcntl::F_GETFL, 0)
448
+ @io.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK | m)
449
+ else
450
+ # Windows doesn't define F_GETFL.
451
+ # It's not very reliable about setting descriptors nonblocking either.
452
+ begin
453
+ s = Socket.for_fd(@io.fileno)
454
+ s.fcntl( Fcntl::F_SETFL, Fcntl::O_NONBLOCK )
455
+ rescue Errno::EINVAL, Errno::EBADF
456
+ STDERR.puts "Serious error: unable to set descriptor non-blocking"
457
+ end
458
+ end
459
+ # TODO, should set CLOEXEC on Unix?
460
+
461
+ @close_scheduled = false
462
+ @close_requested = false
463
+
464
+ se = self; @io.instance_eval { @my_selectable = se }
465
+ Reactor.instance.add_selectable @io
466
+ end
467
+
468
+ def close_scheduled?
469
+ @close_scheduled
470
+ end
471
+
472
+ def select_for_reading?
473
+ false
474
+ end
475
+
476
+ def select_for_writing?
477
+ false
478
+ end
479
+
480
+ def get_peername
481
+ nil
482
+ end
483
+
484
+ def set_inactivity_timeout tm
485
+ @inactivity_timeout = tm
486
+ end
487
+
488
+ def heartbeat
489
+ end
490
+ end
491
+
492
+ end
493
+
494
+ #--------------------------------------------------------------
495
+
496
+
497
+ module EventMachine
498
+
499
+ class StreamObject < Selectable
500
+ def initialize io
501
+ super io
502
+ @outbound_q = []
503
+ end
504
+
505
+ # If we have to close, or a close-after-writing has been requested,
506
+ # then don't read any more data.
507
+ def select_for_reading?
508
+ true unless (@close_scheduled || @close_requested)
509
+ end
510
+
511
+ # If we have to close, don't select for writing.
512
+ # Otherwise, see if the protocol is ready to close.
513
+ # If not, see if he has data to send.
514
+ # If a close-after-writing has been requested and the outbound queue
515
+ # is empty, convert the status to close_scheduled.
516
+ def select_for_writing?
517
+ unless @close_scheduled
518
+ if @outbound_q.empty?
519
+ @close_scheduled = true if @close_requested
520
+ false
521
+ else
522
+ true
523
+ end
524
+ end
525
+ end
526
+
527
+ # Proper nonblocking I/O was added to Ruby 1.8.4 in May 2006.
528
+ # If we have it, then we can read multiple times safely to improve
529
+ # performance.
530
+ # The last-activity clock ASSUMES that we only come here when we
531
+ # have selected readable.
532
+ # TODO, coalesce multiple reads into a single event.
533
+ # TODO, do the function check somewhere else and cache it.
534
+ def eventable_read
535
+ @last_activity = Reactor.instance.current_loop_time
536
+ begin
537
+ if io.respond_to?(:read_nonblock)
538
+ 10.times {
539
+ data = io.read_nonblock(4096)
540
+ EventMachine::event_callback uuid, ConnectionData, data
541
+ }
542
+ else
543
+ data = io.sysread(4096)
544
+ EventMachine::event_callback uuid, ConnectionData, data
545
+ end
546
+ rescue Errno::EAGAIN, Errno::EWOULDBLOCK
547
+ # no-op
548
+ rescue Errno::ECONNRESET, Errno::ECONNREFUSED, EOFError
549
+ @close_scheduled = true
550
+ EventMachine::event_callback uuid, ConnectionUnbound, nil
551
+ end
552
+
553
+ end
554
+
555
+ # Provisional implementation. Will be re-implemented in subclasses.
556
+ # TODO: Complete this implementation. As it stands, this only writes
557
+ # a single packet per cycle. Highly inefficient, but required unless
558
+ # we're running on a Ruby with proper nonblocking I/O (Ruby 1.8.4
559
+ # built from sources from May 25, 2006 or newer).
560
+ # We need to improve the loop so it writes multiple times, however
561
+ # not more than a certain number of bytes per cycle, otherwise
562
+ # one busy connection could hog output buffers and slow down other
563
+ # connections. Also we should coalesce small writes.
564
+ # URGENT TODO: Coalesce small writes. They are a performance killer.
565
+ # The last-activity recorder ASSUMES we'll only come here if we've
566
+ # selected writable.
567
+ def eventable_write
568
+ # coalesce the outbound array here, perhaps
569
+ @last_activity = Reactor.instance.current_loop_time
570
+ while data = @outbound_q.shift do
571
+ begin
572
+ data = data.to_s
573
+ w = if io.respond_to?(:write_nonblock)
574
+ io.write_nonblock data
575
+ else
576
+ io.syswrite data
577
+ end
578
+
579
+ if w < data.length
580
+ @outbound_q.unshift data[w..-1]
581
+ break
582
+ end
583
+ rescue Errno::EAGAIN
584
+ @outbound_q.unshift data
585
+ rescue EOFError, Errno::ECONNRESET, Errno::ECONNREFUSED
586
+ @close_scheduled = true
587
+ @outbound_q.clear
588
+ end
589
+ end
590
+
591
+ end
592
+
593
+ # #send_data
594
+ def send_data data
595
+ # TODO, coalesce here perhaps by being smarter about appending to @outbound_q.last?
596
+ unless @close_scheduled or @close_requested or !data or data.length <= 0
597
+ @outbound_q << data.to_s
598
+ end
599
+ end
600
+
601
+ # #schedule_close
602
+ # The application wants to close the connection.
603
+ def schedule_close after_writing
604
+ if after_writing
605
+ @close_requested = true
606
+ else
607
+ @close_scheduled = true
608
+ end
609
+ end
610
+
611
+ # #get_peername
612
+ # This is defined in the normal way on connected stream objects.
613
+ # Return an object that is suitable for passing to Socket#unpack_sockaddr_in or variants.
614
+ # We could also use a convenience method that did the unpacking automatically.
615
+ def get_peername
616
+ io.getpeername
617
+ end
618
+
619
+ # #get_outbound_data_size
620
+ def get_outbound_data_size
621
+ @outbound_q.inject(0) {|memo,obj| memo += (obj || "").length}
622
+ end
623
+
624
+ def heartbeat
625
+ if @inactivity_timeout and @inactivity_timeout > 0 and (@last_activity + @inactivity_timeout) < Reactor.instance.current_loop_time
626
+ schedule_close true
627
+ end
628
+ end
629
+ end
630
+
631
+
632
+ end
633
+
634
+
635
+ #--------------------------------------------------------------
636
+
637
+
638
+
639
+ module EventMachine
640
+ class EvmaTCPClient < StreamObject
641
+
642
+ def self.connect bind_addr, bind_port, host, port
643
+ sd = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
644
+ sd.bind( Socket.pack_sockaddr_in( bind_port, bind_addr )) if bind_addr
645
+
646
+ begin
647
+ # TODO, this assumes a current Ruby snapshot.
648
+ # We need to degrade to a nonblocking connect otherwise.
649
+ sd.connect_nonblock( Socket.pack_sockaddr_in( port, host ))
650
+ rescue Errno::EINPROGRESS
651
+ end
652
+ EvmaTCPClient.new sd
653
+ end
654
+
655
+
656
+ def initialize io
657
+ super
658
+ @pending = true
659
+ end
660
+
661
+
662
+ def select_for_writing?
663
+ @pending ? true : super
664
+ end
665
+
666
+ def select_for_reading?
667
+ @pending ? false : super
668
+ end
669
+
670
+ def eventable_write
671
+ if @pending
672
+ @pending = false
673
+ if 0 == io.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR).unpack("i").first
674
+ EventMachine::event_callback uuid, ConnectionCompleted, ""
675
+ end
676
+ else
677
+ super
678
+ end
679
+ end
680
+
681
+
682
+
683
+ end
684
+ end
685
+
686
+ #--------------------------------------------------------------
687
+
688
+
689
+
690
+ module EventMachine
691
+ class EvmaKeyboard < StreamObject
692
+
693
+ def self.open
694
+ EvmaKeyboard.new STDIN
695
+ end
696
+
697
+
698
+ def initialize io
699
+ super
700
+ end
701
+
702
+
703
+ def select_for_writing?
704
+ false
705
+ end
706
+
707
+ def select_for_reading?
708
+ true
709
+ end
710
+
711
+
712
+ end
713
+ end
714
+
715
+
716
+ #--------------------------------------------------------------
717
+
718
+
719
+
720
+ module EventMachine
721
+ class EvmaUNIXClient < StreamObject
722
+
723
+ def self.connect chain
724
+ sd = Socket.new( Socket::AF_LOCAL, Socket::SOCK_STREAM, 0 )
725
+ begin
726
+ # TODO, this assumes a current Ruby snapshot.
727
+ # We need to degrade to a nonblocking connect otherwise.
728
+ sd.connect_nonblock( Socket.pack_sockaddr_un( chain ))
729
+ rescue Errno::EINPROGRESS
730
+ end
731
+ EvmaUNIXClient.new sd
732
+ end
733
+
734
+
735
+ def initialize io
736
+ super
737
+ @pending = true
738
+ end
739
+
740
+
741
+ def select_for_writing?
742
+ @pending ? true : super
743
+ end
744
+
745
+ def select_for_reading?
746
+ @pending ? false : super
747
+ end
748
+
749
+ def eventable_write
750
+ if @pending
751
+ @pending = false
752
+ if 0 == io.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR).unpack("i").first
753
+ EventMachine::event_callback uuid, ConnectionCompleted, ""
754
+ end
755
+ else
756
+ super
757
+ end
758
+ end
759
+
760
+
761
+
762
+ end
763
+ end
764
+
765
+
766
+ #--------------------------------------------------------------
767
+
768
+ module EventMachine
769
+ class EvmaTCPServer < Selectable
770
+
771
+ # TODO, refactor and unify with EvmaUNIXServer.
772
+
773
+ class << self
774
+ # Versions of ruby 1.8.4 later than May 26 2006 will work properly
775
+ # with an object of type TCPServer. Prior versions won't so we
776
+ # play it safe and just build a socket.
777
+ #
778
+ def start_server host, port
779
+ sd = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
780
+ sd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true )
781
+ sd.bind( Socket.pack_sockaddr_in( port, host ))
782
+ sd.listen( 50 ) # 5 is what you see in all the books. Ain't enough.
783
+ EvmaTCPServer.new sd
784
+ end
785
+ end
786
+
787
+ def initialize io
788
+ super io
789
+ end
790
+
791
+
792
+ def select_for_reading?
793
+ true
794
+ end
795
+
796
+ #--
797
+ # accept_nonblock returns an array consisting of the accepted
798
+ # socket and a sockaddr_in which names the peer.
799
+ # Don't accept more than 10 at a time.
800
+ def eventable_read
801
+ begin
802
+ 10.times {
803
+ descriptor,peername = io.accept_nonblock
804
+ sd = StreamObject.new descriptor
805
+ EventMachine::event_callback uuid, ConnectionAccepted, sd.uuid
806
+ }
807
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN
808
+ end
809
+ end
810
+
811
+ #--
812
+ #
813
+ def schedule_close
814
+ @close_scheduled = true
815
+ end
816
+
817
+ end
818
+ end
819
+
820
+
821
+ #--------------------------------------------------------------
822
+
823
+ module EventMachine
824
+ class EvmaUNIXServer < Selectable
825
+
826
+ # TODO, refactor and unify with EvmaTCPServer.
827
+
828
+ class << self
829
+ # Versions of ruby 1.8.4 later than May 26 2006 will work properly
830
+ # with an object of type TCPServer. Prior versions won't so we
831
+ # play it safe and just build a socket.
832
+ #
833
+ def start_server chain
834
+ sd = Socket.new( Socket::AF_LOCAL, Socket::SOCK_STREAM, 0 )
835
+ sd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true )
836
+ sd.bind( Socket.pack_sockaddr_un( chain ))
837
+ sd.listen( 50 ) # 5 is what you see in all the books. Ain't enough.
838
+ EvmaUNIXServer.new sd
839
+ end
840
+ end
841
+
842
+ def initialize io
843
+ super io
844
+ end
845
+
846
+
847
+ def select_for_reading?
848
+ true
849
+ end
850
+
851
+ #--
852
+ # accept_nonblock returns an array consisting of the accepted
853
+ # socket and a sockaddr_in which names the peer.
854
+ # Don't accept more than 10 at a time.
855
+ def eventable_read
856
+ begin
857
+ 10.times {
858
+ descriptor,peername = io.accept_nonblock
859
+ sd = StreamObject.new descriptor
860
+ EventMachine::event_callback uuid, ConnectionAccepted, sd.uuid
861
+ }
862
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN
863
+ end
864
+ end
865
+
866
+ #--
867
+ #
868
+ def schedule_close
869
+ @close_scheduled = true
870
+ end
871
+
872
+ end
873
+ end
874
+
875
+
876
+
877
+ #--------------------------------------------------------------
878
+
879
+ module EventMachine
880
+ class LoopbreakReader < Selectable
881
+
882
+ def select_for_reading?
883
+ true
884
+ end
885
+
886
+ def eventable_read
887
+ io.sysread(128)
888
+ EventMachine::event_callback "", LoopbreakSignalled, ""
889
+ end
890
+
891
+ end
892
+ end
893
+
894
+ #--------------------------------------------------------------
895
+
896
+
897
+ module EventMachine
898
+
899
+ class DatagramObject < Selectable
900
+ def initialize io
901
+ super io
902
+ @outbound_q = []
903
+ end
904
+
905
+ # #send_datagram
906
+ def send_datagram data, target
907
+ # TODO, coalesce here perhaps by being smarter about appending to @outbound_q.last?
908
+ unless @close_scheduled or @close_requested
909
+ @outbound_q << [data.to_s, target]
910
+ end
911
+ end
912
+
913
+ # #select_for_writing?
914
+ def select_for_writing?
915
+ unless @close_scheduled
916
+ if @outbound_q.empty?
917
+ @close_scheduled = true if @close_requested
918
+ false
919
+ else
920
+ true
921
+ end
922
+ end
923
+ end
924
+
925
+ # #select_for_reading?
926
+ def select_for_reading?
927
+ true
928
+ end
929
+
930
+ # #get_outbound_data_size
931
+ def get_outbound_data_size
932
+ @outbound_q.inject(0) {|memo,obj| memo += (obj || "").length}
933
+ end
934
+
935
+
936
+ end
937
+
938
+
939
+ end
940
+
941
+
942
+ #--------------------------------------------------------------
943
+
944
+ module EventMachine
945
+ class EvmaUDPSocket < DatagramObject
946
+
947
+ class << self
948
+ def create host, port
949
+ sd = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
950
+ sd.bind Socket::pack_sockaddr_in( port, host )
951
+ EvmaUDPSocket.new sd
952
+ end
953
+ end
954
+
955
+ # #eventable_write
956
+ # This really belongs in DatagramObject, but there is some UDP-specific stuff.
957
+ def eventable_write
958
+ 40.times {
959
+ break if @outbound_q.empty?
960
+ begin
961
+ data,target = @outbound_q.first
962
+
963
+ # This damn better be nonblocking.
964
+ io.send data.to_s, 0, target
965
+
966
+ @outbound_q.shift
967
+ rescue Errno::EAGAIN
968
+ # It's not been observed in testing that we ever get here.
969
+ # True to the definition, packets will be accepted and quietly dropped
970
+ # if the system is under pressure.
971
+ break
972
+ rescue EOFError, Errno::ECONNRESET
973
+ @close_scheduled = true
974
+ @outbound_q.clear
975
+ end
976
+ }
977
+ end
978
+
979
+ # Proper nonblocking I/O was added to Ruby 1.8.4 in May 2006.
980
+ # If we have it, then we can read multiple times safely to improve
981
+ # performance.
982
+ def eventable_read
983
+ begin
984
+ if io.respond_to?(:recvfrom_nonblock)
985
+ 40.times {
986
+ data,@return_address = io.recvfrom_nonblock(16384)
987
+ EventMachine::event_callback uuid, ConnectionData, data
988
+ @return_address = nil
989
+ }
990
+ else
991
+ raise "unimplemented datagram-read operation on this Ruby"
992
+ end
993
+ rescue Errno::EAGAIN
994
+ # no-op
995
+ rescue Errno::ECONNRESET, EOFError
996
+ @close_scheduled = true
997
+ EventMachine::event_callback uuid, ConnectionUnbound, nil
998
+ end
999
+
1000
+ end
1001
+
1002
+
1003
+ def send_data data
1004
+ send_datagram data, @return_address
1005
+ end
1006
+
1007
+ end
1008
+ end
1009
+
1010
+ # load base EM api on top, now that we have the underlying pure ruby
1011
+ # implementation defined
1012
+ require 'eventmachine'
1013
+