libc-eventmachine 0.12.5.42

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 (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
+