io-reactor 0.05 → 1.0.4
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.
- 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
|