libc-eventmachine 0.12.5.42

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