eventmachine-with-ipv6 1.0.0.beta.4.ipv6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (164) hide show
  1. data/.gitignore +21 -0
  2. data/.yardopts +7 -0
  3. data/FORK.md +47 -0
  4. data/GNU +281 -0
  5. data/Gemfile +3 -0
  6. data/LICENSE +60 -0
  7. data/README.md +109 -0
  8. data/Rakefile +20 -0
  9. data/docs/DocumentationGuidesIndex.md +27 -0
  10. data/docs/GettingStarted.md +521 -0
  11. data/docs/old/ChangeLog +211 -0
  12. data/docs/old/DEFERRABLES +246 -0
  13. data/docs/old/EPOLL +141 -0
  14. data/docs/old/INSTALL +13 -0
  15. data/docs/old/KEYBOARD +42 -0
  16. data/docs/old/LEGAL +25 -0
  17. data/docs/old/LIGHTWEIGHT_CONCURRENCY +130 -0
  18. data/docs/old/PURE_RUBY +75 -0
  19. data/docs/old/RELEASE_NOTES +94 -0
  20. data/docs/old/SMTP +4 -0
  21. data/docs/old/SPAWNED_PROCESSES +148 -0
  22. data/docs/old/TODO +8 -0
  23. data/eventmachine.gemspec +50 -0
  24. data/examples/guides/getting_started/01_eventmachine_echo_server.rb +18 -0
  25. data/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb +22 -0
  26. data/examples/guides/getting_started/03_simple_chat_server.rb +149 -0
  27. data/examples/guides/getting_started/04_simple_chat_server_step_one.rb +27 -0
  28. data/examples/guides/getting_started/05_simple_chat_server_step_two.rb +43 -0
  29. data/examples/guides/getting_started/06_simple_chat_server_step_three.rb +98 -0
  30. data/examples/guides/getting_started/07_simple_chat_server_step_four.rb +121 -0
  31. data/examples/guides/getting_started/08_simple_chat_server_step_five.rb +141 -0
  32. data/examples/old/ex_channel.rb +43 -0
  33. data/examples/old/ex_queue.rb +2 -0
  34. data/examples/old/ex_tick_loop_array.rb +15 -0
  35. data/examples/old/ex_tick_loop_counter.rb +32 -0
  36. data/examples/old/helper.rb +2 -0
  37. data/ext/binder.cpp +124 -0
  38. data/ext/binder.h +46 -0
  39. data/ext/cmain.cpp +858 -0
  40. data/ext/ed.cpp +1992 -0
  41. data/ext/ed.h +423 -0
  42. data/ext/em.cpp +2358 -0
  43. data/ext/em.h +245 -0
  44. data/ext/eventmachine.h +127 -0
  45. data/ext/extconf.rb +166 -0
  46. data/ext/fastfilereader/extconf.rb +94 -0
  47. data/ext/fastfilereader/mapper.cpp +214 -0
  48. data/ext/fastfilereader/mapper.h +59 -0
  49. data/ext/fastfilereader/rubymain.cpp +127 -0
  50. data/ext/kb.cpp +79 -0
  51. data/ext/page.cpp +107 -0
  52. data/ext/page.h +51 -0
  53. data/ext/pipe.cpp +347 -0
  54. data/ext/project.h +155 -0
  55. data/ext/rubymain.cpp +1280 -0
  56. data/ext/ssl.cpp +468 -0
  57. data/ext/ssl.h +94 -0
  58. data/java/.classpath +8 -0
  59. data/java/.project +17 -0
  60. data/java/src/com/rubyeventmachine/EmReactor.java +571 -0
  61. data/java/src/com/rubyeventmachine/EmReactorException.java +40 -0
  62. data/java/src/com/rubyeventmachine/EventableChannel.java +69 -0
  63. data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +189 -0
  64. data/java/src/com/rubyeventmachine/EventableSocketChannel.java +364 -0
  65. data/lib/em/buftok.rb +110 -0
  66. data/lib/em/callback.rb +58 -0
  67. data/lib/em/channel.rb +64 -0
  68. data/lib/em/completion.rb +304 -0
  69. data/lib/em/connection.rb +728 -0
  70. data/lib/em/deferrable.rb +210 -0
  71. data/lib/em/deferrable/pool.rb +2 -0
  72. data/lib/em/file_watch.rb +73 -0
  73. data/lib/em/future.rb +61 -0
  74. data/lib/em/iterator.rb +270 -0
  75. data/lib/em/messages.rb +66 -0
  76. data/lib/em/pool.rb +151 -0
  77. data/lib/em/process_watch.rb +45 -0
  78. data/lib/em/processes.rb +123 -0
  79. data/lib/em/protocols.rb +36 -0
  80. data/lib/em/protocols/header_and_content.rb +138 -0
  81. data/lib/em/protocols/httpclient.rb +279 -0
  82. data/lib/em/protocols/httpclient2.rb +600 -0
  83. data/lib/em/protocols/line_and_text.rb +125 -0
  84. data/lib/em/protocols/line_protocol.rb +29 -0
  85. data/lib/em/protocols/linetext2.rb +161 -0
  86. data/lib/em/protocols/memcache.rb +331 -0
  87. data/lib/em/protocols/object_protocol.rb +46 -0
  88. data/lib/em/protocols/postgres3.rb +246 -0
  89. data/lib/em/protocols/saslauth.rb +175 -0
  90. data/lib/em/protocols/smtpclient.rb +365 -0
  91. data/lib/em/protocols/smtpserver.rb +640 -0
  92. data/lib/em/protocols/socks4.rb +66 -0
  93. data/lib/em/protocols/stomp.rb +202 -0
  94. data/lib/em/protocols/tcptest.rb +54 -0
  95. data/lib/em/pure_ruby.rb +1017 -0
  96. data/lib/em/queue.rb +71 -0
  97. data/lib/em/resolver.rb +195 -0
  98. data/lib/em/spawnable.rb +84 -0
  99. data/lib/em/streamer.rb +118 -0
  100. data/lib/em/threaded_resource.rb +90 -0
  101. data/lib/em/tick_loop.rb +85 -0
  102. data/lib/em/timers.rb +61 -0
  103. data/lib/em/version.rb +3 -0
  104. data/lib/eventmachine.rb +1517 -0
  105. data/lib/jeventmachine.rb +279 -0
  106. data/rakelib/cpp.rake_example +77 -0
  107. data/rakelib/package.rake +98 -0
  108. data/rakelib/test.rake +8 -0
  109. data/tests/client.crt +31 -0
  110. data/tests/client.key +51 -0
  111. data/tests/em_test_helper.rb +64 -0
  112. data/tests/test_attach.rb +126 -0
  113. data/tests/test_basic.rb +294 -0
  114. data/tests/test_channel.rb +62 -0
  115. data/tests/test_completion.rb +177 -0
  116. data/tests/test_connection_count.rb +33 -0
  117. data/tests/test_defer.rb +18 -0
  118. data/tests/test_deferrable.rb +35 -0
  119. data/tests/test_epoll.rb +134 -0
  120. data/tests/test_error_handler.rb +38 -0
  121. data/tests/test_exc.rb +28 -0
  122. data/tests/test_file_watch.rb +65 -0
  123. data/tests/test_futures.rb +170 -0
  124. data/tests/test_get_sock_opt.rb +37 -0
  125. data/tests/test_handler_check.rb +35 -0
  126. data/tests/test_hc.rb +155 -0
  127. data/tests/test_httpclient.rb +190 -0
  128. data/tests/test_httpclient2.rb +128 -0
  129. data/tests/test_inactivity_timeout.rb +54 -0
  130. data/tests/test_ipv4.rb +128 -0
  131. data/tests/test_ipv6.rb +135 -0
  132. data/tests/test_kb.rb +34 -0
  133. data/tests/test_ltp.rb +138 -0
  134. data/tests/test_ltp2.rb +288 -0
  135. data/tests/test_next_tick.rb +104 -0
  136. data/tests/test_object_protocol.rb +36 -0
  137. data/tests/test_pause.rb +78 -0
  138. data/tests/test_pending_connect_timeout.rb +52 -0
  139. data/tests/test_pool.rb +194 -0
  140. data/tests/test_process_watch.rb +48 -0
  141. data/tests/test_processes.rb +133 -0
  142. data/tests/test_proxy_connection.rb +168 -0
  143. data/tests/test_pure.rb +88 -0
  144. data/tests/test_queue.rb +50 -0
  145. data/tests/test_resolver.rb +55 -0
  146. data/tests/test_running.rb +14 -0
  147. data/tests/test_sasl.rb +47 -0
  148. data/tests/test_send_file.rb +217 -0
  149. data/tests/test_servers.rb +33 -0
  150. data/tests/test_set_sock_opt.rb +41 -0
  151. data/tests/test_shutdown_hooks.rb +23 -0
  152. data/tests/test_smtpclient.rb +55 -0
  153. data/tests/test_smtpserver.rb +57 -0
  154. data/tests/test_spawn.rb +293 -0
  155. data/tests/test_ssl_args.rb +78 -0
  156. data/tests/test_ssl_methods.rb +48 -0
  157. data/tests/test_ssl_verify.rb +82 -0
  158. data/tests/test_threaded_resource.rb +53 -0
  159. data/tests/test_tick_loop.rb +59 -0
  160. data/tests/test_timers.rb +123 -0
  161. data/tests/test_ud.rb +8 -0
  162. data/tests/test_udp46.rb +54 -0
  163. data/tests/test_unbind_reason.rb +48 -0
  164. metadata +319 -0
@@ -0,0 +1,66 @@
1
+ module EventMachine
2
+ module Protocols
3
+ # Basic SOCKS v4 client implementation
4
+ #
5
+ # Use as you would any regular connection:
6
+ #
7
+ # class MyConn < EM::P::Socks4
8
+ # def post_init
9
+ # send_data("sup")
10
+ # end
11
+ #
12
+ # def receive_data(data)
13
+ # send_data("you said: #{data}")
14
+ # end
15
+ # end
16
+ #
17
+ # EM.connect socks_host, socks_port, MyConn, host, port
18
+ #
19
+ class Socks4 < Connection
20
+ def initialize(host, port)
21
+ @host = Socket.gethostbyname(host).last
22
+ @port = port
23
+ @socks_error_code = nil
24
+ @buffer = ''
25
+ setup_methods
26
+ end
27
+
28
+ def setup_methods
29
+ class << self
30
+ def post_init; socks_post_init; end
31
+ def receive_data(*a); socks_receive_data(*a); end
32
+ end
33
+ end
34
+
35
+ def restore_methods
36
+ class << self
37
+ remove_method :post_init
38
+ remove_method :receive_data
39
+ end
40
+ end
41
+
42
+ def socks_post_init
43
+ header = [4, 1, @port, @host, 0].flatten.pack("CCnA4C")
44
+ send_data(header)
45
+ end
46
+
47
+ def socks_receive_data(data)
48
+ @buffer << data
49
+ return if @buffer.size < 8
50
+
51
+ header_resp = @buffer.slice! 0, 8
52
+ _, r = header_resp.unpack("cc")
53
+ if r != 90
54
+ @socks_error_code = r
55
+ close_connection
56
+ return
57
+ end
58
+
59
+ restore_methods
60
+
61
+ post_init
62
+ receive_data(@buffer) unless @buffer.empty?
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,202 @@
1
+ #--
2
+ #
3
+ # Author:: Francis Cianfrocca (gmail: blackhedd)
4
+ # Homepage:: http://rubyeventmachine.com
5
+ # Date:: 15 November 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
+
27
+ module EventMachine
28
+ module Protocols
29
+
30
+ # Implements Stomp (http://docs.codehaus.org/display/STOMP/Protocol).
31
+ #
32
+ # == Usage example
33
+ #
34
+ # module StompClient
35
+ # include EM::Protocols::Stomp
36
+ #
37
+ # def connection_completed
38
+ # connect :login => 'guest', :passcode => 'guest'
39
+ # end
40
+ #
41
+ # def receive_msg msg
42
+ # if msg.command == "CONNECTED"
43
+ # subscribe '/some/topic'
44
+ # else
45
+ # p ['got a message', msg]
46
+ # puts msg.body
47
+ # end
48
+ # end
49
+ # end
50
+ #
51
+ # EM.run{
52
+ # EM.connect 'localhost', 61613, StompClient
53
+ # }
54
+ #
55
+ module Stomp
56
+ include LineText2
57
+
58
+ class Message
59
+ # The command associated with the message, usually 'CONNECTED' or 'MESSAGE'
60
+ attr_accessor :command
61
+ # Hash containing headers such as destination and message-id
62
+ attr_accessor :header
63
+ alias :headers :header
64
+ # Body of the message
65
+ attr_accessor :body
66
+
67
+ # @private
68
+ def initialize
69
+ @header = {}
70
+ @state = :precommand
71
+ @content_length = nil
72
+ end
73
+ # @private
74
+ def consume_line line
75
+ if @state == :precommand
76
+ unless line =~ /\A\s*\Z/
77
+ @command = line
78
+ @state = :headers
79
+ end
80
+ elsif @state == :headers
81
+ if line == ""
82
+ if @content_length
83
+ yield( [:sized_text, @content_length+1] )
84
+ else
85
+ @state = :body
86
+ yield( [:unsized_text] )
87
+ end
88
+ elsif line =~ /\A([^:]+):(.+)\Z/
89
+ k = $1.dup.strip
90
+ v = $2.dup.strip
91
+ @header[k] = v
92
+ if k == "content-length"
93
+ @content_length = v.to_i
94
+ end
95
+ else
96
+ # This is a protocol error. How to signal it?
97
+ end
98
+ elsif @state == :body
99
+ @body = line
100
+ yield( [:dispatch] )
101
+ end
102
+ end
103
+ end
104
+
105
+ # @private
106
+ def send_frame verb, headers={}, body=""
107
+ ary = [verb, "\n"]
108
+ headers.each {|k,v| ary << "#{k}:#{v}\n" }
109
+ ary << "content-length: #{body.to_s.length}\n"
110
+ ary << "content-type: text/plain; charset=UTF-8\n" unless headers.has_key? 'content-type'
111
+ ary << "\n"
112
+ ary << body.to_s
113
+ ary << "\0"
114
+ send_data ary.join
115
+ end
116
+
117
+ # @private
118
+ def receive_line line
119
+ @stomp_initialized || init_message_reader
120
+ @stomp_message.consume_line(line) {|outcome|
121
+ if outcome.first == :sized_text
122
+ set_text_mode outcome[1]
123
+ elsif outcome.first == :unsized_text
124
+ set_delimiter "\0"
125
+ elsif outcome.first == :dispatch
126
+ receive_msg(@stomp_message) if respond_to?(:receive_msg)
127
+ init_message_reader
128
+ end
129
+ }
130
+ end
131
+
132
+ # @private
133
+ def receive_binary_data data
134
+ @stomp_message.body = data[0..-2]
135
+ receive_msg(@stomp_message) if respond_to?(:receive_msg)
136
+ init_message_reader
137
+ end
138
+
139
+ # @private
140
+ def init_message_reader
141
+ @stomp_initialized = true
142
+ set_delimiter "\n"
143
+ set_line_mode
144
+ @stomp_message = Message.new
145
+ end
146
+
147
+ # Invoked with an incoming Stomp::Message received from the STOMP server
148
+ def receive_msg msg
149
+ # stub, overwrite this in your handler
150
+ end
151
+
152
+ # CONNECT command, for authentication
153
+ #
154
+ # connect :login => 'guest', :passcode => 'guest'
155
+ #
156
+ def connect parms={}
157
+ send_frame "CONNECT", parms
158
+ end
159
+
160
+ # SEND command, for publishing messages to a topic
161
+ #
162
+ # send '/topic/name', 'some message here'
163
+ #
164
+ def send destination, body, parms={}
165
+ send_frame "SEND", parms.merge( :destination=>destination ), body.to_s
166
+ end
167
+
168
+ # SUBSCRIBE command, for subscribing to topics
169
+ #
170
+ # subscribe '/topic/name', false
171
+ #
172
+ def subscribe dest, ack=false
173
+ send_frame "SUBSCRIBE", {:destination=>dest, :ack=>(ack ? "client" : "auto")}
174
+ end
175
+
176
+ # ACK command, for acknowledging receipt of messages
177
+ #
178
+ # module StompClient
179
+ # include EM::P::Stomp
180
+ #
181
+ # def connection_completed
182
+ # connect :login => 'guest', :passcode => 'guest'
183
+ # # subscribe with ack mode
184
+ # subscribe '/some/topic', true
185
+ # end
186
+ #
187
+ # def receive_msg msg
188
+ # if msg.command == "MESSAGE"
189
+ # ack msg.headers['message-id']
190
+ # puts msg.body
191
+ # end
192
+ # end
193
+ # end
194
+ #
195
+ def ack msgid
196
+ send_frame "ACK", 'message-id'=> msgid
197
+ end
198
+
199
+ end
200
+ end
201
+ end
202
+
@@ -0,0 +1,54 @@
1
+ #--
2
+ #
3
+ # Author:: Francis Cianfrocca (gmail: blackhedd)
4
+ # Homepage:: http://rubyeventmachine.com
5
+ # Date:: 16 July 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
+
27
+ module EventMachine
28
+ module Protocols
29
+
30
+ # @private
31
+ class TcpConnectTester < Connection
32
+ include EventMachine::Deferrable
33
+
34
+ def self.test( host, port )
35
+ EventMachine.connect( host, port, self )
36
+ end
37
+
38
+ def post_init
39
+ @start_time = Time.now
40
+ end
41
+
42
+ def connection_completed
43
+ @completed = true
44
+ set_deferred_status :succeeded, (Time.now - @start_time)
45
+ close_connection
46
+ end
47
+
48
+ def unbind
49
+ set_deferred_status :failed, (Time.now - @start_time) unless @completed
50
+ end
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,1017 @@
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
+ # @private
38
+ module EventMachine
39
+ class << self
40
+ # This is mostly useful for automated tests.
41
+ # Return a distinctive symbol so the caller knows whether he's dealing
42
+ # with an extension or with a pure-Ruby library.
43
+ # @private
44
+ def library_type
45
+ :pure_ruby
46
+ end
47
+
48
+ # @private
49
+ def initialize_event_machine
50
+ Reactor.instance.initialize_for_run
51
+ end
52
+
53
+ # Changed 04Oct06: intervals from the caller are now in milliseconds, but our native-ruby
54
+ # processor still wants them in seconds.
55
+ # @private
56
+ def add_oneshot_timer interval
57
+ Reactor.instance.install_oneshot_timer(interval / 1000)
58
+ end
59
+
60
+ # @private
61
+ def run_machine
62
+ Reactor.instance.run
63
+ end
64
+
65
+ # @private
66
+ def release_machine
67
+ end
68
+
69
+ # @private
70
+ def stop
71
+ Reactor.instance.stop
72
+ end
73
+
74
+ # @private
75
+ def connect_server host, port
76
+ bind_connect_server nil, nil, host, port
77
+ end
78
+
79
+ # @private
80
+ def bind_connect_server bind_addr, bind_port, host, port
81
+ EvmaTCPClient.connect(bind_addr, bind_port, host, port).uuid
82
+ end
83
+
84
+ # @private
85
+ def send_data target, data, datalength
86
+ selectable = Reactor.instance.get_selectable( target ) or raise "unknown send_data target"
87
+ selectable.send_data data
88
+ end
89
+
90
+
91
+ # The extension version does NOT raise any kind of an error if an attempt is made
92
+ # to close a non-existent connection. Not sure whether we should. For now, we'll
93
+ # raise an error here in that case.
94
+ # @private
95
+ def close_connection target, after_writing
96
+ selectable = Reactor.instance.get_selectable( target ) or raise "unknown close_connection target"
97
+ selectable.schedule_close after_writing
98
+ end
99
+
100
+ # @private
101
+ def start_tcp_server host, port
102
+ (s = EvmaTCPServer.start_server host, port) or raise "no acceptor"
103
+ s.uuid
104
+ end
105
+
106
+ # @private
107
+ def stop_tcp_server sig
108
+ s = Reactor.instance.get_selectable(sig)
109
+ s.schedule_close
110
+ end
111
+
112
+ # @private
113
+ def start_unix_server chain
114
+ (s = EvmaUNIXServer.start_server chain) or raise "no acceptor"
115
+ s.uuid
116
+ end
117
+
118
+ # @private
119
+ def connect_unix_server chain
120
+ EvmaUNIXClient.connect(chain).uuid
121
+ end
122
+
123
+ # @private
124
+ def signal_loopbreak
125
+ Reactor.instance.signal_loopbreak
126
+ end
127
+
128
+ # @private
129
+ def get_peername sig
130
+ selectable = Reactor.instance.get_selectable( sig ) or raise "unknown get_peername target"
131
+ selectable.get_peername
132
+ end
133
+
134
+ # @private
135
+ def open_udp_socket host, port
136
+ EvmaUDPSocket.create(host, port).uuid
137
+ end
138
+
139
+ # This is currently only for UDP!
140
+ # We need to make it work with unix-domain sockets as well.
141
+ # @private
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
+ # Sets reactor quantum in milliseconds. The underlying Reactor function wants a (possibly
149
+ # fractional) number of seconds.
150
+ # @private
151
+ def set_timer_quantum interval
152
+ Reactor.instance.set_timer_quantum(( 1.0 * interval) / 1000.0)
153
+ end
154
+
155
+ # This method is a harmless no-op in the pure-Ruby implementation. This is intended to ensure
156
+ # that user code behaves properly across different EM implementations.
157
+ # @private
158
+ def epoll
159
+ end
160
+
161
+ # This method is not implemented for pure-Ruby implementation
162
+ # @private
163
+ def ssl?
164
+ false
165
+ end
166
+
167
+ # This method is a no-op in the pure-Ruby implementation. We simply return Ruby's built-in
168
+ # per-process file-descriptor limit.
169
+ # @private
170
+ def set_rlimit_nofile n
171
+ 1024
172
+ end
173
+
174
+ # This method is a harmless no-op in pure Ruby, which doesn't have a built-in limit
175
+ # on the number of available timers.
176
+ # @private
177
+ def set_max_timer_count n
178
+ end
179
+
180
+ # @private
181
+ def get_sock_opt signature, level, optname
182
+ selectable = Reactor.instance.get_selectable( signature ) or raise "unknown get_peername target"
183
+ selectable.getsockopt level, optname
184
+ end
185
+
186
+ # @private
187
+ def set_sock_opt signature, level, optname, optval
188
+ selectable = Reactor.instance.get_selectable( signature ) or raise "unknown get_peername target"
189
+ selectable.setsockopt level, optname, optval
190
+ end
191
+
192
+ # @private
193
+ def send_file_data sig, filename
194
+ sz = File.size(filename)
195
+ raise "file too large" if sz > 32*1024
196
+ data =
197
+ begin
198
+ File.read filename
199
+ rescue
200
+ ""
201
+ end
202
+ send_data sig, data, data.length
203
+ end
204
+
205
+ # @private
206
+ def get_outbound_data_size sig
207
+ r = Reactor.instance.get_selectable( sig ) or raise "unknown get_outbound_data_size target"
208
+ r.get_outbound_data_size
209
+ end
210
+
211
+ # @private
212
+ def read_keyboard
213
+ EvmaKeyboard.open.uuid
214
+ end
215
+
216
+ # @private
217
+ def set_comm_inactivity_timeout sig, tm
218
+ r = Reactor.instance.get_selectable( sig ) or raise "unknown set_comm_inactivity_timeout target"
219
+ r.set_inactivity_timeout tm
220
+ end
221
+ end
222
+ end
223
+
224
+
225
+ module EventMachine
226
+ # @private
227
+ class Error < Exception; end
228
+ end
229
+
230
+ module EventMachine
231
+ # @private
232
+ class Connection
233
+ # @private
234
+ def get_outbound_data_size
235
+ EventMachine::get_outbound_data_size @signature
236
+ end
237
+ end
238
+ end
239
+
240
+ module EventMachine
241
+
242
+ # Factored out so we can substitute other implementations
243
+ # here if desired, such as the one in ActiveRBAC.
244
+ # @private
245
+ module UuidGenerator
246
+ def self.generate
247
+ @ix ||= 0
248
+ @ix += 1
249
+ end
250
+ end
251
+ end
252
+
253
+
254
+ module EventMachine
255
+ # @private
256
+ TimerFired = 100
257
+ # @private
258
+ ConnectionData = 101
259
+ # @private
260
+ ConnectionUnbound = 102
261
+ # @private
262
+ ConnectionAccepted = 103
263
+ # @private
264
+ ConnectionCompleted = 104
265
+ # @private
266
+ LoopbreakSignalled = 105
267
+ end
268
+
269
+ module EventMachine
270
+ # @private
271
+ class Reactor
272
+ include Singleton
273
+
274
+ HeartbeatInterval = 2
275
+
276
+ attr_reader :current_loop_time
277
+
278
+ def initialize
279
+ initialize_for_run
280
+ end
281
+
282
+ def install_oneshot_timer interval
283
+ uuid = UuidGenerator::generate
284
+ #@timers << [Time.now + interval, uuid]
285
+ #@timers.sort! {|a,b| a.first <=> b.first}
286
+ @timers.add([Time.now + interval, uuid])
287
+ uuid
288
+ end
289
+
290
+ # Called before run, this is a good place to clear out arrays
291
+ # with cruft that may be left over from a previous run.
292
+ # @private
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
+ # @private
425
+ class IO
426
+ extend Forwardable
427
+ def_delegator :@my_selectable, :close_scheduled?
428
+ def_delegator :@my_selectable, :select_for_reading?
429
+ def_delegator :@my_selectable, :select_for_writing?
430
+ def_delegator :@my_selectable, :eventable_read
431
+ def_delegator :@my_selectable, :eventable_write
432
+ def_delegator :@my_selectable, :uuid
433
+ def_delegator :@my_selectable, :send_data
434
+ def_delegator :@my_selectable, :schedule_close
435
+ def_delegator :@my_selectable, :get_peername
436
+ def_delegator :@my_selectable, :send_datagram
437
+ def_delegator :@my_selectable, :get_outbound_data_size
438
+ def_delegator :@my_selectable, :set_inactivity_timeout
439
+ def_delegator :@my_selectable, :heartbeat
440
+ end
441
+
442
+ module EventMachine
443
+ # @private
444
+ class Selectable
445
+
446
+ attr_reader :io, :uuid
447
+
448
+ def initialize io
449
+ @uuid = UuidGenerator.generate
450
+ @io = io
451
+ @last_activity = Reactor.instance.current_loop_time
452
+
453
+ if defined?(Fcntl::F_GETFL)
454
+ m = @io.fcntl(Fcntl::F_GETFL, 0)
455
+ @io.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK | m)
456
+ else
457
+ # Windows doesn't define F_GETFL.
458
+ # It's not very reliable about setting descriptors nonblocking either.
459
+ begin
460
+ s = Socket.for_fd(@io.fileno)
461
+ s.fcntl( Fcntl::F_SETFL, Fcntl::O_NONBLOCK )
462
+ rescue Errno::EINVAL, Errno::EBADF
463
+ warn "Serious error: unable to set descriptor non-blocking"
464
+ end
465
+ end
466
+ # TODO, should set CLOEXEC on Unix?
467
+
468
+ @close_scheduled = false
469
+ @close_requested = false
470
+
471
+ se = self; @io.instance_eval { @my_selectable = se }
472
+ Reactor.instance.add_selectable @io
473
+ end
474
+
475
+ def close_scheduled?
476
+ @close_scheduled
477
+ end
478
+
479
+ def select_for_reading?
480
+ false
481
+ end
482
+
483
+ def select_for_writing?
484
+ false
485
+ end
486
+
487
+ def get_peername
488
+ nil
489
+ end
490
+
491
+ def set_inactivity_timeout tm
492
+ @inactivity_timeout = tm
493
+ end
494
+
495
+ def heartbeat
496
+ end
497
+ end
498
+
499
+ end
500
+
501
+ module EventMachine
502
+ # @private
503
+ class StreamObject < Selectable
504
+ def initialize io
505
+ super io
506
+ @outbound_q = []
507
+ end
508
+
509
+ # If we have to close, or a close-after-writing has been requested,
510
+ # then don't read any more data.
511
+ def select_for_reading?
512
+ true unless (@close_scheduled || @close_requested)
513
+ end
514
+
515
+ # If we have to close, don't select for writing.
516
+ # Otherwise, see if the protocol is ready to close.
517
+ # If not, see if he has data to send.
518
+ # If a close-after-writing has been requested and the outbound queue
519
+ # is empty, convert the status to close_scheduled.
520
+ def select_for_writing?
521
+ unless @close_scheduled
522
+ if @outbound_q.empty?
523
+ @close_scheduled = true if @close_requested
524
+ false
525
+ else
526
+ true
527
+ end
528
+ end
529
+ end
530
+
531
+ # Proper nonblocking I/O was added to Ruby 1.8.4 in May 2006.
532
+ # If we have it, then we can read multiple times safely to improve
533
+ # performance.
534
+ # The last-activity clock ASSUMES that we only come here when we
535
+ # have selected readable.
536
+ # TODO, coalesce multiple reads into a single event.
537
+ # TODO, do the function check somewhere else and cache it.
538
+ def eventable_read
539
+ @last_activity = Reactor.instance.current_loop_time
540
+ begin
541
+ if io.respond_to?(:read_nonblock)
542
+ 10.times {
543
+ data = io.read_nonblock(4096)
544
+ EventMachine::event_callback uuid, ConnectionData, data
545
+ }
546
+ else
547
+ data = io.sysread(4096)
548
+ EventMachine::event_callback uuid, ConnectionData, data
549
+ end
550
+ rescue Errno::EAGAIN, Errno::EWOULDBLOCK
551
+ # no-op
552
+ rescue Errno::ECONNRESET, Errno::ECONNREFUSED, EOFError
553
+ @close_scheduled = true
554
+ EventMachine::event_callback uuid, ConnectionUnbound, nil
555
+ end
556
+
557
+ end
558
+
559
+ # Provisional implementation. Will be re-implemented in subclasses.
560
+ # TODO: Complete this implementation. As it stands, this only writes
561
+ # a single packet per cycle. Highly inefficient, but required unless
562
+ # we're running on a Ruby with proper nonblocking I/O (Ruby 1.8.4
563
+ # built from sources from May 25, 2006 or newer).
564
+ # We need to improve the loop so it writes multiple times, however
565
+ # not more than a certain number of bytes per cycle, otherwise
566
+ # one busy connection could hog output buffers and slow down other
567
+ # connections. Also we should coalesce small writes.
568
+ # URGENT TODO: Coalesce small writes. They are a performance killer.
569
+ # The last-activity recorder ASSUMES we'll only come here if we've
570
+ # selected writable.
571
+ def eventable_write
572
+ # coalesce the outbound array here, perhaps
573
+ @last_activity = Reactor.instance.current_loop_time
574
+ while data = @outbound_q.shift do
575
+ begin
576
+ data = data.to_s
577
+ w = if io.respond_to?(:write_nonblock)
578
+ io.write_nonblock data
579
+ else
580
+ io.syswrite data
581
+ end
582
+
583
+ if w < data.length
584
+ @outbound_q.unshift data[w..-1]
585
+ break
586
+ end
587
+ rescue Errno::EAGAIN
588
+ @outbound_q.unshift data
589
+ rescue EOFError, Errno::ECONNRESET, Errno::ECONNREFUSED
590
+ @close_scheduled = true
591
+ @outbound_q.clear
592
+ end
593
+ end
594
+
595
+ end
596
+
597
+ # #send_data
598
+ def send_data data
599
+ # TODO, coalesce here perhaps by being smarter about appending to @outbound_q.last?
600
+ unless @close_scheduled or @close_requested or !data or data.length <= 0
601
+ @outbound_q << data.to_s
602
+ end
603
+ end
604
+
605
+ # #schedule_close
606
+ # The application wants to close the connection.
607
+ def schedule_close after_writing
608
+ if after_writing
609
+ @close_requested = true
610
+ else
611
+ @close_scheduled = true
612
+ end
613
+ end
614
+
615
+ # #get_peername
616
+ # This is defined in the normal way on connected stream objects.
617
+ # Return an object that is suitable for passing to Socket#unpack_sockaddr_in or variants.
618
+ # We could also use a convenience method that did the unpacking automatically.
619
+ def get_peername
620
+ io.getpeername
621
+ end
622
+
623
+ # #get_outbound_data_size
624
+ def get_outbound_data_size
625
+ @outbound_q.inject(0) {|memo,obj| memo += (obj || "").length}
626
+ end
627
+
628
+ def heartbeat
629
+ if @inactivity_timeout and @inactivity_timeout > 0 and (@last_activity + @inactivity_timeout) < Reactor.instance.current_loop_time
630
+ schedule_close true
631
+ end
632
+ end
633
+ end
634
+
635
+
636
+ end
637
+
638
+
639
+ #--------------------------------------------------------------
640
+
641
+
642
+
643
+ module EventMachine
644
+ # @private
645
+ class EvmaTCPClient < StreamObject
646
+
647
+ def self.connect bind_addr, bind_port, host, port
648
+ sd = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
649
+ sd.bind( Socket.pack_sockaddr_in( bind_port, bind_addr )) if bind_addr
650
+
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
+ module EventMachine
694
+ # @private
695
+ class EvmaKeyboard < StreamObject
696
+
697
+ def self.open
698
+ EvmaKeyboard.new STDIN
699
+ end
700
+
701
+
702
+ def initialize io
703
+ super
704
+ end
705
+
706
+
707
+ def select_for_writing?
708
+ false
709
+ end
710
+
711
+ def select_for_reading?
712
+ true
713
+ end
714
+
715
+
716
+ end
717
+ end
718
+
719
+
720
+
721
+ module EventMachine
722
+ # @private
723
+ class EvmaUNIXClient < StreamObject
724
+
725
+ def self.connect chain
726
+ sd = Socket.new( Socket::AF_LOCAL, Socket::SOCK_STREAM, 0 )
727
+ begin
728
+ # TODO, this assumes a current Ruby snapshot.
729
+ # We need to degrade to a nonblocking connect otherwise.
730
+ sd.connect_nonblock( Socket.pack_sockaddr_un( chain ))
731
+ rescue Errno::EINPROGRESS
732
+ end
733
+ EvmaUNIXClient.new sd
734
+ end
735
+
736
+
737
+ def initialize io
738
+ super
739
+ @pending = true
740
+ end
741
+
742
+
743
+ def select_for_writing?
744
+ @pending ? true : super
745
+ end
746
+
747
+ def select_for_reading?
748
+ @pending ? false : super
749
+ end
750
+
751
+ def eventable_write
752
+ if @pending
753
+ @pending = false
754
+ if 0 == io.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR).unpack("i").first
755
+ EventMachine::event_callback uuid, ConnectionCompleted, ""
756
+ end
757
+ else
758
+ super
759
+ end
760
+ end
761
+
762
+
763
+
764
+ end
765
+ end
766
+
767
+
768
+ #--------------------------------------------------------------
769
+
770
+ module EventMachine
771
+ # @private
772
+ class EvmaTCPServer < Selectable
773
+
774
+ # TODO, refactor and unify with EvmaUNIXServer.
775
+
776
+ class << self
777
+ # Versions of ruby 1.8.4 later than May 26 2006 will work properly
778
+ # with an object of type TCPServer. Prior versions won't so we
779
+ # play it safe and just build a socket.
780
+ #
781
+ def start_server host, port
782
+ sd = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
783
+ sd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true )
784
+ sd.bind( Socket.pack_sockaddr_in( port, host ))
785
+ sd.listen( 50 ) # 5 is what you see in all the books. Ain't enough.
786
+ EvmaTCPServer.new sd
787
+ end
788
+ end
789
+
790
+ def initialize io
791
+ super io
792
+ end
793
+
794
+
795
+ def select_for_reading?
796
+ true
797
+ end
798
+
799
+ #--
800
+ # accept_nonblock returns an array consisting of the accepted
801
+ # socket and a sockaddr_in which names the peer.
802
+ # Don't accept more than 10 at a time.
803
+ def eventable_read
804
+ begin
805
+ 10.times {
806
+ descriptor,peername = io.accept_nonblock
807
+ sd = StreamObject.new descriptor
808
+ EventMachine::event_callback uuid, ConnectionAccepted, sd.uuid
809
+ }
810
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN
811
+ end
812
+ end
813
+
814
+ #--
815
+ #
816
+ def schedule_close
817
+ @close_scheduled = true
818
+ end
819
+
820
+ end
821
+ end
822
+
823
+
824
+ #--------------------------------------------------------------
825
+
826
+ module EventMachine
827
+ # @private
828
+ class EvmaUNIXServer < Selectable
829
+
830
+ # TODO, refactor and unify with EvmaTCPServer.
831
+
832
+ class << self
833
+ # Versions of ruby 1.8.4 later than May 26 2006 will work properly
834
+ # with an object of type TCPServer. Prior versions won't so we
835
+ # play it safe and just build a socket.
836
+ #
837
+ def start_server chain
838
+ sd = Socket.new( Socket::AF_LOCAL, Socket::SOCK_STREAM, 0 )
839
+ sd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true )
840
+ sd.bind( Socket.pack_sockaddr_un( chain ))
841
+ sd.listen( 50 ) # 5 is what you see in all the books. Ain't enough.
842
+ EvmaUNIXServer.new sd
843
+ end
844
+ end
845
+
846
+ def initialize io
847
+ super io
848
+ end
849
+
850
+
851
+ def select_for_reading?
852
+ true
853
+ end
854
+
855
+ #--
856
+ # accept_nonblock returns an array consisting of the accepted
857
+ # socket and a sockaddr_in which names the peer.
858
+ # Don't accept more than 10 at a time.
859
+ def eventable_read
860
+ begin
861
+ 10.times {
862
+ descriptor,peername = io.accept_nonblock
863
+ sd = StreamObject.new descriptor
864
+ EventMachine::event_callback uuid, ConnectionAccepted, sd.uuid
865
+ }
866
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN
867
+ end
868
+ end
869
+
870
+ #--
871
+ #
872
+ def schedule_close
873
+ @close_scheduled = true
874
+ end
875
+
876
+ end
877
+ end
878
+
879
+
880
+
881
+ #--------------------------------------------------------------
882
+
883
+ module EventMachine
884
+ # @private
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
+ # @private
902
+ module EventMachine
903
+ # @private
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
+ module EventMachine
948
+ # @private
949
+ class EvmaUDPSocket < DatagramObject
950
+
951
+ class << self
952
+ def create host, port
953
+ sd = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
954
+ sd.bind Socket::pack_sockaddr_in( port, host )
955
+ EvmaUDPSocket.new sd
956
+ end
957
+ end
958
+
959
+ # #eventable_write
960
+ # This really belongs in DatagramObject, but there is some UDP-specific stuff.
961
+ def eventable_write
962
+ 40.times {
963
+ break if @outbound_q.empty?
964
+ begin
965
+ data,target = @outbound_q.first
966
+
967
+ # This damn better be nonblocking.
968
+ io.send data.to_s, 0, target
969
+
970
+ @outbound_q.shift
971
+ rescue Errno::EAGAIN
972
+ # It's not been observed in testing that we ever get here.
973
+ # True to the definition, packets will be accepted and quietly dropped
974
+ # if the system is under pressure.
975
+ break
976
+ rescue EOFError, Errno::ECONNRESET
977
+ @close_scheduled = true
978
+ @outbound_q.clear
979
+ end
980
+ }
981
+ end
982
+
983
+ # Proper nonblocking I/O was added to Ruby 1.8.4 in May 2006.
984
+ # If we have it, then we can read multiple times safely to improve
985
+ # performance.
986
+ def eventable_read
987
+ begin
988
+ if io.respond_to?(:recvfrom_nonblock)
989
+ 40.times {
990
+ data,@return_address = io.recvfrom_nonblock(16384)
991
+ EventMachine::event_callback uuid, ConnectionData, data
992
+ @return_address = nil
993
+ }
994
+ else
995
+ raise "unimplemented datagram-read operation on this Ruby"
996
+ end
997
+ rescue Errno::EAGAIN
998
+ # no-op
999
+ rescue Errno::ECONNRESET, EOFError
1000
+ @close_scheduled = true
1001
+ EventMachine::event_callback uuid, ConnectionUnbound, nil
1002
+ end
1003
+
1004
+ end
1005
+
1006
+
1007
+ def send_data data
1008
+ send_datagram data, @return_address
1009
+ end
1010
+
1011
+ end
1012
+ end
1013
+
1014
+ # load base EM api on top, now that we have the underlying pure ruby
1015
+ # implementation defined
1016
+ require 'eventmachine'
1017
+