crusher-eventmachine 0.12.11

Sign up to get free protection for your applications and to get access to all the features.
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
+