io-reactor 0.05

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,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