smparkes-eventmachine 0.12.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. data/.gitignore +15 -0
  2. data/README +81 -0
  3. data/Rakefile +374 -0
  4. data/docs/COPYING +60 -0
  5. data/docs/ChangeLog +211 -0
  6. data/docs/DEFERRABLES +133 -0
  7. data/docs/EPOLL +141 -0
  8. data/docs/GNU +281 -0
  9. data/docs/INSTALL +13 -0
  10. data/docs/KEYBOARD +38 -0
  11. data/docs/LEGAL +25 -0
  12. data/docs/LIGHTWEIGHT_CONCURRENCY +70 -0
  13. data/docs/PURE_RUBY +75 -0
  14. data/docs/RELEASE_NOTES +94 -0
  15. data/docs/SMTP +2 -0
  16. data/docs/SPAWNED_PROCESSES +89 -0
  17. data/docs/TODO +8 -0
  18. data/eventmachine.gemspec +40 -0
  19. data/examples/ex_channel.rb +43 -0
  20. data/examples/ex_queue.rb +2 -0
  21. data/examples/helper.rb +2 -0
  22. data/ext/binder.cpp +125 -0
  23. data/ext/binder.h +46 -0
  24. data/ext/cmain.cpp +827 -0
  25. data/ext/cplusplus.cpp +202 -0
  26. data/ext/ed.cpp +1901 -0
  27. data/ext/ed.h +424 -0
  28. data/ext/em.cpp +2288 -0
  29. data/ext/em.h +229 -0
  30. data/ext/emwin.cpp +300 -0
  31. data/ext/emwin.h +94 -0
  32. data/ext/epoll.cpp +26 -0
  33. data/ext/epoll.h +25 -0
  34. data/ext/eventmachine.h +122 -0
  35. data/ext/eventmachine_cpp.h +96 -0
  36. data/ext/extconf.rb +150 -0
  37. data/ext/fastfilereader/extconf.rb +85 -0
  38. data/ext/fastfilereader/mapper.cpp +214 -0
  39. data/ext/fastfilereader/mapper.h +59 -0
  40. data/ext/fastfilereader/rubymain.cpp +127 -0
  41. data/ext/files.cpp +94 -0
  42. data/ext/files.h +65 -0
  43. data/ext/kb.cpp +81 -0
  44. data/ext/page.cpp +107 -0
  45. data/ext/page.h +51 -0
  46. data/ext/pipe.cpp +349 -0
  47. data/ext/project.h +156 -0
  48. data/ext/rubymain.cpp +1194 -0
  49. data/ext/sigs.cpp +89 -0
  50. data/ext/sigs.h +32 -0
  51. data/ext/ssl.cpp +460 -0
  52. data/ext/ssl.h +94 -0
  53. data/java/.classpath +8 -0
  54. data/java/.project +17 -0
  55. data/java/src/com/rubyeventmachine/EmReactor.java +570 -0
  56. data/java/src/com/rubyeventmachine/EmReactorException.java +40 -0
  57. data/java/src/com/rubyeventmachine/EventableChannel.java +69 -0
  58. data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +189 -0
  59. data/java/src/com/rubyeventmachine/EventableSocketChannel.java +364 -0
  60. data/java/src/com/rubyeventmachine/application/Application.java +194 -0
  61. data/java/src/com/rubyeventmachine/application/Connection.java +74 -0
  62. data/java/src/com/rubyeventmachine/application/ConnectionFactory.java +37 -0
  63. data/java/src/com/rubyeventmachine/application/DefaultConnectionFactory.java +46 -0
  64. data/java/src/com/rubyeventmachine/application/PeriodicTimer.java +38 -0
  65. data/java/src/com/rubyeventmachine/application/Timer.java +54 -0
  66. data/java/src/com/rubyeventmachine/tests/ApplicationTest.java +109 -0
  67. data/java/src/com/rubyeventmachine/tests/ConnectTest.java +148 -0
  68. data/java/src/com/rubyeventmachine/tests/EMTest.java +80 -0
  69. data/java/src/com/rubyeventmachine/tests/TestDatagrams.java +53 -0
  70. data/java/src/com/rubyeventmachine/tests/TestServers.java +75 -0
  71. data/java/src/com/rubyeventmachine/tests/TestTimers.java +90 -0
  72. data/lib/em/buftok.rb +138 -0
  73. data/lib/em/callback.rb +26 -0
  74. data/lib/em/channel.rb +57 -0
  75. data/lib/em/connection.rb +564 -0
  76. data/lib/em/deferrable.rb +192 -0
  77. data/lib/em/file_watch.rb +54 -0
  78. data/lib/em/future.rb +61 -0
  79. data/lib/em/messages.rb +66 -0
  80. data/lib/em/process_watch.rb +44 -0
  81. data/lib/em/processes.rb +119 -0
  82. data/lib/em/protocols/header_and_content.rb +138 -0
  83. data/lib/em/protocols/httpclient.rb +263 -0
  84. data/lib/em/protocols/httpclient2.rb +590 -0
  85. data/lib/em/protocols/line_and_text.rb +125 -0
  86. data/lib/em/protocols/linetext2.rb +161 -0
  87. data/lib/em/protocols/memcache.rb +323 -0
  88. data/lib/em/protocols/object_protocol.rb +45 -0
  89. data/lib/em/protocols/postgres3.rb +247 -0
  90. data/lib/em/protocols/saslauth.rb +175 -0
  91. data/lib/em/protocols/smtpclient.rb +357 -0
  92. data/lib/em/protocols/smtpserver.rb +547 -0
  93. data/lib/em/protocols/socks4.rb +66 -0
  94. data/lib/em/protocols/stomp.rb +200 -0
  95. data/lib/em/protocols/tcptest.rb +53 -0
  96. data/lib/em/protocols.rb +36 -0
  97. data/lib/em/queue.rb +61 -0
  98. data/lib/em/spawnable.rb +85 -0
  99. data/lib/em/streamer.rb +130 -0
  100. data/lib/em/timers.rb +56 -0
  101. data/lib/em/version.rb +3 -0
  102. data/lib/eventmachine.rb +1592 -0
  103. data/lib/evma/callback.rb +32 -0
  104. data/lib/evma/container.rb +75 -0
  105. data/lib/evma/factory.rb +77 -0
  106. data/lib/evma/protocol.rb +87 -0
  107. data/lib/evma/reactor.rb +48 -0
  108. data/lib/evma.rb +32 -0
  109. data/lib/jeventmachine.rb +257 -0
  110. data/lib/pr_eventmachine.rb +1022 -0
  111. data/setup.rb +1585 -0
  112. data/tasks/cpp.rake_example +77 -0
  113. data/tests/client.crt +31 -0
  114. data/tests/client.key +51 -0
  115. data/tests/test_attach.rb +126 -0
  116. data/tests/test_basic.rb +284 -0
  117. data/tests/test_channel.rb +63 -0
  118. data/tests/test_connection_count.rb +35 -0
  119. data/tests/test_defer.rb +47 -0
  120. data/tests/test_epoll.rb +160 -0
  121. data/tests/test_error_handler.rb +35 -0
  122. data/tests/test_errors.rb +82 -0
  123. data/tests/test_exc.rb +55 -0
  124. data/tests/test_file_watch.rb +49 -0
  125. data/tests/test_futures.rb +198 -0
  126. data/tests/test_get_sock_opt.rb +30 -0
  127. data/tests/test_handler_check.rb +37 -0
  128. data/tests/test_hc.rb +218 -0
  129. data/tests/test_httpclient.rb +218 -0
  130. data/tests/test_httpclient2.rb +153 -0
  131. data/tests/test_inactivity_timeout.rb +50 -0
  132. data/tests/test_kb.rb +60 -0
  133. data/tests/test_ltp.rb +182 -0
  134. data/tests/test_ltp2.rb +317 -0
  135. data/tests/test_next_tick.rb +133 -0
  136. data/tests/test_object_protocol.rb +37 -0
  137. data/tests/test_pause.rb +70 -0
  138. data/tests/test_pending_connect_timeout.rb +48 -0
  139. data/tests/test_process_watch.rb +48 -0
  140. data/tests/test_processes.rb +128 -0
  141. data/tests/test_proxy_connection.rb +92 -0
  142. data/tests/test_pure.rb +125 -0
  143. data/tests/test_queue.rb +44 -0
  144. data/tests/test_running.rb +42 -0
  145. data/tests/test_sasl.rb +72 -0
  146. data/tests/test_send_file.rb +242 -0
  147. data/tests/test_servers.rb +76 -0
  148. data/tests/test_smtpclient.rb +83 -0
  149. data/tests/test_smtpserver.rb +85 -0
  150. data/tests/test_spawn.rb +322 -0
  151. data/tests/test_ssl_args.rb +79 -0
  152. data/tests/test_ssl_methods.rb +50 -0
  153. data/tests/test_ssl_verify.rb +82 -0
  154. data/tests/test_timers.rb +162 -0
  155. data/tests/test_ud.rb +36 -0
  156. data/tests/testem.rb +31 -0
  157. data/web/whatis +7 -0
  158. metadata +237 -0
@@ -0,0 +1,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
+
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
+