io-reactor 0.05

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # $Date: 2003/07/21 14:21:30 $
4
+ # Copyright (c) 2000 Masatoshi SEKI
5
+ #
6
+ # install.rb is copyrighted free software by Masatoshi SEKI.
7
+ # You can redistribute it and/or modify it under the same term as Ruby.
8
+
9
+ require 'rbconfig'
10
+ require 'find'
11
+ require 'ftools'
12
+
13
+ include Config
14
+
15
+ class Installer
16
+ protected
17
+ def install(from, to, mode = nil, verbose = false)
18
+ str = "install '#{from}' to '#{to}'"
19
+ str += ", mode=#{mode}" if mode
20
+ puts str if verbose
21
+ end
22
+
23
+ protected
24
+ def makedirs(*dirs)
25
+ for d in dirs
26
+ puts "mkdir #{d}"
27
+ end
28
+ end
29
+
30
+ def initialize(test=false)
31
+ @version = CONFIG["MAJOR"]+"."+CONFIG["MINOR"]
32
+ @libdir = File.join(CONFIG["libdir"], "ruby", @version)
33
+ @sitelib = find_site_libdir
34
+ @ftools = (test) ? self : File
35
+ end
36
+ public
37
+ attr_reader(:libdir, :sitelib)
38
+
39
+ private
40
+ def find_site_libdir
41
+ site_libdir = $:.find {|x| x =~ /site_ruby$/}
42
+ if !site_libdir
43
+ site_libdir = File.join(@libdir, "site_ruby")
44
+ elsif site_libdir !~ Regexp::new( Regexp.quote(@version) )
45
+ site_libdir = File.join(site_libdir, @version)
46
+ end
47
+ site_libdir
48
+ end
49
+
50
+ public
51
+ def files_in_dir(dir)
52
+ list = []
53
+ Find.find(dir) do |f|
54
+ list.push(f)
55
+ end
56
+ list
57
+ end
58
+
59
+ public
60
+ def install_files(srcdir, files, destdir=@sitelib)
61
+ path = []
62
+ dir = []
63
+
64
+ for f in files
65
+ next if (f = f[srcdir.length+1..-1]) == nil
66
+ path.push f if File.ftype(File.join(srcdir, f)) == 'file'
67
+ dir |= [ File.dirname(File.join(destdir, f)) ]
68
+ end
69
+ @ftools.makedirs(*dir)
70
+ for f in path
71
+ @ftools.install(File.join(srcdir, f), File.join(destdir, f), nil, true)
72
+ end
73
+ end
74
+
75
+ public
76
+ def install_rb
77
+ intall_files('lib', files_in_dir('lib'))
78
+ end
79
+ end
80
+
81
+ if __FILE__ == $0
82
+ inst = Installer.new(ARGV.shift == '-n')
83
+ inst.install_files('lib', ['lib/io/reactor.rb'])
84
+ end
85
+
@@ -0,0 +1,21 @@
1
+ require 'date'
2
+ Gem::Specification.new do |s|
3
+ s.name = %q{io-reactor}
4
+ s.version = "0.05"
5
+ s.date = Date.today.to_s
6
+ s.summary = %q{An implementation of the Reactor design pattern for multiplexed asynchronous single-thread IO.}
7
+ s.description =<<DESCRIPTION
8
+ An implementation of the Reactor design pattern for multiplexed asynchronous single-thread IO.
9
+ DESCRIPTION
10
+ s.author = %q{Michael Granger}
11
+ s.email = %q{ged@FaerieMUD.org}
12
+ s.homepage = %q{http://www.deveiate.org/code/IO-Reactor.html}
13
+ s.files = Dir.glob('**/*')
14
+ s.require_paths = %w{lib .}
15
+ s.autorequire = %q{io/reactor}
16
+ s.has_rdoc = true
17
+ s.rdoc_options = ["--main", "README"]
18
+ s.extra_rdoc_files = ["README"]
19
+ s.test_files = %w{test.rb}
20
+ s.required_ruby_version = Gem::Version::Requirement.new(">= 1.8.0")
21
+ end
@@ -0,0 +1,317 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # An object-oriented multiplexing asynchronous IO mechanism for Ruby.
4
+ #
5
+ # == Synopsis
6
+ #
7
+ # require 'io/reactor'
8
+ # require 'socket'
9
+ #
10
+ # reactorobj = IO::Reactor::new
11
+ #
12
+ # # Open a listening socket on port 1138 and register it with the
13
+ # # reactor. When a :read event occurs on it, it means an incoming
14
+ # # connection has been established
15
+ # sock = TCPServer::new('localhost', 1138)
16
+ # reactorobj.register( sock, :read ) {|sock,event|
17
+ # case event
18
+ #
19
+ # # Accept the incoming connection, registering it also with
20
+ # # the reactor; events on client sockets are handled by the
21
+ # # #clientHandler method.
22
+ # when :read
23
+ # clsock = sock.accept
24
+ # reactorobj.register( clsock, :read, :write,
25
+ # &method(:clientHandler) )
26
+ #
27
+ # # Errors on the main listening socket cause the whole
28
+ # # reactor to be shut down
29
+ # when :error
30
+ # reactorobj.clear
31
+ # $stderr.puts "Server error: Shutting down"
32
+ #
33
+ # else
34
+ # $stderr.puts "Unhandled event: #{event}"
35
+ # end
36
+ # }
37
+ #
38
+ # # Drive the reactor until it's empty with a single thread
39
+ # Thread::new { reactorobj.poll until reactorobj.empty? }
40
+ #
41
+ # == Author
42
+ #
43
+ # Michael Granger <ged@FaerieMUD.org>
44
+ #
45
+ # Copyright (c) 2002, 2003 The FaerieMUD Consortium. All rights reserved.
46
+ #
47
+ # This module is free software. You may use, modify, and/or redistribute this
48
+ # software under the same terms as Ruby itself.
49
+ #
50
+ # This library is distributed in the hope that it will be useful, but WITHOUT
51
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
52
+ # FOR A PARTICULAR PURPOSE.
53
+ #
54
+ # == Version
55
+ #
56
+ # $Id: reactor.rb,v 1.13 2003/08/04 23:56:14 deveiant Exp $
57
+ #
58
+
59
+ require 'delegate'
60
+ require 'rbconfig'
61
+
62
+ class IO
63
+
64
+ ### An object-oriented multiplexing asynchronous IO reactor class.
65
+ class Reactor
66
+
67
+ ### Class constants
68
+ Version = /([\d\.]+)/.match( %q{$Revision: 1.13 $} )[1]
69
+ Rcsid = %q$Id: reactor.rb,v 1.13 2003/08/04 23:56:14 deveiant Exp $
70
+
71
+ ValidEvents = [:read, :write, :error]
72
+
73
+ ### Create and return a new IO reactor object.
74
+ def initialize
75
+ @handles = Hash::new {|hsh,key|
76
+ hsh[ key ] = {
77
+ :events => [],
78
+ :handler => nil,
79
+ :args => [],
80
+ }
81
+ }
82
+ @pendingEvents = Hash::new {|hsh,key| hsh[ key ] = []}
83
+ end
84
+
85
+
86
+ ######
87
+ public
88
+ ######
89
+
90
+ # The Hash of handles (instances of IO or its subclasses) associated with
91
+ # the reactor. The keys are the IO objects, and the values are a Hash of
92
+ # event/s => handler.
93
+ attr_reader :handles
94
+
95
+ # The Hash of unhandled events which occurred in the last call to #poll,
96
+ # keyed by handle.
97
+ attr_reader :pendingEvents
98
+
99
+
100
+ ### Register the specified IO object with the reactor for events given as
101
+ ### <tt>args</tt>. The reactor will test the given <tt>io</tt> for the
102
+ ### events specified whenever #poll is called. See the #poll method for a
103
+ ### list of valid events. If no events are specified, only <tt>:error</tt>
104
+ ### events will be polled for.
105
+ ###
106
+ ### If a <tt>handler</tt> is specified, it will be called whenever the
107
+ ### <tt>io</tt> has any of the specified <tt>events</tt> occur to it. It
108
+ ### should take at least two parameters: the <tt>io</tt> and the event.
109
+ ###
110
+ ### If +args+ contains any objects except the Symbols '<tt>:read</tt>',
111
+ ### '<tt>:write</tt>', or '<tt>:error</tt>', and a +handler+ is specified,
112
+ ### they will be saved and passed to handler for each event.
113
+ ###
114
+ ### Registering a handle will unregister any previously registered
115
+ ### event/handler+arguments pairs associated with the handle.
116
+ def register( io, *args, &handler )
117
+ events = [:read, :write, :error] & args
118
+ args -= events
119
+
120
+ self.unregister( io )
121
+ self.enableEvents( io, *events )
122
+ if handler
123
+ self.setHandler( io, *args, &handler )
124
+ else
125
+ self.setArgs( io, *args )
126
+ end
127
+
128
+ return self
129
+ end
130
+ alias_method :add, :register
131
+
132
+
133
+ ### Add the specified +events+ to the list that will be polled for on the
134
+ ### given +io+ handle.
135
+ def enableEvents( io, *events )
136
+ @handles[ io ][:events] |= events
137
+ end
138
+
139
+
140
+ ### Remove the specified +events+ from the list that will be polled for on
141
+ ### the given +io+ handle.
142
+ def disableEvents( io, *events )
143
+ raise RuntimeError, "Cannot disable the :error event" if
144
+ events.include?( :error )
145
+ @handles[ io ][:events] -= events
146
+ end
147
+
148
+
149
+ ### Set the handler for events on the given +io+ handle to the specified
150
+ ### +handler+. If any +args+ are present, they will be passed as an exploded
151
+ ### array to the handler for each event. Returns the previously-registered
152
+ ### handler, if any.
153
+ def setHandler( io, *args, &handler )
154
+ rval = @handles[ io ][:handler]
155
+ @handles[ io ][:handler] = handler
156
+ self.setArgs( io, *args )
157
+ return rval
158
+ end
159
+
160
+
161
+ ### Remove and return the handler for events on the given +io+ handle.
162
+ def removeHandler( io )
163
+ rval = @handles[ io ][:handler]
164
+ @handles[ io ][:handler] = nil
165
+ self.removeArgs( io )
166
+ return rval
167
+ end
168
+
169
+
170
+ ### Set the additional arguments to pass to the handler for the given +io+
171
+ ### handle on each event to the given +args+.
172
+ def setArgs( io, *args )
173
+ rval = @handles[ io ][:args]
174
+ @handles[ io ][:args] = args
175
+ return rval
176
+ end
177
+
178
+
179
+ ### Remove the arguments for the given handle to the given +args+.
180
+ def removeArgs( io )
181
+ return @handles[ io ][:args].clear
182
+ end
183
+
184
+
185
+ ### Remove the specified <tt>io</tt> from the receiver's list of registered
186
+ ### handles, if present. Returns the handle if it was registered, or
187
+ ### <tt>nil</tt> if it was not.
188
+ def unregister( io )
189
+ @pendingEvents.delete( io )
190
+ @handles.delete( io )
191
+ end
192
+ alias_method :remove, :unregister
193
+
194
+
195
+ ### Returns true if the specified <tt>io</tt> is registered with the poll
196
+ ### object.
197
+ def registered?( io )
198
+ return @handles.has_key?( io )
199
+ end
200
+
201
+
202
+ ### Clear all registered handles from the poll object. Returns the handles
203
+ ### that were cleared.
204
+ def clear
205
+ rv = @handles.keys
206
+
207
+ @pendingEvents.clear
208
+ @handles.clear
209
+
210
+ return rv
211
+ end
212
+
213
+
214
+
215
+ ### Poll the handles registered to the reactor for pending events. The
216
+ ### following event types are defined:
217
+ ###
218
+ ### [<tt>:read</tt>]
219
+ ### Data may be read from the handle without blocking.
220
+ ### [<tt>:write</tt>]
221
+ ### Data may be written to the handle without blocking.
222
+ ### [<tt>:error</tt>]
223
+ ### An error has occurred on the handle. This event type is always
224
+ ### enabled, regardless of whether or not it is passed as one of the
225
+ ### <tt>events</tt>.
226
+ ###
227
+ ### Any handlers specified when the handles were registered are run for
228
+ ### those handles with events. If a block is given, it will be invoked once
229
+ ### for each handle which doesn't have an explicit handler. If no block is
230
+ ### given, events without explicit handlers are inserted into the reactor's
231
+ ### <tt>pendingEvents</tt> attribute.
232
+ ###
233
+ ### The <tt>timeout</tt> argument is the number of floating-point seconds to
234
+ ### wait for an event before returning (ie., fourth argument to the
235
+ ### underlying <tt>select()</tt> call); negative timeout values will cause
236
+ ### #poll to block until there is at least one event to report.
237
+ ###
238
+ ### This method returns the number of handles on which one or more events
239
+ ### occurred.
240
+ def poll( timeout=-1 ) # :yields: io, eventMask
241
+ timeout = timeout.to_f
242
+ @pendingEvents.clear
243
+ count = 0
244
+
245
+ unless @handles.empty?
246
+ timeout = nil if timeout < 0
247
+ eventedHandles = self.getPendingEvents( timeout )
248
+
249
+ # For each event of each io that had an event happen, call any
250
+ # associated callback, or any provided block, or failing both of
251
+ # those, add the event to the hash of unhandled pending events.
252
+ eventedHandles.each {|io,events|
253
+ count += 1
254
+ events.each {|ev|
255
+ args = @handles[ io ][:args]
256
+
257
+ if @handles[ io ][:handler]
258
+ @handles[ io ][:handler].call( io, ev, *args )
259
+ elsif block_given?
260
+ yield( io, ev, *args )
261
+ else
262
+ @pendingEvents[io].push( ev )
263
+ end
264
+ }
265
+ }
266
+ end
267
+
268
+ return count
269
+ end
270
+
271
+
272
+ ### Returns <tt>true</tt> if no handles are associated with the receiver.
273
+ def empty?
274
+ @handles.empty?
275
+ end
276
+
277
+
278
+ #########
279
+ protected
280
+ #########
281
+
282
+ ### Select on the registered handles, returning a Hash of handles => events
283
+ ### for handles which had events occur.
284
+ def getPendingEvents( timeout )
285
+ eventHandles = IO::select( self.getReadHandles, self.getWriteHandles,
286
+ @handles.keys, timeout ) or return {}
287
+ eventHash = Hash::new {|hsh,io| hsh[io] = []}
288
+
289
+ # Fill in the hash with pending events of each type
290
+ [:read, :write, :error].each_with_index {|event,i|
291
+ eventHandles[i].each {|io| eventHash[io].push( event )}
292
+ }
293
+ return eventHash
294
+ end
295
+
296
+
297
+ ### Return an Array of handles which have handlers for the <tt>:read</tt>
298
+ ### event.
299
+ def getReadHandles
300
+ @handles.
301
+ find_all {|io,hsh| hsh[:events].include?( :read )}.
302
+ collect {|io,hsh| io}
303
+ end
304
+
305
+
306
+ ### Return an Array of handles which have handlers for the <tt>:write</tt>
307
+ ### event.
308
+ def getWriteHandles
309
+ @handles.
310
+ find_all {|io,hsh| hsh[:events].include?( :write )}.
311
+ collect {|io,hsh| io}
312
+ end
313
+
314
+
315
+ end # class Reactor
316
+ end # class IO
317
+
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # MUES Documentation Generation Script
4
+ # $Id: makedocs.rb,v 1.3 2002/09/18 12:33:35 deveiant Exp $
5
+ #
6
+ # Copyright (c) 2001,2002 The FaerieMUD Consortium.
7
+ #
8
+ # This is free software. You may use, modify, and/or redistribute this
9
+ # software under the terms of the Perl Artistic License. (See
10
+ # http://language.perl.com/misc/Artistic.html)
11
+ #
12
+
13
+ # Muck with the load path and the cwd
14
+ $basedir = File::expand_path( $0 ).sub( %r{/makedocs.rb}, '' )
15
+ unless $basedir.empty? || Dir.getwd == $basedir
16
+ $stderr.puts "Changing working directory from '#{Dir.getwd}' to '#$basedir'"
17
+ Dir.chdir( $basedir )
18
+ end
19
+
20
+ $LOAD_PATH.unshift "docs/lib"
21
+
22
+ # Load modules
23
+ require 'getoptlong'
24
+ require 'rdoc/rdoc'
25
+ require "utils"
26
+ include UtilityFunctions
27
+
28
+ opts = GetoptLong.new
29
+ opts.set_options(
30
+ [ '--debug', '-d', GetoptLong::NO_ARGUMENT ],
31
+ [ '--verbose', '-v', GetoptLong::NO_ARGUMENT ]
32
+ )
33
+
34
+ $docsdir = "docs/html"
35
+ $libdirs = %w{poll.c lib examples README}
36
+ opts.each {|opt,val|
37
+ case opt
38
+
39
+ when '--debug'
40
+ $debug = true
41
+
42
+ when '--verbose'
43
+ $verbose = true
44
+
45
+ when '--upload'
46
+ $upload = true
47
+
48
+ end
49
+ }
50
+
51
+
52
+ header "Making documentation in #$docsdir from files in #{$libdirs.join(', ')}."
53
+
54
+ flags = [
55
+ '--main', 'README',
56
+ '--fmt', 'html',
57
+ '--include', 'docs',
58
+ '--op', $docsdir,
59
+ '--title', "Ruby-Poll"
60
+ ]
61
+
62
+ message "Running 'rdoc #{flags.join(' ')} #{$libdirs.join(' ')}'\n" if $verbose
63
+
64
+ unless $debug
65
+ begin
66
+ r = RDoc::RDoc.new
67
+ r.document( flags + $libdirs )
68
+ rescue RDoc::RDocError => e
69
+ $stderr.puts e.message
70
+ exit(1)
71
+ end
72
+ end
73
+
74
+ # rdoc \
75
+ # --all \
76
+ # --inline_source \
77
+ # --main "lib/mues.rb" \
78
+ # --title "Multi-User Environment Server (MUES)" \
79
+ # lib server