MattHulse-eventmachine 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. data/.gitignore +14 -0
  2. data/README +82 -0
  3. data/Rakefile +279 -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 +41 -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 +821 -0
  25. data/ext/cplusplus.cpp +202 -0
  26. data/ext/ed.cpp +1868 -0
  27. data/ext/ed.h +416 -0
  28. data/ext/em.cpp +2270 -0
  29. data/ext/em.h +228 -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 +138 -0
  37. data/ext/fastfilereader/extconf.rb +84 -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 +147 -0
  48. data/ext/rubymain.cpp +1152 -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/Application.java +192 -0
  56. data/java/src/com/rubyeventmachine/Connection.java +74 -0
  57. data/java/src/com/rubyeventmachine/ConnectionFactory.java +37 -0
  58. data/java/src/com/rubyeventmachine/DefaultConnectionFactory.java +46 -0
  59. data/java/src/com/rubyeventmachine/EmReactor.java +557 -0
  60. data/java/src/com/rubyeventmachine/EmReactorException.java +40 -0
  61. data/java/src/com/rubyeventmachine/EventableChannel.java +69 -0
  62. data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +189 -0
  63. data/java/src/com/rubyeventmachine/EventableSocketChannel.java +364 -0
  64. data/java/src/com/rubyeventmachine/PeriodicTimer.java +38 -0
  65. data/java/src/com/rubyeventmachine/Timer.java +54 -0
  66. data/java/src/com/rubyeventmachine/tests/ApplicationTest.java +108 -0
  67. data/java/src/com/rubyeventmachine/tests/ConnectTest.java +146 -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 +74 -0
  71. data/java/src/com/rubyeventmachine/tests/TestTimers.java +89 -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 +187 -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.rb +35 -0
  83. data/lib/em/protocols/header_and_content.rb +138 -0
  84. data/lib/em/protocols/httpclient.rb +263 -0
  85. data/lib/em/protocols/httpclient2.rb +582 -0
  86. data/lib/em/protocols/line_and_text.rb +126 -0
  87. data/lib/em/protocols/linetext2.rb +160 -0
  88. data/lib/em/protocols/memcache.rb +323 -0
  89. data/lib/em/protocols/object_protocol.rb +45 -0
  90. data/lib/em/protocols/postgres3.rb +247 -0
  91. data/lib/em/protocols/saslauth.rb +175 -0
  92. data/lib/em/protocols/smtpclient.rb +350 -0
  93. data/lib/em/protocols/smtpserver.rb +547 -0
  94. data/lib/em/protocols/stomp.rb +200 -0
  95. data/lib/em/protocols/tcptest.rb +53 -0
  96. data/lib/em/queue.rb +61 -0
  97. data/lib/em/spawnable.rb +85 -0
  98. data/lib/em/streamer.rb +130 -0
  99. data/lib/em/timers.rb +55 -0
  100. data/lib/em/version.rb +3 -0
  101. data/lib/eventmachine.rb +1698 -0
  102. data/lib/evma.rb +32 -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/jeventmachine.rb +246 -0
  109. data/lib/pr_eventmachine.rb +1022 -0
  110. data/setup.rb +1585 -0
  111. data/tasks/cpp.rake +77 -0
  112. data/tasks/project.rake +79 -0
  113. data/tasks/tests.rake +193 -0
  114. data/tests/client.crt +31 -0
  115. data/tests/client.key +51 -0
  116. data/tests/test_attach.rb +126 -0
  117. data/tests/test_basic.rb +284 -0
  118. data/tests/test_channel.rb +63 -0
  119. data/tests/test_connection_count.rb +35 -0
  120. data/tests/test_defer.rb +47 -0
  121. data/tests/test_epoll.rb +160 -0
  122. data/tests/test_error_handler.rb +35 -0
  123. data/tests/test_errors.rb +82 -0
  124. data/tests/test_exc.rb +55 -0
  125. data/tests/test_file_watch.rb +49 -0
  126. data/tests/test_futures.rb +198 -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_process_watch.rb +48 -0
  138. data/tests/test_processes.rb +128 -0
  139. data/tests/test_proxy_connection.rb +92 -0
  140. data/tests/test_pure.rb +125 -0
  141. data/tests/test_queue.rb +44 -0
  142. data/tests/test_running.rb +42 -0
  143. data/tests/test_sasl.rb +72 -0
  144. data/tests/test_send_file.rb +242 -0
  145. data/tests/test_servers.rb +76 -0
  146. data/tests/test_smtpclient.rb +83 -0
  147. data/tests/test_smtpserver.rb +85 -0
  148. data/tests/test_spawn.rb +322 -0
  149. data/tests/test_ssl_args.rb +68 -0
  150. data/tests/test_ssl_methods.rb +50 -0
  151. data/tests/test_ssl_verify.rb +82 -0
  152. data/tests/test_timers.rb +162 -0
  153. data/tests/test_ud.rb +36 -0
  154. data/tests/testem.rb +31 -0
  155. data/web/whatis +7 -0
  156. metadata +223 -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
+