eventmachine-win32 0.5.3 → 0.7.0

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.
@@ -0,0 +1,42 @@
1
+ # $Id: eventmachine_version.rb 221 2006-08-07 13:17:51Z blackhedd $
2
+ #
3
+ # Author:: blackhedd (gmail address: garbagecat10).
4
+ # Date:: 8 Apr 2006
5
+ #
6
+ # Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
7
+ #
8
+ # This program is made available under the terms of the GPL version 2.
9
+ #
10
+ # See EventMachine and EventMachine::Connection for documentation and
11
+ # usage examples.
12
+ #
13
+ #----------------------------------------------------------------------------
14
+ #
15
+ # Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
16
+ #
17
+ # Gmail: garbagecat10
18
+ #
19
+ # This program is free software; you can redistribute it and/or modify
20
+ # it under the terms of the GNU General Public License as published by
21
+ # the Free Software Foundation; either version 2 of the License, or
22
+ # (at your option) any later version.
23
+ #
24
+ # This program is distributed in the hope that it will be useful,
25
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
26
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27
+ # GNU General Public License for more details.
28
+ #
29
+ # You should have received a copy of the GNU General Public License
30
+ # along with this program; if not, write to the Free Software
31
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
32
+ #
33
+ #---------------------------------------------------------------------------
34
+ #
35
+ #
36
+
37
+ module EventMachine
38
+
39
+ VERSION = "0.7.0"
40
+
41
+ end
42
+
data/lib/evma.rb ADDED
@@ -0,0 +1,35 @@
1
+ # $Id: evma.rb 88 2006-05-19 03:42:54Z blackhedd $
2
+ #
3
+ # Homepage:: http://rubyeventmachine.com
4
+ # Copyright:: (C) 2006 by Francis Cianfrocca. All Rights Reserved.
5
+ # Email:: gmail address: garbagecat10
6
+ #
7
+ # Available under the GNU Lesser General Public License.
8
+ # See the file COPYING in the distribution for full licensing information.
9
+ #-------------------------------------------------------------------
10
+ # This file is part of Ruby/EventMachine.
11
+ #
12
+ # Ruby/EventMachine is free software; you can redistribute it and/or modify
13
+ # it under the terms of the GNU Lesser General Public License as published by
14
+ # the Free Software Foundation; either version 2.1 of the License, or
15
+ # (at your option) any later version.
16
+ #
17
+ # Ruby/EventMachine is distributed in the hope that it will be useful,
18
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
+ # GNU Lesser General Public License for more details.
21
+ #
22
+ # You should have received a copy of the GNU Lesser General Public License
23
+ # along with Ruby/EventMachine; if not, write to the Free Software
24
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25
+ #-------------------------------------------------------------------
26
+ #
27
+
28
+
29
+ require 'rubyeventmachine'
30
+ require 'evma/reactor'
31
+ require 'evma/callback'
32
+ require 'evma/protocol'
33
+ require 'evma/factory'
34
+ require 'evma/container'
35
+
@@ -0,0 +1,35 @@
1
+ # $Id: callback.rb 88 2006-05-19 03:42:54Z blackhedd $
2
+ #
3
+ # Homepage:: http://rubyeventmachine.com
4
+ # Copyright:: (C) 2006 by Francis Cianfrocca. All Rights Reserved.
5
+ # Email:: gmail address: garbagecat10
6
+ #
7
+ # Available under the GNU Lesser General Public License.
8
+ # See the file COPYING in the distribution for full licensing information.
9
+ #-------------------------------------------------------------------
10
+ # This file is part of Ruby/EventMachine.
11
+ #
12
+ # Ruby/EventMachine is free software; you can redistribute it and/or modify
13
+ # it under the terms of the GNU Lesser General Public License as published by
14
+ # the Free Software Foundation; either version 2.1 of the License, or
15
+ # (at your option) any later version.
16
+ #
17
+ # Ruby/EventMachine is distributed in the hope that it will be useful,
18
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
+ # GNU Lesser General Public License for more details.
21
+ #
22
+ # You should have received a copy of the GNU Lesser General Public License
23
+ # along with Ruby/EventMachine; if not, write to the Free Software
24
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25
+ #-------------------------------------------------------------------
26
+ #
27
+
28
+
29
+ module EventMachine
30
+
31
+ def self.event_callback target, opcode, data
32
+ Evma::Container.callback target, opcode, data
33
+ end
34
+
35
+ end # module EventMachine
@@ -0,0 +1,77 @@
1
+ # $Id: container.rb 90 2006-05-19 04:58:15Z blackhedd $
2
+ #
3
+ # Homepage:: http://rubyeventmachine.com
4
+ # Copyright:: (C) 2006 by Francis Cianfrocca. All Rights Reserved.
5
+ # Email:: gmail address: garbagecat10
6
+ #
7
+ # Available under the GNU Lesser General Public License.
8
+ # See the file COPYING in the distribution for full licensing information.
9
+ #-------------------------------------------------------------------
10
+ # This file is part of Ruby/EventMachine.
11
+ #
12
+ # Ruby/EventMachine is free software; you can redistribute it and/or modify
13
+ # it under the terms of the GNU Lesser General Public License as published by
14
+ # the Free Software Foundation; either version 2.1 of the License, or
15
+ # (at your option) any later version.
16
+ #
17
+ # Ruby/EventMachine is distributed in the hope that it will be useful,
18
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
+ # GNU Lesser General Public License for more details.
21
+ #
22
+ # You should have received a copy of the GNU Lesser General Public License
23
+ # along with Ruby/EventMachine; if not, write to the Free Software
24
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25
+ #-------------------------------------------------------------------
26
+ #
27
+
28
+
29
+ require 'singleton'
30
+
31
+ module Evma
32
+
33
+ class ContainerHasObject < Exception; end
34
+ class UnsupportedCallback < Exception; end
35
+ class UnknownTarget < Exception; end
36
+
37
+ class Container
38
+ include Singleton
39
+
40
+ def initialize
41
+ @objects = {}
42
+ end
43
+
44
+ def self.store obj
45
+ instance.store obj
46
+ end
47
+
48
+ def self.callback target, opcode, data
49
+ instance.callback target, opcode, data
50
+ end
51
+
52
+ def store obj
53
+ sig = obj.signature
54
+ raise ContainerHasObject.new(sig) if @objects.has_key?(sig)
55
+ @objects[sig] = obj
56
+ end
57
+
58
+ def callback target, opcode, data
59
+ case opcode
60
+ when 101 # received data
61
+ obj = @objects[target] or raise UnknownTarget.new( target )
62
+ obj.receive_data data
63
+ when 102 # unbind
64
+ obj = @objects[target] or raise UnknownTarget.new( target )
65
+ obj.unbind
66
+ @objects.delete obj.signature
67
+ when 103 # accept
68
+ obj = @objects[target] or raise UnknownTarget.new( target )
69
+ obj.accept data
70
+ else
71
+ raise UnsupportedCallback.new( opcode.to_s )
72
+ end
73
+ end
74
+
75
+ end # class Container
76
+ end # module Evma
77
+
@@ -0,0 +1,80 @@
1
+ # $Id: factory.rb 90 2006-05-19 04:58:15Z blackhedd $
2
+ #
3
+ # Homepage:: http://rubyeventmachine.com
4
+ # Copyright:: (C) 2006 by Francis Cianfrocca. All Rights Reserved.
5
+ # Email:: gmail address: garbagecat10
6
+ #
7
+ # Available under the GNU Lesser General Public License.
8
+ # See the file COPYING in the distribution for full licensing information.
9
+ #-------------------------------------------------------------------
10
+ # This file is part of Ruby/EventMachine.
11
+ #
12
+ # Ruby/EventMachine is free software; you can redistribute it and/or modify
13
+ # it under the terms of the GNU Lesser General Public License as published by
14
+ # the Free Software Foundation; either version 2.1 of the License, or
15
+ # (at your option) any later version.
16
+ #
17
+ # Ruby/EventMachine is distributed in the hope that it will be useful,
18
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
+ # GNU Lesser General Public License for more details.
21
+ #
22
+ # You should have received a copy of the GNU Lesser General Public License
23
+ # along with Ruby/EventMachine; if not, write to the Free Software
24
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25
+ #-------------------------------------------------------------------
26
+ #
27
+
28
+
29
+ module Evma
30
+ class ProtocolFactory < Protocol
31
+
32
+ #--
33
+ # default implementation raises an exception.
34
+ # we expect subclasses to override this.
35
+ # we can't do anything reasonable here because
36
+ def accept new_object
37
+ # don't bother calling Evma::Reactor.instance, since only Reactor can call accept
38
+ Evma::Container.store Evma::Protocol.new( new_object )
39
+ EventMachine.close_connection new_object, false
40
+ end
41
+
42
+
43
+ end # class ProtocolFactory
44
+ end # module Evma
45
+
46
+ ######################################
47
+
48
+ module Evma
49
+ class TcpSocket
50
+
51
+ def self.connect server, port, protocol_handler = Evma::Protocol
52
+ Evma::Reactor.instance # ensure initialization
53
+ sig = EventMachine.connect_server server, port
54
+ Evma::Container.store protocol_handler.new( sig )
55
+ end
56
+
57
+ end
58
+ end # module Evma
59
+
60
+ ######################################
61
+
62
+ module Evma
63
+ class TcpServerFactory < Evma::ProtocolFactory
64
+
65
+ def initialize server, port, protocol_handler = Evma::Protocol
66
+ Evma::Reactor.instance # ensure initialization
67
+ sig = EventMachine.start_tcp_server server, port
68
+ super sig
69
+ @protocol_handler = protocol_handler
70
+ Evma::Container.store self
71
+ end
72
+
73
+ def accept new_obj
74
+ # don't bother calling Evma::Reactor.instance, since only Reactor can call accept
75
+ Evma::Container.store @protocol_handler.new( new_obj )
76
+ end
77
+
78
+ end
79
+ end # module Evma
80
+
@@ -0,0 +1,89 @@
1
+ # $Id: protocol.rb 90 2006-05-19 04:58:15Z blackhedd $
2
+ #
3
+ # Homepage:: http://rubyeventmachine.com
4
+ # Copyright:: (C) 2006 by Francis Cianfrocca. All Rights Reserved.
5
+ # Email:: gmail address: garbagecat10
6
+ #
7
+ # Available under the GNU Lesser General Public License.
8
+ # See the file COPYING in the distribution for full licensing information.
9
+ #-------------------------------------------------------------------
10
+ # This file is part of Ruby/EventMachine.
11
+ #
12
+ # Ruby/EventMachine is free software; you can redistribute it and/or modify
13
+ # it under the terms of the GNU Lesser General Public License as published by
14
+ # the Free Software Foundation; either version 2.1 of the License, or
15
+ # (at your option) any later version.
16
+ #
17
+ # Ruby/EventMachine is distributed in the hope that it will be useful,
18
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
+ # GNU Lesser General Public License for more details.
21
+ #
22
+ # You should have received a copy of the GNU Lesser General Public License
23
+ # along with Ruby/EventMachine; if not, write to the Free Software
24
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25
+ #-------------------------------------------------------------------
26
+ #
27
+
28
+ module Evma
29
+ class Protocol
30
+
31
+ attr_reader :signature
32
+
33
+ def initialize sig
34
+ @signature = sig
35
+ end
36
+
37
+ def unbind
38
+ end
39
+
40
+ def close
41
+ Evma::Reactor.instance # ensure initialized
42
+ EventMachine.close_connection signature, false
43
+ end
44
+
45
+ def close_after_writing
46
+ Evma::Reactor.instance # ensure initialized
47
+ EventMachine.close_connection signature, true
48
+ end
49
+
50
+ end # class Protocol
51
+ end # module Evma
52
+
53
+
54
+ ###########################################
55
+
56
+ module Evma
57
+ class StreamProtocol < Protocol
58
+
59
+ def initialize sig
60
+ super
61
+ end
62
+
63
+ def send_data data
64
+ Evma::Reactor.instance # ensure initialized
65
+ EventMachine.send_data signature, data, data.length
66
+ end
67
+
68
+ end # class Protocol
69
+ end # module Evma
70
+
71
+
72
+ ###########################################
73
+
74
+ module Evma
75
+ class DatagramProtocol < Protocol
76
+
77
+ def initialize sig
78
+ super
79
+ end
80
+
81
+ def send_message data
82
+ Evma::Reactor.instance # ensure initialized
83
+ raise "unimplemented"
84
+ end
85
+
86
+ end # class Protocol
87
+ end # module Evma
88
+
89
+
@@ -0,0 +1,50 @@
1
+ # $Id: reactor.rb 83 2006-05-18 21:36:31Z blackhedd $
2
+ #
3
+ # Homepage:: http://rubyeventmachine.com
4
+ # Copyright:: (C) 2006 by Francis Cianfrocca. All Rights Reserved.
5
+ # Email:: gmail address: garbagecat10
6
+ #
7
+ # Available under the GNU Lesser General Public License.
8
+ # See the file COPYING in the distribution for full licensing information.
9
+ #-------------------------------------------------------------------
10
+ # This file is part of Ruby/EventMachine.
11
+ #
12
+ # Ruby/EventMachine is free software; you can redistribute it and/or modify
13
+ # it under the terms of the GNU Lesser General Public License as published by
14
+ # the Free Software Foundation; either version 2.1 of the License, or
15
+ # (at your option) any later version.
16
+ #
17
+ # Ruby/EventMachine is distributed in the hope that it will be useful,
18
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
+ # GNU Lesser General Public License for more details.
21
+ #
22
+ # You should have received a copy of the GNU Lesser General Public License
23
+ # along with Ruby/EventMachine; if not, write to the Free Software
24
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25
+ #-------------------------------------------------------------------
26
+ #
27
+
28
+
29
+ require 'singleton'
30
+
31
+ module Evma
32
+ class Reactor
33
+ include Singleton
34
+
35
+ #--
36
+ def initialize
37
+ EventMachine.initialize_event_machine
38
+ end
39
+
40
+ #--
41
+ #
42
+ def run
43
+ EventMachine.run_machine
44
+ end
45
+
46
+ end # class Reactor
47
+ end # module Evma
48
+
49
+
50
+
@@ -0,0 +1,714 @@
1
+ # $Id: pr_eventmachine.rb 264 2006-10-05 16:33:22Z blackhedd $
2
+ #
3
+ # Author:: blackhedd (gmail address: garbagecat10).
4
+ # Date:: 8 Apr 2006
5
+ #
6
+ # Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
7
+ #
8
+ # This program is made available under the terms of the GPL version 2.
9
+ #
10
+ # See EventMachine and EventMachine::Connection for documentation and
11
+ # usage examples.
12
+ #
13
+ #----------------------------------------------------------------------------
14
+ #
15
+ # Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
16
+ #
17
+ # Gmail: garbagecat10
18
+ #
19
+ # This program is free software; you can redistribute it and/or modify
20
+ # it under the terms of the GNU General Public License as published by
21
+ # the Free Software Foundation; either version 2 of the License, or
22
+ # (at your option) any later version.
23
+ #
24
+ # This program is distributed in the hope that it will be useful,
25
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
26
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27
+ # GNU General Public License for more details.
28
+ #
29
+ # You should have received a copy of the GNU General Public License
30
+ # along with this program; if not, write to the Free Software
31
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
32
+ #
33
+ #---------------------------------------------------------------------------
34
+ #
35
+ #
36
+
37
+ # TODO List:
38
+ # TCP-connects currently assume non-blocking connect is available- need to
39
+ # degrade automatically on versions of Ruby prior to June 2006.
40
+ #
41
+
42
+ require 'singleton'
43
+ require 'forwardable'
44
+ require 'socket'
45
+ require 'fcntl'
46
+
47
+
48
+ module EventMachine
49
+
50
+
51
+ class << self
52
+ # This is mostly useful for automated tests.
53
+ # Return a distinctive symbol so the caller knows whether he's dealing
54
+ # with an extension or with a pure-Ruby library.
55
+ def library_type
56
+ :pure_ruby
57
+ end
58
+
59
+ # #initialize_event_machine
60
+ def initialize_event_machine
61
+ Reactor.instance.initialize_for_run
62
+ end
63
+
64
+ # #add_oneshot_timer
65
+ #--
66
+ # Changed 04Oct06: intervals from the caller are now in milliseconds, but our native-ruby
67
+ # processor still wants them in seconds.
68
+ def add_oneshot_timer interval
69
+ Reactor.instance.install_oneshot_timer(interval / 1000)
70
+ end
71
+
72
+ # run_machine
73
+ def run_machine
74
+ Reactor.instance.run
75
+ end
76
+
77
+ # release_machine. Probably a no-op.
78
+ def release_machine
79
+ end
80
+
81
+ # #stop
82
+ def stop
83
+ Reactor.instance.stop
84
+ end
85
+
86
+ # #connect_server. Return a connection descriptor to the caller.
87
+ # TODO, what do we return here if we can't connect?
88
+ def connect_server host, port
89
+ EvmaTCPClient.connect(host, port).uuid
90
+ end
91
+
92
+ # #send_data
93
+ def send_data target, data, datalength
94
+ selectable = Reactor.instance.get_selectable( target ) or raise "unknown send_data target"
95
+ selectable.send_data data
96
+ end
97
+
98
+ # #close_connection
99
+ # The extension version does NOT raise any kind of an error if an attempt is made
100
+ # to close a non-existent connection. Not sure whether we should. For now, we'll
101
+ # raise an error here in that case.
102
+ def close_connection target, after_writing
103
+ selectable = Reactor.instance.get_selectable( target ) or raise "unknown close_connection target"
104
+ selectable.schedule_close after_writing
105
+ end
106
+
107
+ # #start_tcp_server
108
+ def start_tcp_server host, port
109
+ (s = EvmaTCPServer.start_server host, port) or raise "no acceptor"
110
+ s.uuid
111
+ end
112
+
113
+ # #signal_loopbreak
114
+ def signal_loopbreak
115
+ Reactor.instance.signal_loopbreak
116
+ end
117
+
118
+ # #get_peername
119
+ def get_peername sig
120
+ selectable = Reactor.instance.get_selectable( sig ) or raise "unknown get_peername target"
121
+ selectable.get_peername
122
+ end
123
+
124
+ # #open_udp_socket
125
+ def open_udp_socket host, port
126
+ EvmaUDPSocket.create(host, port).uuid
127
+ end
128
+
129
+ # #send_datagram. This is currently only for UDP!
130
+ # We need to make it work with unix-domain sockets as well.
131
+ def send_datagram target, data, datalength, host, port
132
+ selectable = Reactor.instance.get_selectable( target ) or raise "unknown send_data target"
133
+ selectable.send_datagram data, Socket::pack_sockaddr_in(port, host)
134
+ end
135
+
136
+
137
+ # #set_timer_quantum in milliseconds. The underlying Reactor function wants a (possibly
138
+ # fractional) number of seconds.
139
+ def set_timer_quantum interval
140
+ Reactor.instance.set_timer_quantum(( 1.0 * interval) / 1000.0)
141
+ end
142
+
143
+ end
144
+
145
+ end
146
+
147
+
148
+ #-----------------------------------------------------------------
149
+
150
+ module EventMachine
151
+
152
+ class Error < Exception; end
153
+
154
+ end
155
+
156
+ #-----------------------------------------------------------------
157
+
158
+ module EventMachine
159
+
160
+ # Factored out so we can substitute other implementations
161
+ # here if desired, such as the one in ActiveRBAC.
162
+ module UuidGenerator
163
+
164
+ def self.generate
165
+ if @ix and @ix >= 10000
166
+ @ix = nil
167
+ @seed = nil
168
+ end
169
+
170
+ @seed ||= `uuidgen`.chomp.gsub(/-/,"")
171
+ @ix ||= 0
172
+
173
+ "#{@seed}#{@ix += 1}"
174
+ end
175
+
176
+ end
177
+
178
+ end
179
+
180
+ #-----------------------------------------------------------------
181
+
182
+ module EventMachine
183
+
184
+ TimerFired = 100
185
+ ConnectionData = 101
186
+ ConnectionUnbound = 102
187
+ ConnectionAccepted = 103
188
+ ConnectionCompleted = 104
189
+ LoopbreakSignalled = 105
190
+
191
+ end
192
+
193
+ #-----------------------------------------------------------------
194
+
195
+ module EventMachine
196
+ class Reactor
197
+ include Singleton
198
+
199
+ def initialize
200
+ initialize_for_run
201
+ end
202
+
203
+ def install_oneshot_timer interval
204
+ uuid = UuidGenerator::generate
205
+ @timers << [Time.now + interval, uuid]
206
+ @timers.sort! {|a,b| a.first <=> b.first}
207
+ uuid
208
+ end
209
+
210
+ # Called before run, this is a good place to clear out arrays
211
+ # with cruft that may be left over from a previous run.
212
+ def initialize_for_run
213
+ @running = false
214
+ @stop_scheduled = false
215
+ @selectables ||= {}; @selectables.clear
216
+ @timers = []
217
+ set_timer_quantum(0.5)
218
+ end
219
+
220
+ def add_selectable io
221
+ @selectables[io.uuid] = io
222
+ end
223
+
224
+ def get_selectable uuid
225
+ @selectables[uuid]
226
+ end
227
+
228
+ def run
229
+ raise Error.new( "already running" ) if @running
230
+ @running = true
231
+ open_loopbreaker
232
+
233
+ loop {
234
+ break if @stop_scheduled
235
+ run_timers
236
+ break if @stop_scheduled
237
+ crank_selectables
238
+ }
239
+
240
+ close_loopbreaker
241
+ @selectables.each {|k, io| io.close}
242
+ @selectables.clear
243
+
244
+ @running = false
245
+ end
246
+
247
+ def run_timers
248
+ now = Time.now
249
+ while @timers.length > 0 and @timers.first.first <= now
250
+ t = @timers.shift
251
+ EventMachine::event_callback "", TimerFired, t.last
252
+ end
253
+ end
254
+
255
+ def crank_selectables
256
+ #$stderr.write 'R'
257
+
258
+ readers = @selectables.values.select {|io| io.select_for_reading?}
259
+ writers = @selectables.values.select {|io| io.select_for_writing?}
260
+
261
+ s = select( readers, writers, nil, @timer_quantum)
262
+
263
+ s and s[1] and s[1].each {|w| w.eventable_write }
264
+ s and s[0] and s[0].each {|r| r.eventable_read }
265
+
266
+ @selectables.delete_if {|k,io|
267
+ if io.close_scheduled?
268
+ io.close
269
+ true
270
+ end
271
+ }
272
+ end
273
+
274
+ # #stop
275
+ def stop
276
+ raise Error.new( "not running") unless @running
277
+ @stop_scheduled = true
278
+ end
279
+
280
+ def open_loopbreaker
281
+ @loopbreak_writer.close if @loopbreak_writer
282
+ rd,@loopbreak_writer = IO.pipe
283
+ LoopbreakReader.new rd
284
+ end
285
+
286
+ def close_loopbreaker
287
+ @loopbreak_writer.close
288
+ @loopbreak_writer = nil
289
+ end
290
+
291
+ def signal_loopbreak
292
+ @loopbreak_writer.write '+' if @loopbreak_writer
293
+ end
294
+
295
+ def set_timer_quantum interval_in_seconds
296
+ @timer_quantum = interval_in_seconds
297
+ end
298
+
299
+ end
300
+
301
+ end
302
+
303
+
304
+ #--------------------------------------------------------------
305
+
306
+ class IO
307
+ extend Forwardable
308
+ def_delegator :@my_selectable, :close_scheduled?
309
+ def_delegator :@my_selectable, :select_for_reading?
310
+ def_delegator :@my_selectable, :select_for_writing?
311
+ def_delegator :@my_selectable, :eventable_read
312
+ def_delegator :@my_selectable, :eventable_write
313
+ def_delegator :@my_selectable, :uuid
314
+ def_delegator :@my_selectable, :send_data
315
+ def_delegator :@my_selectable, :schedule_close
316
+ def_delegator :@my_selectable, :get_peername
317
+ def_delegator :@my_selectable, :send_datagram
318
+ end
319
+
320
+ #--------------------------------------------------------------
321
+
322
+ module EventMachine
323
+ class Selectable
324
+
325
+ attr_reader :io, :uuid
326
+
327
+ def initialize io
328
+ @uuid = UuidGenerator.generate
329
+ @io = io
330
+
331
+ m = @io.fcntl(Fcntl::F_GETFL, 0)
332
+ @io.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK | m)
333
+ # TODO, should set CLOEXEC on Unix?
334
+
335
+ @close_scheduled = false
336
+ @close_requested = false
337
+
338
+ se = self; @io.instance_eval { @my_selectable = se }
339
+ Reactor.instance.add_selectable @io
340
+ end
341
+
342
+ def close_scheduled?
343
+ @close_scheduled
344
+ end
345
+
346
+ def select_for_reading?
347
+ false
348
+ end
349
+
350
+ def select_for_writing?
351
+ false
352
+ end
353
+
354
+ def get_peername
355
+ nil
356
+ end
357
+
358
+ end
359
+
360
+ end
361
+
362
+ #--------------------------------------------------------------
363
+
364
+
365
+ module EventMachine
366
+
367
+ class StreamObject < Selectable
368
+ def initialize io
369
+ super io
370
+ @outbound_q = []
371
+ end
372
+
373
+ # If we have to close, or a close-after-writing has been requested,
374
+ # then don't read any more data.
375
+ def select_for_reading?
376
+ true unless (@close_scheduled || @close_requested)
377
+ end
378
+
379
+ # If we have to close, don't select for writing.
380
+ # Otherwise, see if the protocol is ready to close.
381
+ # If not, see if he has data to send.
382
+ # If a close-after-writing has been requested and the outbound queue
383
+ # is empty, convert the status to close_scheduled.
384
+ def select_for_writing?
385
+ unless @close_scheduled
386
+ if @outbound_q.empty?
387
+ @close_scheduled = true if @close_requested
388
+ false
389
+ else
390
+ true
391
+ end
392
+ end
393
+ end
394
+
395
+ # Proper nonblocking I/O was added to Ruby 1.8.4 in May 2006.
396
+ # If we have it, then we can read multiple times safely to improve
397
+ # performance.
398
+ # TODO, coalesce multiple reads into a single event.
399
+ # TODO, do the function check somewhere else and cache it.
400
+ def eventable_read
401
+ begin
402
+ if io.respond_to?(:read_nonblock)
403
+ 10.times {
404
+ data = io.read_nonblock(4096)
405
+ EventMachine::event_callback uuid, ConnectionData, data
406
+ }
407
+ else
408
+ data = io.sysread(4096)
409
+ EventMachine::event_callback uuid, ConnectionData, data
410
+ end
411
+ rescue Errno::EAGAIN
412
+ # no-op
413
+ rescue Errno::ECONNRESET, EOFError
414
+ @close_scheduled = true
415
+ EventMachine::event_callback uuid, ConnectionUnbound, nil
416
+ end
417
+
418
+ end
419
+
420
+ # Provisional implementation. Will be re-implemented in subclasses.
421
+ # TODO: Complete this implementation. As it stands, this only writes
422
+ # a single packet per cycle. Highly inefficient, but required unless
423
+ # we're running on a Ruby with proper nonblocking I/O (Ruby 1.8.4
424
+ # built from sources from May 25, 2006 or newer).
425
+ # We need to improve the loop so it writes multiple times, however
426
+ # not more than a certain number of bytes per cycle, otherwise
427
+ # one busy connection could hog output buffers and slow down other
428
+ # connections. Also we should coalesce small writes.
429
+ # URGENT TODO: Coalesce small writes. They are a performance killer.
430
+ def eventable_write
431
+ # coalesce the outbound array here, perhaps
432
+ while data = @outbound_q.shift do
433
+ begin
434
+ data = data.to_s
435
+ w = if io.respond_to?(:write_nonblock)
436
+ io.write_nonblock data
437
+ else
438
+ io.syswrite data
439
+ end
440
+
441
+ if w < data.length
442
+ $outbound_q.unshift data[w..-1]
443
+ break
444
+ end
445
+ rescue Errno::EAGAIN
446
+ @outbound_q.unshift data
447
+ rescue EOFError, Errno::ECONNRESET
448
+ @close_scheduled = true
449
+ @outbound_q.clear
450
+ end
451
+ end
452
+
453
+ end
454
+
455
+ # #send_data
456
+ def send_data data
457
+ # TODO, coalesce here perhaps by being smarter about appending to @outbound_q.last?
458
+ unless @close_scheduled or @close_requested or !data or data.length <= 0
459
+ @outbound_q << data.to_s
460
+ end
461
+ end
462
+
463
+ # #schedule_close
464
+ # The application wants to close the connection.
465
+ def schedule_close after_writing
466
+ if after_writing
467
+ @close_requested = true
468
+ else
469
+ @close_scheduled = true
470
+ end
471
+ end
472
+
473
+ # #get_peername
474
+ # This is defined in the normal way on connected stream objects.
475
+ # Return an object that is suitable for passing to Socket#unpack_sockaddr_in or variants.
476
+ # We could also use a convenience method that did the unpacking automatically.
477
+ def get_peername
478
+ io.getpeername
479
+ end
480
+
481
+ end
482
+
483
+
484
+ end
485
+
486
+
487
+ #--------------------------------------------------------------
488
+
489
+
490
+
491
+ module EventMachine
492
+ class EvmaTCPClient < StreamObject
493
+
494
+ def self.connect host, port
495
+ sd = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
496
+ begin
497
+ # TODO, this assumes a current Ruby snapshot.
498
+ # We need to degrade to a nonblocking connect otherwise.
499
+ sd.connect_nonblock( Socket.pack_sockaddr_in( port, host ))
500
+ rescue Errno::EINPROGRESS
501
+ end
502
+ EvmaTCPClient.new sd
503
+ end
504
+
505
+
506
+ def initialize io
507
+ super
508
+ @pending = true
509
+ end
510
+
511
+
512
+ def select_for_writing?
513
+ @pending ? true : super
514
+ end
515
+
516
+ def select_for_reading?
517
+ @pending ? false : super
518
+ end
519
+
520
+ def eventable_write
521
+ if @pending
522
+ @pending = false
523
+ EventMachine::event_callback uuid, ConnectionCompleted, ""
524
+ else
525
+ super
526
+ end
527
+ end
528
+
529
+
530
+
531
+ end
532
+ end
533
+
534
+
535
+ #--------------------------------------------------------------
536
+
537
+ module EventMachine
538
+ class EvmaTCPServer < Selectable
539
+
540
+ class << self
541
+ # Versions of ruby 1.8.4 later than May 26 2006 will work properly
542
+ # with an object of type TCPServer. Prior versions won't so we
543
+ # play it safe and just build a socket.
544
+ #
545
+ def start_server host, port
546
+ sd = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
547
+ sd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true )
548
+ sd.bind( Socket.pack_sockaddr_in( port, host ))
549
+ sd.listen( 50 ) # 5 is what you see in all the books. Ain't enough.
550
+ EvmaTCPServer.new sd
551
+ end
552
+ end
553
+
554
+ def initialize io
555
+ super io
556
+ end
557
+
558
+
559
+ def select_for_reading?
560
+ true
561
+ end
562
+
563
+ #--
564
+ # accept_nonblock returns an array consisting of the accepted
565
+ # socket and a sockaddr_in which names the peer.
566
+ # Don't accept more than 10 at a time.
567
+ def eventable_read
568
+ begin
569
+ 10.times {
570
+ descriptor,peername = io.accept_nonblock
571
+ sd = StreamObject.new descriptor
572
+ EventMachine::event_callback uuid, ConnectionAccepted, sd.uuid
573
+ }
574
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN
575
+ end
576
+ end
577
+
578
+
579
+ end
580
+ end
581
+
582
+
583
+
584
+ #--------------------------------------------------------------
585
+
586
+ module EventMachine
587
+ class LoopbreakReader < Selectable
588
+
589
+ def select_for_reading?
590
+ true
591
+ end
592
+
593
+ def eventable_read
594
+ io.sysread(128)
595
+ EventMachine::event_callback "", LoopbreakSignalled, ""
596
+ end
597
+
598
+ end
599
+ end
600
+
601
+ #--------------------------------------------------------------
602
+
603
+
604
+ module EventMachine
605
+
606
+ class DatagramObject < Selectable
607
+ def initialize io
608
+ super io
609
+ @outbound_q = []
610
+ end
611
+
612
+ # #send_datagram
613
+ def send_datagram data, target
614
+ # TODO, coalesce here perhaps by being smarter about appending to @outbound_q.last?
615
+ unless @close_scheduled or @close_requested
616
+ @outbound_q << [data.to_s, target]
617
+ end
618
+ end
619
+
620
+ # #select_for_writing?
621
+ def select_for_writing?
622
+ unless @close_scheduled
623
+ if @outbound_q.empty?
624
+ @close_scheduled = true if @close_requested
625
+ false
626
+ else
627
+ true
628
+ end
629
+ end
630
+ end
631
+
632
+ # #select_for_reading?
633
+ def select_for_reading?
634
+ true
635
+ end
636
+
637
+
638
+ end
639
+
640
+
641
+ end
642
+
643
+
644
+ #--------------------------------------------------------------
645
+
646
+ module EventMachine
647
+ class EvmaUDPSocket < DatagramObject
648
+
649
+ class << self
650
+ def create host, port
651
+ sd = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
652
+ sd.bind Socket::pack_sockaddr_in( port, host )
653
+ EvmaUDPSocket.new sd
654
+ end
655
+ end
656
+
657
+ # #eventable_write
658
+ # This really belongs in DatagramObject, but there is some UDP-specific stuff.
659
+ def eventable_write
660
+ 40.times {
661
+ break if @outbound_q.empty?
662
+ begin
663
+ data,target = @outbound_q.first
664
+
665
+ # This damn better be nonblocking.
666
+ io.send data.to_s, 0, target
667
+
668
+ @outbound_q.shift
669
+ rescue Errno::EAGAIN
670
+ # It's not been observed in testing that we ever get here.
671
+ # True to the definition, packets will be accepted and quietly dropped
672
+ # if the system is under pressure.
673
+ break
674
+ rescue EOFError, Errno::ECONNRESET
675
+ @close_scheduled = true
676
+ @outbound_q.clear
677
+ end
678
+ }
679
+ end
680
+
681
+ # Proper nonblocking I/O was added to Ruby 1.8.4 in May 2006.
682
+ # If we have it, then we can read multiple times safely to improve
683
+ # performance.
684
+ def eventable_read
685
+ begin
686
+ if io.respond_to?(:recvfrom_nonblock)
687
+ 40.times {
688
+ data,@return_address = io.recvfrom_nonblock(16384)
689
+ EventMachine::event_callback uuid, ConnectionData, data
690
+ @return_address = nil
691
+ }
692
+ else
693
+ raise "unimplemented datagram-read operation on this Ruby"
694
+ end
695
+ rescue Errno::EAGAIN
696
+ # no-op
697
+ rescue Errno::ECONNRESET, EOFError
698
+ @close_scheduled = true
699
+ EventMachine::event_callback uuid, ConnectionUnbound, nil
700
+ end
701
+
702
+ end
703
+
704
+
705
+ def send_data data
706
+ send_datagram data, @return_address
707
+ end
708
+
709
+ end
710
+ end
711
+
712
+ #--------------------------------------------------------------
713
+
714
+