crusher-eventmachine 0.12.11

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