io-reactor 0.05 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +482 -217
- data/LICENSE +27 -0
- data/README +1 -1
- data/Rakefile +257 -0
- data/examples/chatserver.rb +1 -1
- data/examples/simple.rb +59 -0
- data/examples/simpleserver.rb +44 -0
- data/lib/io/reactor.rb +114 -87
- data/rake/dependencies.rb +62 -0
- data/rake/helpers.rb +384 -0
- data/rake/manual.rb +384 -0
- data/rake/packaging.rb +112 -0
- data/rake/publishing.rb +302 -0
- data/rake/rdoc.rb +31 -0
- data/rake/style.rb +62 -0
- data/rake/svn.rb +468 -0
- data/rake/testing.rb +191 -0
- data/rake/verifytask.rb +64 -0
- data/spec/io/reactor_spec.rb +269 -0
- metadata +71 -45
- data/CATALOG +0 -10
- data/install.rb +0 -85
- data/io-reactor.gemspec +0 -21
- data/makedocs.rb +0 -79
- data/test.rb +0 -212
- data/utils.rb +0 -484
data/LICENSE
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Copyright (c) 2008, Michael Granger
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
* Redistributions of source code must retain the above copyright notice,
|
8
|
+
this list of conditions and the following disclaimer.
|
9
|
+
|
10
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
12
|
+
and/or other materials provided with the distribution.
|
13
|
+
|
14
|
+
* Neither the name of the author/s, nor the names of the project's
|
15
|
+
contributors may be used to endorse or promote products derived from this
|
16
|
+
software without specific prior written permission.
|
17
|
+
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
19
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
20
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
22
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
23
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
24
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
25
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
26
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
27
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README
CHANGED
data/Rakefile
ADDED
@@ -0,0 +1,257 @@
|
|
1
|
+
#!rake
|
2
|
+
#
|
3
|
+
# IO-Reactor rakefile
|
4
|
+
#
|
5
|
+
# Based on various other Rakefiles, especially one by Ben Bleything
|
6
|
+
#
|
7
|
+
# Copyright (c) 2008 The FaerieMUD Consortium
|
8
|
+
#
|
9
|
+
# Authors:
|
10
|
+
# * Michael Granger <ged@FaerieMUD.org>
|
11
|
+
#
|
12
|
+
|
13
|
+
BEGIN {
|
14
|
+
require 'pathname'
|
15
|
+
basedir = Pathname.new( __FILE__ ).dirname
|
16
|
+
|
17
|
+
libdir = basedir + "lib"
|
18
|
+
extdir = basedir + "ext"
|
19
|
+
|
20
|
+
$LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
|
21
|
+
$LOAD_PATH.unshift( extdir.to_s ) unless $LOAD_PATH.include?( extdir.to_s )
|
22
|
+
}
|
23
|
+
|
24
|
+
|
25
|
+
require 'rbconfig'
|
26
|
+
require 'rubygems'
|
27
|
+
require 'rake'
|
28
|
+
require 'rake/rdoctask'
|
29
|
+
require 'rake/testtask'
|
30
|
+
require 'rake/packagetask'
|
31
|
+
require 'rake/clean'
|
32
|
+
|
33
|
+
$dryrun = false
|
34
|
+
|
35
|
+
### Config constants
|
36
|
+
BASEDIR = Pathname.new( __FILE__ ).dirname.relative_path_from( Pathname.getwd )
|
37
|
+
LIBDIR = BASEDIR + 'lib'
|
38
|
+
EXTDIR = BASEDIR + 'ext'
|
39
|
+
DOCSDIR = BASEDIR + 'docs'
|
40
|
+
PKGDIR = BASEDIR + 'pkg'
|
41
|
+
|
42
|
+
PKG_NAME = 'io-reactor'
|
43
|
+
PKG_SUMMARY = 'Multiplexed IO Reactor class'
|
44
|
+
VERSION_FILE = LIBDIR + 'io/reactor.rb'
|
45
|
+
PKG_VERSION = VERSION_FILE.read[ /VERSION = '(\d+\.\d+\.\d+)'/, 1 ]
|
46
|
+
PKG_FILE_NAME = "#{PKG_NAME.downcase}-#{PKG_VERSION}"
|
47
|
+
GEM_FILE_NAME = "#{PKG_FILE_NAME}.gem"
|
48
|
+
|
49
|
+
ARTIFACTS_DIR = Pathname.new( ENV['CC_BUILD_ARTIFACTS'] || 'artifacts' )
|
50
|
+
|
51
|
+
TEXT_FILES = %w( Rakefile ChangeLog README LICENSE ).collect {|filename| BASEDIR + filename }
|
52
|
+
LIB_FILES = Pathname.glob( LIBDIR + '**/*.rb' ).delete_if {|item| item =~ /\.svn/ }
|
53
|
+
EXT_FILES = Pathname.glob( EXTDIR + '**/*.{c,h,rb}' ).delete_if {|item| item =~ /\.svn/ }
|
54
|
+
|
55
|
+
SPECDIR = BASEDIR + 'spec'
|
56
|
+
SPEC_FILES = Pathname.glob( SPECDIR + '**/*_spec.rb' ).delete_if {|item| item =~ /\.svn/ }
|
57
|
+
|
58
|
+
TESTDIR = BASEDIR + 'tests'
|
59
|
+
TEST_FILES = Pathname.glob( TESTDIR + '**/*.tests.rb' ).delete_if {|item| item =~ /\.svn/ }
|
60
|
+
|
61
|
+
RAKE_TASKDIR = BASEDIR + 'rake'
|
62
|
+
RAKE_TASKLIBS = Pathname.glob( RAKE_TASKDIR + '*.rb' )
|
63
|
+
|
64
|
+
LOCAL_RAKEFILE = BASEDIR + 'Rakefile.local'
|
65
|
+
|
66
|
+
EXTRA_PKGFILES = []
|
67
|
+
EXTRA_PKGFILES.concat Pathname.glob( BASEDIR + 'examples/*.rb' ).delete_if {|item| item =~ /\.svn/ }
|
68
|
+
|
69
|
+
RELEASE_FILES = TEXT_FILES +
|
70
|
+
SPEC_FILES +
|
71
|
+
TEST_FILES +
|
72
|
+
LIB_FILES +
|
73
|
+
EXT_FILES +
|
74
|
+
RAKE_TASKLIBS +
|
75
|
+
EXTRA_PKGFILES
|
76
|
+
|
77
|
+
RELEASE_FILES << LOCAL_RAKEFILE if LOCAL_RAKEFILE.exist?
|
78
|
+
|
79
|
+
COVERAGE_MINIMUM = ENV['COVERAGE_MINIMUM'] ? Float( ENV['COVERAGE_MINIMUM'] ) : 85.0
|
80
|
+
RCOV_EXCLUDES = 'spec,tests,/Library/Ruby,/var/lib,/usr/local/lib'
|
81
|
+
RCOV_OPTS = [
|
82
|
+
'--exclude', RCOV_EXCLUDES,
|
83
|
+
'--xrefs',
|
84
|
+
'--save',
|
85
|
+
'--callsites',
|
86
|
+
#'--aggregate', 'coverage.data' # <- doesn't work as of 0.8.1.2.0
|
87
|
+
]
|
88
|
+
|
89
|
+
|
90
|
+
# Subversion constants -- directory names for releases and tags
|
91
|
+
SVN_TRUNK_DIR = 'trunk'
|
92
|
+
SVN_RELEASES_DIR = 'releases'
|
93
|
+
SVN_BRANCHES_DIR = 'branches'
|
94
|
+
SVN_TAGS_DIR = 'tags'
|
95
|
+
|
96
|
+
SVN_DOTDIR = BASEDIR + '.svn'
|
97
|
+
SVN_ENTRIES = SVN_DOTDIR + 'entries'
|
98
|
+
|
99
|
+
|
100
|
+
### Load some task libraries that need to be loaded early
|
101
|
+
require RAKE_TASKDIR + 'helpers.rb'
|
102
|
+
require RAKE_TASKDIR + 'svn.rb'
|
103
|
+
require RAKE_TASKDIR + 'verifytask.rb'
|
104
|
+
|
105
|
+
# Define some constants that depend on the 'svn' tasklib
|
106
|
+
PKG_BUILD = get_svn_rev( BASEDIR ) || 0
|
107
|
+
SNAPSHOT_PKG_NAME = "#{PKG_FILE_NAME}.#{PKG_BUILD}"
|
108
|
+
SNAPSHOT_GEM_NAME = "#{SNAPSHOT_PKG_NAME}.gem"
|
109
|
+
|
110
|
+
# Documentation constants
|
111
|
+
RDOC_OPTIONS = [
|
112
|
+
'-w', '4',
|
113
|
+
'-SHN',
|
114
|
+
'-i', '.',
|
115
|
+
'-m', 'README',
|
116
|
+
'-W', 'http://deveiate.org/projects/IO-Reactor//browser/trunk/'
|
117
|
+
]
|
118
|
+
|
119
|
+
# Release constants
|
120
|
+
SMTP_HOST = 'mail.faeriemud.org'
|
121
|
+
SMTP_PORT = 465 # SMTP + SSL
|
122
|
+
|
123
|
+
# Project constants
|
124
|
+
PROJECT_HOST = 'deveiate.org'
|
125
|
+
PROJECT_PUBDIR = "/usr/local/www/public/code"
|
126
|
+
PROJECT_DOCDIR = "#{PROJECT_PUBDIR}/#{PKG_NAME}"
|
127
|
+
PROJECT_SCPPUBURL = "#{PROJECT_HOST}:#{PROJECT_PUBDIR}"
|
128
|
+
PROJECT_SCPDOCURL = "#{PROJECT_HOST}:#{PROJECT_DOCDIR}"
|
129
|
+
|
130
|
+
# Rubyforge stuff
|
131
|
+
RUBYFORGE_GROUP = 'deveiate'
|
132
|
+
RUBYFORGE_PROJECT = 'io-reactor'
|
133
|
+
|
134
|
+
# Gem dependencies: gemname => version
|
135
|
+
DEPENDENCIES = {
|
136
|
+
}
|
137
|
+
|
138
|
+
# Non-gem requirements: packagename => version
|
139
|
+
REQUIREMENTS = {
|
140
|
+
}
|
141
|
+
|
142
|
+
# RubyGem specification
|
143
|
+
GEMSPEC = Gem::Specification.new do |gem|
|
144
|
+
gem.name = PKG_NAME.downcase
|
145
|
+
gem.version = PKG_VERSION
|
146
|
+
|
147
|
+
gem.summary = PKG_SUMMARY
|
148
|
+
gem.description = <<-EOD
|
149
|
+
A pure-Ruby implementation of an asynchronous multiplexed IO
|
150
|
+
Reactor which is based on the Reactor design pattern found in _Pattern-Oriented
|
151
|
+
Software Architecture, Volume 2: Patterns for Concurrent and Networked
|
152
|
+
Objects_. It allows a single thread to demultiplex and dispatch events from one
|
153
|
+
or more IO objects to the appropriate handler.
|
154
|
+
EOD
|
155
|
+
|
156
|
+
gem.authors = 'Michael Granger'
|
157
|
+
gem.email = 'ged@FaerieMUD.org'
|
158
|
+
gem.homepage = 'http://deveiate.org/projects/IO-Reactor/'
|
159
|
+
gem.rubyforge_project = RUBYFORGE_PROJECT
|
160
|
+
|
161
|
+
gem.has_rdoc = true
|
162
|
+
gem.rdoc_options = RDOC_OPTIONS
|
163
|
+
|
164
|
+
gem.files = RELEASE_FILES.
|
165
|
+
collect {|f| f.relative_path_from(BASEDIR).to_s }
|
166
|
+
gem.test_files = SPEC_FILES.
|
167
|
+
collect {|f| f.relative_path_from(BASEDIR).to_s }
|
168
|
+
|
169
|
+
DEPENDENCIES.each do |name, version|
|
170
|
+
version = '>= 0' if version.length.zero?
|
171
|
+
gem.add_dependency( name, version )
|
172
|
+
end
|
173
|
+
|
174
|
+
REQUIREMENTS.each do |name, version|
|
175
|
+
gem.requirements << [ name, version ].compact.join(' ')
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
$trace = Rake.application.options.trace ? true : false
|
180
|
+
$dryrun = Rake.application.options.dryrun ? true : false
|
181
|
+
|
182
|
+
|
183
|
+
# Load any remaining task libraries
|
184
|
+
RAKE_TASKLIBS.each do |tasklib|
|
185
|
+
next if tasklib =~ %r{/(helpers|svn|verifytask)\.rb$}
|
186
|
+
begin
|
187
|
+
require tasklib
|
188
|
+
rescue ScriptError => err
|
189
|
+
fail "Task library '%s' failed to load: %s: %s" %
|
190
|
+
[ tasklib, err.class.name, err.message ]
|
191
|
+
trace "Backtrace: \n " + err.backtrace.join( "\n " )
|
192
|
+
rescue => err
|
193
|
+
log "Task library '%s' failed to load: %s: %s. Some tasks may not be available." %
|
194
|
+
[ tasklib, err.class.name, err.message ]
|
195
|
+
trace "Backtrace: \n " + err.backtrace.join( "\n " )
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Load any project-specific rules defined in 'Rakefile.local' if it exists
|
200
|
+
import LOCAL_RAKEFILE if LOCAL_RAKEFILE.exist?
|
201
|
+
|
202
|
+
|
203
|
+
#####################################################################
|
204
|
+
### T A S K S
|
205
|
+
#####################################################################
|
206
|
+
|
207
|
+
### Default task
|
208
|
+
task :default => [:clean, :local, :spec, :rdoc, :package]
|
209
|
+
|
210
|
+
### Task the local Rakefile can append to -- no-op by default
|
211
|
+
task :local
|
212
|
+
|
213
|
+
|
214
|
+
### Task: clean
|
215
|
+
CLEAN.include 'coverage'
|
216
|
+
CLOBBER.include 'artifacts', 'coverage.info', PKGDIR
|
217
|
+
|
218
|
+
# Target to hinge on ChangeLog updates
|
219
|
+
file SVN_ENTRIES
|
220
|
+
|
221
|
+
### Task: changelog
|
222
|
+
file 'ChangeLog' => SVN_ENTRIES.to_s do |task|
|
223
|
+
log "Updating #{task.name}"
|
224
|
+
|
225
|
+
changelog = make_svn_changelog()
|
226
|
+
File.open( task.name, 'w' ) do |fh|
|
227
|
+
fh.print( changelog )
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
|
232
|
+
### Task: cruise (Cruisecontrol task)
|
233
|
+
desc "Cruisecontrol build"
|
234
|
+
task :cruise => [:clean, :spec, :package] do |task|
|
235
|
+
raise "Artifacts dir not set." if ARTIFACTS_DIR.to_s.empty?
|
236
|
+
artifact_dir = ARTIFACTS_DIR.cleanpath
|
237
|
+
artifact_dir.mkpath
|
238
|
+
|
239
|
+
coverage = BASEDIR + 'coverage'
|
240
|
+
if coverage.exist? && coverage.directory?
|
241
|
+
$stderr.puts "Copying coverage stats..."
|
242
|
+
FileUtils.cp_r( 'coverage', artifact_dir )
|
243
|
+
end
|
244
|
+
|
245
|
+
$stderr.puts "Copying packages..."
|
246
|
+
FileUtils.cp_r( FileList['pkg/*'].to_a, artifact_dir )
|
247
|
+
end
|
248
|
+
|
249
|
+
|
250
|
+
desc "Update the build system to the latest version"
|
251
|
+
task :update_build do
|
252
|
+
log "Updating the build system"
|
253
|
+
sh 'svn', 'up', RAKE_TASKDIR
|
254
|
+
log "Updating the Rakefile"
|
255
|
+
sh 'rake', '-f', RAKE_TASKDIR + 'Metarakefile'
|
256
|
+
end
|
257
|
+
|
data/examples/chatserver.rb
CHANGED
data/examples/simple.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'io/reactor'
|
4
|
+
|
5
|
+
def debug( msg )
|
6
|
+
return unless $DEBUG
|
7
|
+
$stderr.puts( " " + msg )
|
8
|
+
end
|
9
|
+
|
10
|
+
reactor = IO::Reactor.new
|
11
|
+
data_to_send = "some stuff to send"
|
12
|
+
|
13
|
+
reader, writer = IO.pipe
|
14
|
+
|
15
|
+
# Read from the reader end of the pipe until the writer finishes
|
16
|
+
reactor.register( reader, :read ) do |io,event|
|
17
|
+
if io.eof?
|
18
|
+
debug "done with reading, closing reader"
|
19
|
+
reactor.unregister( io )
|
20
|
+
io.close
|
21
|
+
else
|
22
|
+
debug "reading..."
|
23
|
+
puts io.read( 256 )
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Write to the writer end of the pipe until there's no data left
|
28
|
+
reactor.register( writer, :write ) do |io,event|
|
29
|
+
debug "writing..."
|
30
|
+
bytes = io.write( data_to_send )
|
31
|
+
data_to_send.slice!( 0, bytes )
|
32
|
+
|
33
|
+
if data_to_send.empty?
|
34
|
+
debug "done with writing; closing writer."
|
35
|
+
reactor.unregister( io )
|
36
|
+
io.close
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Now pump the reactor until both sides are done
|
41
|
+
puts "Starting IO"
|
42
|
+
until reactor.empty?
|
43
|
+
debug "polling..."
|
44
|
+
reactor.poll
|
45
|
+
end
|
46
|
+
puts "done, exiting."
|
47
|
+
|
48
|
+
|
49
|
+
# $ ruby -d -Ilib examples/simple.rb
|
50
|
+
# Starting IO
|
51
|
+
# polling...
|
52
|
+
# writing...
|
53
|
+
# done with writing; closing writer.
|
54
|
+
# polling...
|
55
|
+
# reading...
|
56
|
+
# some stuff to send
|
57
|
+
# polling...
|
58
|
+
# done with reading, closing reader
|
59
|
+
# done, exiting.
|
@@ -0,0 +1,44 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
require 'io/reactor'
|
5
|
+
|
6
|
+
listener = TCPServer.new( '127.0.0.1', 18181 )
|
7
|
+
reactor = IO::Reactor.new
|
8
|
+
|
9
|
+
Signal.trap( 'HUP' ) { reactor.clear }
|
10
|
+
Signal.trap( 'INT' ) { reactor.clear }
|
11
|
+
|
12
|
+
reactor.register( listener, :read ) do |sock, _|
|
13
|
+
client = sock.accept
|
14
|
+
$stderr.puts "Accepted client from %s:%d" % [ client.peeraddr[2], client.peeraddr[1] ]
|
15
|
+
# message = Time.now.to_s + "\r\n\r\n"
|
16
|
+
message = File.read(__FILE__) + "\r\n\r\n"
|
17
|
+
|
18
|
+
reactor.register( client, message, :write ) do |io, event, buf|
|
19
|
+
bytes = io.write( buf )
|
20
|
+
buf.slice!( 0, bytes )
|
21
|
+
|
22
|
+
if buf.empty?
|
23
|
+
reactor.unregister( io )
|
24
|
+
io.close
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
$stdout.puts "Starting up..."
|
31
|
+
until reactor.empty?
|
32
|
+
reactor.poll( 0.1 ) do |io, event|
|
33
|
+
case event
|
34
|
+
when :error
|
35
|
+
$stderr.puts " %p: in the error event handler" % [ io ]
|
36
|
+
reactor.unregister( io )
|
37
|
+
io.close
|
38
|
+
else
|
39
|
+
fail "Reactor got unexpected event %p on %p" % [ event, io ]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
$stdout.puts "Server shut down."
|
44
|
+
|
data/lib/io/reactor.rb
CHANGED
@@ -1,48 +1,45 @@
|
|
1
|
-
#!/usr/bin/ruby
|
2
|
-
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
class IO # :nodoc:
|
4
|
+
|
3
5
|
# An object-oriented multiplexing asynchronous IO mechanism for Ruby.
|
4
6
|
#
|
5
7
|
# == Synopsis
|
6
8
|
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
# end
|
36
|
-
# }
|
37
|
-
#
|
38
|
-
# # Drive the reactor until it's empty with a single thread
|
39
|
-
# Thread::new { reactorobj.poll until reactorobj.empty? }
|
9
|
+
# reactor = IO::Reactor.new
|
10
|
+
# data_to_send = "some stuff to send"
|
11
|
+
#
|
12
|
+
# reader, writer = IO.pipe
|
13
|
+
#
|
14
|
+
# # Read from the reader end of the pipe until the writer finishes
|
15
|
+
# reactor.register( reader, :read ) do |io,event|
|
16
|
+
# if io.eof?
|
17
|
+
# reactor.unregister( io )
|
18
|
+
# io.close
|
19
|
+
# else
|
20
|
+
# puts io.read( 256 )
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# # Write to the writer end of the pipe until there's no data left
|
25
|
+
# reactor.register( writer, :write ) do |io,event|
|
26
|
+
# bytes = io.write( data_to_send )
|
27
|
+
# data_to_send.slice!( 0, bytes )
|
28
|
+
#
|
29
|
+
# if data_to_send.empty?
|
30
|
+
# reactor.unregister( io )
|
31
|
+
# io.close
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# # Now pump the reactor until both sides are done
|
36
|
+
# reactor.poll until reactor.empty?
|
40
37
|
#
|
41
38
|
# == Author
|
42
39
|
#
|
43
40
|
# Michael Granger <ged@FaerieMUD.org>
|
44
41
|
#
|
45
|
-
# Copyright (c) 2002
|
42
|
+
# Copyright (c) 2002-2008 The FaerieMUD Consortium. All rights reserved.
|
46
43
|
#
|
47
44
|
# This module is free software. You may use, modify, and/or redistribute this
|
48
45
|
# software under the same terms as Ruby itself.
|
@@ -53,33 +50,40 @@
|
|
53
50
|
#
|
54
51
|
# == Version
|
55
52
|
#
|
56
|
-
# $Id: reactor.rb
|
53
|
+
# $Id: reactor.rb 86 2008-08-19 23:41:58Z deveiant $
|
57
54
|
#
|
55
|
+
class Reactor
|
58
56
|
|
59
|
-
|
60
|
-
require 'rbconfig'
|
57
|
+
### Class constants
|
61
58
|
|
62
|
-
|
59
|
+
# SVN Revision
|
60
|
+
SVNRev = %q$Rev: 86 $
|
63
61
|
|
64
|
-
|
65
|
-
|
62
|
+
# SVN Id
|
63
|
+
SVNId = %q$Id: reactor.rb 86 2008-08-19 23:41:58Z deveiant $
|
66
64
|
|
67
|
-
|
68
|
-
|
69
|
-
Rcsid = %q$Id: reactor.rb,v 1.13 2003/08/04 23:56:14 deveiant Exp $
|
65
|
+
# Package version
|
66
|
+
VERSION = '1.0.4'
|
70
67
|
|
71
|
-
|
68
|
+
# List of valid event types, in the order IO#select returns them
|
69
|
+
VALID_EVENTS = [:read, :write, :error]
|
70
|
+
VALID_EVENTS.freeze
|
71
|
+
|
72
|
+
|
73
|
+
#################################################################
|
74
|
+
### I N S T A N C E M E T H O D S
|
75
|
+
#################################################################
|
72
76
|
|
73
77
|
### Create and return a new IO reactor object.
|
74
78
|
def initialize
|
75
|
-
@handles = Hash
|
79
|
+
@handles = Hash.new {|hsh,key|
|
76
80
|
hsh[ key ] = {
|
77
81
|
:events => [],
|
78
82
|
:handler => nil,
|
79
83
|
:args => [],
|
80
84
|
}
|
81
85
|
}
|
82
|
-
@
|
86
|
+
@pending_events = Hash.new {|hsh,key| hsh[ key ] = []}
|
83
87
|
end
|
84
88
|
|
85
89
|
|
@@ -94,7 +98,8 @@ class Reactor
|
|
94
98
|
|
95
99
|
# The Hash of unhandled events which occurred in the last call to #poll,
|
96
100
|
# keyed by handle.
|
97
|
-
attr_reader :
|
101
|
+
attr_reader :pending_events
|
102
|
+
alias_method :pendingEvents, :pending_events
|
98
103
|
|
99
104
|
|
100
105
|
### Register the specified IO object with the reactor for events given as
|
@@ -114,15 +119,15 @@ class Reactor
|
|
114
119
|
### Registering a handle will unregister any previously registered
|
115
120
|
### event/handler+arguments pairs associated with the handle.
|
116
121
|
def register( io, *args, &handler )
|
117
|
-
events =
|
122
|
+
events = VALID_EVENTS & args
|
118
123
|
args -= events
|
119
124
|
|
120
125
|
self.unregister( io )
|
121
|
-
self.
|
126
|
+
self.enable_events( io, *events )
|
122
127
|
if handler
|
123
|
-
self.
|
128
|
+
self.set_handler( io, *args, &handler )
|
124
129
|
else
|
125
|
-
self.
|
130
|
+
self.set_args( io, *args )
|
126
131
|
end
|
127
132
|
|
128
133
|
return self
|
@@ -130,81 +135,95 @@ class Reactor
|
|
130
135
|
alias_method :add, :register
|
131
136
|
|
132
137
|
|
138
|
+
### Returns +true+ if the given +io+ handle is registered with the reactor.
|
139
|
+
def registered?( io )
|
140
|
+
return @handles.key?( io )
|
141
|
+
end
|
142
|
+
|
143
|
+
|
133
144
|
### Add the specified +events+ to the list that will be polled for on the
|
134
145
|
### given +io+ handle.
|
135
|
-
def
|
146
|
+
def enable_events( io, *events )
|
136
147
|
@handles[ io ][:events] |= events
|
137
148
|
end
|
149
|
+
alias_method :enableEvents, :enable_events
|
138
150
|
|
139
151
|
|
140
152
|
### Remove the specified +events+ from the list that will be polled for on
|
141
153
|
### the given +io+ handle.
|
142
|
-
def
|
154
|
+
def disable_events( io, *events )
|
143
155
|
raise RuntimeError, "Cannot disable the :error event" if
|
144
156
|
events.include?( :error )
|
145
157
|
@handles[ io ][:events] -= events
|
146
158
|
end
|
159
|
+
alias_method :disableEvents, :disable_events
|
147
160
|
|
148
161
|
|
162
|
+
### Returns +true+ if the specified +event+ is enabled for the given +io+.
|
163
|
+
def event_enabled?( io, event )
|
164
|
+
return false unless @handles.key?( io )
|
165
|
+
return true if event == :error # Error is always enabled for all handles
|
166
|
+
return @handles[ io ][ :events ].include?( event )
|
167
|
+
end
|
168
|
+
alias_method :has_event_enabled?, :event_enabled?
|
169
|
+
|
170
|
+
|
149
171
|
### Set the handler for events on the given +io+ handle to the specified
|
150
172
|
### +handler+. If any +args+ are present, they will be passed as an exploded
|
151
173
|
### array to the handler for each event. Returns the previously-registered
|
152
174
|
### handler, if any.
|
153
|
-
def
|
175
|
+
def set_handler( io, *args, &handler )
|
154
176
|
rval = @handles[ io ][:handler]
|
155
177
|
@handles[ io ][:handler] = handler
|
156
|
-
self.
|
178
|
+
self.set_args( io, *args )
|
157
179
|
return rval
|
158
180
|
end
|
181
|
+
alias_method :setHandler, :set_handler
|
159
182
|
|
160
183
|
|
161
184
|
### Remove and return the handler for events on the given +io+ handle.
|
162
|
-
def
|
185
|
+
def remove_handler( io )
|
163
186
|
rval = @handles[ io ][:handler]
|
164
187
|
@handles[ io ][:handler] = nil
|
165
|
-
self.
|
188
|
+
self.remove_args( io )
|
166
189
|
return rval
|
167
190
|
end
|
191
|
+
alias_method :removeHandler, :remove_handler
|
168
192
|
|
169
193
|
|
170
194
|
### Set the additional arguments to pass to the handler for the given +io+
|
171
195
|
### handle on each event to the given +args+.
|
172
|
-
def
|
196
|
+
def set_args( io, *args )
|
173
197
|
rval = @handles[ io ][:args]
|
174
198
|
@handles[ io ][:args] = args
|
175
199
|
return rval
|
176
200
|
end
|
201
|
+
alias_method :setArgs, :set_args
|
177
202
|
|
178
203
|
|
179
204
|
### Remove the arguments for the given handle to the given +args+.
|
180
|
-
def
|
205
|
+
def remove_args( io )
|
181
206
|
return @handles[ io ][:args].clear
|
182
207
|
end
|
208
|
+
alias_method :removeArgs, :remove_args
|
183
209
|
|
184
210
|
|
185
211
|
### Remove the specified <tt>io</tt> from the receiver's list of registered
|
186
212
|
### handles, if present. Returns the handle if it was registered, or
|
187
213
|
### <tt>nil</tt> if it was not.
|
188
214
|
def unregister( io )
|
189
|
-
@
|
215
|
+
@pending_events.delete( io )
|
190
216
|
@handles.delete( io )
|
191
217
|
end
|
192
218
|
alias_method :remove, :unregister
|
193
219
|
|
194
220
|
|
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
221
|
### Clear all registered handles from the poll object. Returns the handles
|
203
222
|
### that were cleared.
|
204
223
|
def clear
|
205
224
|
rv = @handles.keys
|
206
225
|
|
207
|
-
@
|
226
|
+
@pending_events.clear
|
208
227
|
@handles.clear
|
209
228
|
|
210
229
|
return rv
|
@@ -228,7 +247,7 @@ class Reactor
|
|
228
247
|
### those handles with events. If a block is given, it will be invoked once
|
229
248
|
### for each handle which doesn't have an explicit handler. If no block is
|
230
249
|
### given, events without explicit handlers are inserted into the reactor's
|
231
|
-
### <tt>
|
250
|
+
### <tt>pending_events</tt> attribute.
|
232
251
|
###
|
233
252
|
### The <tt>timeout</tt> argument is the number of floating-point seconds to
|
234
253
|
### wait for an event before returning (ie., fourth argument to the
|
@@ -239,19 +258,22 @@ class Reactor
|
|
239
258
|
### occurred.
|
240
259
|
def poll( timeout=-1 ) # :yields: io, eventMask
|
241
260
|
timeout = timeout.to_f
|
242
|
-
@
|
261
|
+
@pending_events.clear
|
243
262
|
count = 0
|
244
263
|
|
245
264
|
unless @handles.empty?
|
246
265
|
timeout = nil if timeout < 0
|
247
|
-
|
266
|
+
evented_handles = self.get_pending_events( timeout )
|
248
267
|
|
249
268
|
# For each event of each io that had an event happen, call any
|
250
269
|
# associated callback, or any provided block, or failing both of
|
251
270
|
# those, add the event to the hash of unhandled pending events.
|
252
|
-
|
271
|
+
evented_handles.each do |io,events|
|
253
272
|
count += 1
|
254
|
-
events.each
|
273
|
+
events.each do |ev|
|
274
|
+
# Don't continue if the io was unregistered by an earlier handler
|
275
|
+
break unless @handles.key?( io )
|
276
|
+
|
255
277
|
args = @handles[ io ][:args]
|
256
278
|
|
257
279
|
if @handles[ io ][:handler]
|
@@ -259,10 +281,10 @@ class Reactor
|
|
259
281
|
elsif block_given?
|
260
282
|
yield( io, ev, *args )
|
261
283
|
else
|
262
|
-
@
|
284
|
+
@pending_events[io].push( ev )
|
263
285
|
end
|
264
|
-
|
265
|
-
|
286
|
+
end
|
287
|
+
end
|
266
288
|
end
|
267
289
|
|
268
290
|
return count
|
@@ -281,35 +303,40 @@ class Reactor
|
|
281
303
|
|
282
304
|
### Select on the registered handles, returning a Hash of handles => events
|
283
305
|
### for handles which had events occur.
|
284
|
-
def
|
285
|
-
|
306
|
+
def get_pending_events( timeout )
|
307
|
+
@handles.delete_if {|io,_| io.closed? }
|
308
|
+
|
309
|
+
eventHandles = select( self.get_read_handles, self.get_write_handles,
|
286
310
|
@handles.keys, timeout ) or return {}
|
287
|
-
eventHash = Hash
|
311
|
+
eventHash = Hash.new {|hsh,io| hsh[io] = []}
|
288
312
|
|
289
313
|
# Fill in the hash with pending events of each type
|
290
|
-
|
314
|
+
VALID_EVENTS.each_with_index do |event,i|
|
291
315
|
eventHandles[i].each {|io| eventHash[io].push( event )}
|
292
|
-
|
316
|
+
end
|
293
317
|
return eventHash
|
294
318
|
end
|
319
|
+
alias_method :getPendingEvents, :get_pending_events
|
295
320
|
|
296
321
|
|
297
322
|
### Return an Array of handles which have handlers for the <tt>:read</tt>
|
298
323
|
### event.
|
299
|
-
def
|
324
|
+
def get_read_handles
|
300
325
|
@handles.
|
301
326
|
find_all {|io,hsh| hsh[:events].include?( :read )}.
|
302
|
-
collect {|io,
|
327
|
+
collect {|io,_| io }
|
303
328
|
end
|
329
|
+
alias_method :getReadHandles, :get_read_handles
|
304
330
|
|
305
331
|
|
306
332
|
### Return an Array of handles which have handlers for the <tt>:write</tt>
|
307
333
|
### event.
|
308
|
-
def
|
334
|
+
def get_write_handles
|
309
335
|
@handles.
|
310
336
|
find_all {|io,hsh| hsh[:events].include?( :write )}.
|
311
|
-
collect {|io,
|
337
|
+
collect {|io,_| io }
|
312
338
|
end
|
339
|
+
alias_method :getWriteHandles, :get_write_handles
|
313
340
|
|
314
341
|
|
315
342
|
end # class Reactor
|