eventmachine 1.2.0.dev.2-x64-mingw32

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